Skip to content

hongfaqiu/geo-timeline

Repository files navigation

geo-timeline

A D3.js(v7) based geologic timeline&timeScale&timeSlider

gzip size npm latest version license

timeLine: geo-timeline

  • Zoom: Use the mouse wheel to control zoom, double-click to zoom in.
  • Adjustment: Hold down the left button to drag the timeline.
  • Location: Left click the timeline to move the handler quickly, or drag to move the handler.

timeScale: geo-timescale-simple

  • Location: Left click the geo stage to quick location.
  • Zoom: Use the mouse wheel to control zoom in or zoom out.

timeSlider: geo-timeslider

  • Drag: Drag the slider to change the time.
  • Click: Click on the square to change the time range.

Install

For node.js

#npm
npm install --save @zjugis/geo-timeline
#yarn
yarn add @zjugis/geo-timeline

For legacy environments, you can load @zjugis/geo-timeline’s UMD bundle from an npm-based CDN such as jsDelivr; a timeLine global is exported:

<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
<script src="//unpkg.com/@zjugis/geo-timeline@latest"></script>

<script>
  fetch('./test/GTS_2020.json').then(async res => {
    const intervals = await res.json()

    new timeLine.GeoTimeLine("#geoTimeLineContainer", intervals)
    new timeLine.GeoTimeScale("#geoTimeScaleContainer", intervals)
    new timeLine.GeoTimeSlider("#geoTimeSliderContainer", intervals)
  })
</script>

Usage

For node.js

import { GeoTimeLine, GeoTimeScale } from "@zjugis/geo-timeline";

fetch('./test/GTS_2020.json').then(async res => {
  const intervals = await res.json()

  // create geotimeline obj
  const geoTimeLine = new GeoTimeLine("#geoTimeContainer", intervals, {
    onChange: function(time, level) {
      // do something
    },
    // determin interval by time
    intervalSum: d => d.leaf ? d.start - d.end : 0
  });

  // It is recommended to set the time after changing the level
  geoTimeLine.level = 2
  geoTimeLine.time = 2000

  // create geotimescale obj
  const geoTimeScale = new GeoTimeScale("#geoTimeScale-simple", intervals, {
    onChange: val => console.log(val),
    // only show 2 levels once
    simplify: true,
    height: 70,
  })
  // set stage
  geoTimeScale.stage = 'Cambrian'

  // create geotimeslider obj
  const geoTimeSlider = new GeoTimeSlider("#geoTimeSlider", intervals, {
    // do something when time range is changed
    onChange: val => console.log(val),
  })

  // set time range 
  geoTimeSlider.timeRange = [300, 100]
})

API

GeoTimeLine

class GeoTimeLine {
    /** text font */
    readonly font: string;
    /** interval data's max level */
    readonly maxLevel: number;
    /** svg object */
    readonly svg: Selection<SVGSVGElement, unknown, HTMLElement, any>;
    /** interval data */
    readonly intervals: IntervalItem[];
    /** hierarchical data generated by intervals */
    readonly hierarchicalData: HierarchyNode<IntervalItem>;
    /** the root hierarchical data */
    readonly root: NodeItem;
    /** user input options */
    readonly options: GeoTimeLineOptions
    
    /** 
     * Create a GeoTimeLine
     * @param selector CSS selector string
     * @param intervals geo time intervals array
     * @param options GeoTimeLine options
    */
    constructor(selector: string | BaseType, intervals: IntervalItem[], options?: GeoTimeLineOptions);

    /** get or set time */
    get time(): number;
    set time(val: number);
    /** get or set level */
    get level(): number;
    set level(val: number);
    get ready(): boolean;
}

/**
 * BaseType serves as an alias for the 'minimal' data type which can be selected
 * without 'd3-selection' trying to use properties internally which would otherwise not
 * be supported.
 */
type BaseType = Element | EnterElement | Document | Window | null;

interface GeoTimeLineOptions {
    /** geo time intervals array */
    intervals: IntervalItem[];
    /** svg width, defaults to container's width */
    width?: number;
    /** svg height, defaults to 70 */
    height?: number;
    /** font size, defaults to 16px */
    fontSize?: number;
    /** font family, defaults to 'sans-serif' */
    fontFamily?: string;
    /** callback when handle's position or scale level changed */
    onChange?: (time: number, level: number) => void;
    /** dispatch when mouseup or zoom  */
    onAfterChange?: (time: number, level: number) => void;
    /** defaults to {
      top: 0, right: 0, bottom: 0, left: 0,
    } */
    margin?: {
        top?: number;
        bottom?: number;
        left?: number;
        right?: number;
    }
    /** defaults to {
      top: 0, right: 0, bottom: 0, left: 0,
    } */
    padding?: {
        top?: number;
        bottom?: number;
        left?: number;
        right?: number;
    }
    /** initial time, defaults to 0 */
    time?: number;
    /** animation time, defaults to 450ms */
    transition?: number;
    /** interval transform setting, defaults to (d) => d.leaf ? 1 : 0 */
    intervalSum?: (d: IntervalItem) => number;
    /** min zoom level */
    minZoom?: number;
    /** max zoom level, defaults to 10 */
    maxZoom?: number;
}

/** geo time data intervals schema **/
type IntervalItem = {
    id: number;
    name: string;
    abbr?: string;
    color: string;
    textColor?: string;
    end: number;
    start: number;
    level?: number;
    parentId?: number;
    leaf?: boolean;
}

GeoTimeScale

class GeoTimeLine {
    /** text font */
    readonly font: string;
    /** svg object */
    readonly svg: Selection<SVGSVGElement, unknown, HTMLElement, any>;
    /** interval data */
    readonly intervals: IntervalItem[];
    /** hierarchical data generated by intervals */
    readonly hierarchicalData: HierarchyNode<IntervalItem>;
    /** the root hierarchical data */
    readonly root: NodeItem;
    /** user input options */
    readonly options: GeoTimeScaleOptions;
    
    /**
     * Create a GeoTimeScale
     * @param selector CSS selector string
     * @param intervals geo time intervals array
     * @param options GeoTimeScale options
     */
    constructor(selector: string | BaseType, intervals: IntervalItem[], options?: GeoTimeScaleOptions);
    /** get or set animation transition time */
    transition: number;
    /** get or set focused stage
     * @example
     * geoTimeScale.stage = 'Cambrian'
     */
    get stage(): string;
    set stage(val: string);
    get sequence(): NodeItem[];
    get ready(): boolean;
}

interface GeoTimeScaleOptions {
    /** geo time intervals array */
    intervals: IntervalItem[];
    /** svg width, defaults to container's width */
    width?: number;
    /** svg height, defaults to 400px */
    height?: number;
    /** font size, defaults to 12px */
    fontSize?: number;
    /** font family, defaults to 'sans-serif' */
    fontFamily?: string;
    /** callback when handle's position or scale level changed */
    onChange?: (node: NodeItem) => void;
    /** defaults to {
      top: 0, right: 0, bottom: 0, left: 0,
    } */
    margin?: MarginOpts;
    /** defaults to {
      top: 0, right: 0, bottom: 0, left: 0,
    } */
    padding?: MarginOpts;
    /** animation time, defaults to 450ms */
    transition?: number;
    /** interval transform setting, defaults to d => d.leaf ? d.start - d.end : 0 */
    intervalSum?: (d: IntervalItem) => number;
    /** simplify show 2 levels or not, defaults to false */
    simplify?: boolean;
    /** focused node's neighbor node width, defaults to 100px */
    neighborWidth?: number;
    /** tick length, defaults to 15px */
    tickLength?: number;
}

GeoTimeSlider

class GeoTimeSlider {
    /** text font */
    readonly font: string;
    /** svg object */
    readonly svg: any;
    /** interval data */
    readonly intervals: IntervalItem[];
    /** hierarchical data generated by intervals */
    readonly hierarchicalData: HierarchyNode<IntervalItem>;
    /** the root hierarchical data */
    readonly root: SliderNodeItem;
    /** user input options */
    readonly options: Required<GeoTimeSliderOptions>;
    /** get or set animation transition time */
    transition: number;
    readonly startEnd: number[];
    /**
     * Create a GeoTimeSlider
     * @param {string | BaseType} selector CSS selector string
     * @param {IntervalItem[]} intervals geo time intervals array
     * @param {number} [options.width] svg width, defaults to container's width
     * @param {number} [options.height = 400] svg height, defaults to 400px
     * @param {number} [options.fontSize = 12] font size, defaults to 12px
     * @param {string} [options.fontFamily = 'sans-serif'] font family, defaults to 'sans-serif'
     * @param {Function} [options.onChange] callback when focused node change
     * @param {Object} [options.margin] svg margin, defaults to { top: 0, right: 0, bottom: 0, left: 0 }
     * @param {Object} [options.padding] svg padding, defaults to { top: 0, right: 0, bottom: 0, left: 0 }
     * @param {number} [options.transition = 300] animation time, defaults to 300ms
     * @param {Function} [options.intervalSum] interval transform setting, defaults to d => d.leaf ? d.start - d.end : 0
     * @param {boolean} [options.simplify = true] simplify show 2 levels or not
     * @param {number} [options.neighborWidth = 100] focused node's neighbor node width, defaults to 100px
     * @param {number} [options.tickLength = 15] tick length, defaults to 15px
     * @param {number} [options.tickHeight = 30] tick height, defaults to 30px
     * @param {string} [options.unit = ''] tick value unit
     */
    constructor(selector: string | BaseType, intervals: IntervalItem[], options?: GeoTimeSliderOptions);
    /** get or set focused stage
     * @example
     * geoTimeScale.stage = 'Cambrian'
     */
    get stage(): string | undefined;
    set stage(val: string);
    get sliderPosition(): number[];
    get timeRange(): number[];
    set timeRange(range: number[]);
    get ready(): boolean;
}

interface GeoTimeSliderOptions {
    /** svg width, defaults to container's width */
    width?: number;
    /** svg height, defaults to 400px */
    height?: number;
    /** font size, defaults to 12px */
    fontSize?: number;
    /** font family, defaults to 'sans-serif' */
    fontFamily?: string;
    /** callback when handle's position or scale level changed */
    onChange?: (timeRange: number[]) => void;
    /** defaults to {
      top: 0, right: 0, bottom: 0, left: 0,
    } */
    margin?: MarginOpts;
    /** defaults to {
      top: 0, right: 0, bottom: 0, left: 0,
    } */
    padding?: MarginOpts;
    /** animation time, defaults to 300ms */
    transition?: number;
    /** interval transform setting, defaults to d => d.leaf ? d.start - d.end : 0 */
    intervalSum?: (d: IntervalItem) => number;
    /** focused node's neighbor node width, defaults to 100px */
    neighborWidth?: number;
    /** tick length, defaults to 15px */
    tickLength?: number;
    /** tick height, defaults to 30px */
    tickHeight?: number;
    /** tick value unit */
    unit?: string;
}

Custom data

The interval item's schema like follow:

{
  "id": 753,
  "name": "Archean",
  "level": 1,
  "parentId": 0,
  "color": "#F0047F",
  "end": 2500,
  "start": 4000
}

Demo

Online demo

geoTimeLine geo-timeLine

geoTimeScale geo-timescale-simple

geoTimeSlider geo-timeslider

Develop

Run the command:

pnpm dev

And then Launch index.html with node server, if use VS Code, suggest the Live Server extension.

Run test:

pnpm test

Credit

https://github.com/UW-Macrostrat/geo-timescale