@geoblocks/elevation-profile 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,149 @@
1
+ import {LitElement, svg} from 'lit';
2
+ import {customElement, state, property} from 'lit/decorators.js';
3
+ import {ResizeController} from '@lit-labs/observers/resize-controller.js';
4
+ import {createRef, ref} from 'lit/directives/ref.js';
5
+
6
+ import {extent, bisector} from 'd3-array';
7
+ import {scaleLinear} from 'd3-scale';
8
+ import {line, area} from 'd3-shape';
9
+ import {axisBottom, axisLeft} from 'd3-axis';
10
+ import {select, pointer} from 'd3-selection';
11
+
12
+
13
+ @customElement('elevation-profile')
14
+ export class ElevationProfile extends LitElement {
15
+ @property({type: Array}) lines = [];
16
+ @property({type: Object}) margin = {top: 20, right: 20, bottom: 20, left: 40};
17
+ @property({type: Object}) tickSize = {x: 100, y: 40};
18
+
19
+ @state() pointer = {x: 0, y: 0};
20
+ private resizeController = new ResizeController(this, {});
21
+
22
+ private plotData;
23
+ private scaleX = scaleLinear();
24
+ private scaleY = scaleLinear();
25
+
26
+ private bisectDistance = bisector((data) => data[0]).left;
27
+
28
+ private line = line()
29
+ .x((point) => this.scaleX(point[0]))
30
+ .y((point) => this.scaleY(point[1]));
31
+ private area = area()
32
+ .x((point) => this.scaleX(point[0]))
33
+ .y1((point) => this.scaleY(point[1]));
34
+ private xAxis = axisBottom(this.scaleX)
35
+ .tickFormat((i) => i + ' m');
36
+ private yAxis = axisLeft(this.scaleY)
37
+ .tickFormat((i) => i + ' m');
38
+ private xGrid = axisBottom(this.scaleX).tickFormat(() => '');
39
+ private yGrid = axisLeft(this.scaleY).tickFormat(() => '');
40
+
41
+ private xRef = createRef();
42
+ private yRef = createRef();
43
+ private yGridRef = createRef();
44
+ private xGridRef = createRef();
45
+
46
+ willUpdate(changedProperties) {
47
+ if (changedProperties.has('lines')) {
48
+ this.plotData = this.lines.map((coordinate) => [coordinate[3], coordinate[2]]);
49
+
50
+ this.scaleX.domain(extent(this.plotData, (data) => data[0]));
51
+ this.scaleY.domain(extent(this.plotData, (data) => data[1])).nice();
52
+ }
53
+ }
54
+
55
+ render() {
56
+ const width = this.offsetWidth;
57
+ const height = this.offsetHeight;
58
+
59
+ this.scaleX.range([this.margin.left, width - this.margin.right]);
60
+ this.scaleY.range([height - this.margin.bottom, this.margin.top]);
61
+
62
+ this.area.y0(height - this.margin.bottom);
63
+
64
+ this.yGrid.tickSize(-width + this.margin.left + this.margin.right);
65
+ this.xGrid.tickSize(height - this.margin.top - this.margin.bottom);
66
+
67
+ const xTicks = width / this.tickSize.x;
68
+ const yTicks = height / this.tickSize.y;
69
+ this.xAxis.ticks(xTicks);
70
+ this.xGrid.ticks(xTicks);
71
+ this.yAxis.ticks(yTicks);
72
+ this.yGrid.ticks(yTicks);
73
+
74
+ select(this.xRef.value).call(this.xAxis);
75
+ select(this.yRef.value).call(this.yAxis);
76
+ select(this.xGridRef.value).call(this.xGrid);
77
+ select(this.yGridRef.value).call(this.yGrid);
78
+
79
+ return svg`
80
+ <svg width="${width}" height="${height}">
81
+ <g class="grid y" ${ref(this.yGridRef)} transform="translate(${this.margin.left}, 0)" />
82
+ <g class="grid x" ${ref(this.xGridRef)} transform="translate(0, ${this.margin.bottom})" />
83
+ <g class="axis x" ${ref(this.xRef)} transform="translate(0, ${height - this.margin.bottom})" />
84
+ <g class="axis y" ${ref(this.yRef)} transform="translate(${this.margin.left}, 0)" />
85
+ <path class="area" d="${this.area(this.plotData)}" />
86
+ <path class="elevation" d="${this.line(this.plotData)}" fill="none" />
87
+ <g style="visibility: ${this.pointer.x > 0 ? 'visible' : 'hidden'}">
88
+ <path class="elevation highlight" d="${this.line(this.plotData)}" fill="none"
89
+ clip-path="polygon(0 0, ${this.pointer.x - 40} 0, ${this.pointer.x - 40} 100%, 0 100%)"
90
+ />
91
+ <line
92
+ class="pointer-line y"
93
+ x1="${this.pointer.x}"
94
+ y1="${this.margin.top}"
95
+ x2="${this.pointer.x}"
96
+ y2="${height - this.margin.bottom}"
97
+ />
98
+ <circle class="pointer-circle" cx="${this.pointer.x}" cy="${this.pointer.y}" />
99
+ </g>
100
+ <rect
101
+ width="${width}"
102
+ height="${height}"
103
+ fill="none"
104
+ pointer-events="all"
105
+ @pointermove="${this.pointerMove}"
106
+ @pointerout="${this.pointerOut}"
107
+ />
108
+ </svg>
109
+ `;
110
+ }
111
+
112
+ private pointerMove(event: PointerEvent) {
113
+ const pointerDistance = this.scaleX.invert(pointer(event)[0]);
114
+ const index = Math.min(this.bisectDistance(this.plotData, pointerDistance), this.plotData.length - 1);
115
+ // FIXME:
116
+ // var d0 = data[i - 1]
117
+ // var d1 = data[i];
118
+ // // work out which date value is closest to the mouse
119
+ // var d = mouseDate - d0[0] > d1[0] - mouseDate ? d1 : d0;
120
+
121
+ const data = this.plotData[index];
122
+
123
+ this.pointer = {
124
+ x: this.scaleX(data[0]),
125
+ y: this.scaleY(data[1]),
126
+ };
127
+
128
+ this.dispatchEvent(
129
+ new CustomEvent('over', {
130
+ detail: {
131
+ coordinate: this.lines[index],
132
+ position: this.pointer
133
+ }
134
+ }),
135
+ );
136
+ }
137
+
138
+ private pointerOut() {
139
+ this.pointer = {
140
+ x: 0,
141
+ y: 0,
142
+ };
143
+ this.dispatchEvent(new CustomEvent('out'));
144
+ }
145
+
146
+ createRenderRoot() {
147
+ return this;
148
+ }
149
+ }
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@geoblocks/elevation-profile",
3
+ "version": "0.0.1",
4
+ "license": "BSD-3-Clause",
5
+ "repository": "github:geoblocks/elevation-profile",
6
+ "source": "elevation-profile.ts",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "files": [
11
+ "elevation-profile.ts",
12
+ "dist/"
13
+ ],
14
+ "scripts": {
15
+ "start": "parcel index.html",
16
+ "build": "rm -rf dist && parcel build",
17
+ "build-demo": "parcel build index.html",
18
+ "publish-demo": "rm -rf dist && npm run build-demo && gh-pages -d dist"
19
+ },
20
+ "dependencies": {
21
+ "@lit-labs/observers": "2.0.2",
22
+ "d3-array": "3.2.4",
23
+ "d3-axis": "3.0.0",
24
+ "d3-scale": "4.0.2",
25
+ "d3-selection": "3.0.0",
26
+ "d3-shape": "3.2.0",
27
+ "lit": "3.0.2"
28
+ },
29
+ "devDependencies": {
30
+ "@types/d3": "7.4.3",
31
+ "gh-pages": "6.0.0",
32
+ "parcel": "2.10.2"
33
+ }
34
+ }