@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.
- package/LICENSE +29 -0
- package/dist/elevation-profile.js +125 -0
- package/dist/elevation-profile.js.map +1 -0
- package/elevation-profile.ts +149 -0
- package/package.json +34 -0
|
@@ -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
|
+
}
|