@geoblocks/elevation-profile 0.0.12 → 0.0.14
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/README.md
CHANGED
|
@@ -21,12 +21,15 @@ npm i --save @geoblocks/elevation-profile
|
|
|
21
21
|
| Name | Type | Default | Description
|
|
22
22
|
| --------------- | ---------------- | -------------------------------------------- | -----------
|
|
23
23
|
| `lines` | `number[][][]` | | MultiLineString coordinates
|
|
24
|
+
| `points` | `number[][]` | | Points to be displayed on the profile
|
|
24
25
|
| `margin` | `Object` | `{top: 20, right: 20, bottom: 20, left: 40}` | Margin in pixels around the elevation profile
|
|
25
26
|
| `pointerEvents` | `Boolean` | `true` | Whether to emit pointer events
|
|
26
27
|
| `tickSize` | `Object` | `{x: 100, y: 40}` | Size of the ticks in pixels
|
|
27
28
|
| `locale` | `string` | `navigator.language` | Locale for the axis labels
|
|
28
29
|
| `tolerance` | `number` | `1` | Tolerance for the line simplification. Set to `0` to disable simplification
|
|
29
30
|
|
|
31
|
+
### Ticks formating
|
|
32
|
+
|
|
30
33
|
The value of the tick in the axis can be changed by overriding the `tickFormat` method.
|
|
31
34
|
```javascript
|
|
32
35
|
profile.tickFormat = (value, axis) => {
|
|
@@ -35,6 +38,16 @@ profile.tickFormat = (value, axis) => {
|
|
|
35
38
|
```
|
|
36
39
|
Where `value` is the value of the tick and `axis` is the axis where the tick is located (`x` or `y`).
|
|
37
40
|
|
|
41
|
+
### Adding points
|
|
42
|
+
|
|
43
|
+
Points can be added to the profile by setting the `points` property. By default, the points will be displayed as circles on the profile.
|
|
44
|
+
This can be changed by overriding the `pointSvg` method.
|
|
45
|
+
```javascript
|
|
46
|
+
profile.pointSvg = (x, y, index) => {
|
|
47
|
+
return `<circle cx="${x}" cy="${y}" r="5" fill="red" />`;
|
|
48
|
+
};
|
|
49
|
+
```
|
|
50
|
+
Where `x` and `y` are the position in pixels and `index` is the index of the point in the `points` array.
|
|
38
51
|
|
|
39
52
|
### Events
|
|
40
53
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LitElement, PropertyValues } from "lit";
|
|
1
|
+
import { LitElement, PropertyValues, TemplateResult } from "lit";
|
|
2
2
|
export type OverDetails = {
|
|
3
3
|
coordinate: number[];
|
|
4
4
|
position: {
|
|
@@ -10,6 +10,7 @@ export default class ElevationProfile extends LitElement {
|
|
|
10
10
|
tolerance: number;
|
|
11
11
|
locale: string;
|
|
12
12
|
lines: number[][][];
|
|
13
|
+
points: number[][];
|
|
13
14
|
updateScale: (x: scaleLinear, y: scaleLinear, width: number, height: number) => void;
|
|
14
15
|
margin: {
|
|
15
16
|
top: number;
|
|
@@ -28,8 +29,9 @@ export default class ElevationProfile extends LitElement {
|
|
|
28
29
|
};
|
|
29
30
|
updated(changedProperties: PropertyValues): void;
|
|
30
31
|
willUpdate(changedProperties: PropertyValues): void;
|
|
31
|
-
render():
|
|
32
|
+
render(): TemplateResult<2>;
|
|
32
33
|
tickFormat(value: number, axis: 'x' | 'y'): string;
|
|
34
|
+
pointSvg(x: number, y: number, index: number): TemplateResult;
|
|
33
35
|
firstUpdated(): void;
|
|
34
36
|
createRenderRoot(): this;
|
|
35
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":";
|
|
1
|
+
{"mappings":";AAoBA,0BAA0B;IACxB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC;CAClC,CAAC;AAEF,qCACsC,SAAQ,UAAU;IAC5B,SAAS,SAAK;IACd,MAAM,SAAsB;IAC7B,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAM;IACzB,MAAM,EAAE,MAAM,EAAE,EAAE,CAAM;IACrC,WAAW,0CAA2C,MAAM,UAAU,MAAM,KAAG,IAAI,CAAO;IAC5E,MAAM;;;;;MAA8C;IACpD,QAAQ;;;MAAmB;IAC1B,aAAa,UAAQ;IAEvC,OAAO;;;MAAgB;IA4BvB,OAAO,CAAC,iBAAiB,EAAE,cAAc;IAczC,UAAU,CAAC,iBAAiB,EAAE,cAAc;IAsB5C,MAAM;IAiER,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,GAAG;IAQzC,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc;IAI3D,YAAY;IA+CZ,gBAAgB;CAG1B;AAGD,QAAQ,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,mBAAmB,EAAE,gBAAgB,CAAC;KACvC;CACF","sources":["elevation-profile.ts"],"sourcesContent":["import {LitElement, svg} from 'lit';\nimport {customElement, state, property} from 'lit/decorators.js';\nimport {ResizeController} from '@lit-labs/observers/resize-controller.js';\nimport {guard} from 'lit/directives/guard.js';\nimport type {PropertyValues, TemplateResult} from 'lit';\n\nimport {extent, bisector} from 'd3-array';\nimport {scaleLinear} from 'd3-scale';\nimport {line, area} from 'd3-shape';\nimport {axisBottom, axisLeft} from 'd3-axis';\nimport {select, pointer} from 'd3-selection';\n\nimport simplify from 'simplify-js';\n\ntype PlotPoint = {\n x: number;\n y: number;\n coordinate: number[];\n};\n\nexport type OverDetails = {\n coordinate: number[];\n position: {x: number; y: number};\n};\n\n@customElement('elevation-profile')\nexport default class ElevationProfile extends LitElement {\n @property({type: Number}) tolerance = 1;\n @property({type: String}) locale = navigator.language;\n @property({type: Array}) lines: number[][][] = [];\n @property({type: Array}) points: number[][] = [];\n @property() updateScale = (x: scaleLinear, y: scaleLinear, width: number, height: number): void => {};\n @property({type: Object}) margin = {top: 20, right: 20, bottom: 20, left: 40};\n @property({type: Object}) tickSize = {x: 100, y: 40};\n @property({type: Boolean}) pointerEvents = true;\n\n @state() pointer = {x: 0, y: 0};\n private resizeController = new ResizeController(this, {\n callback: () => [this.offsetWidth, this.offsetHeight],\n });\n\n private plotData: PlotPoint[] = [];\n private pointsData: PlotPoint[] = [];\n private scaleX = scaleLinear();\n private scaleY = scaleLinear();\n\n private bisectDistance = bisector((point: PlotPoint) => point.x);\n\n private line = line()\n .defined((point: PlotPoint) => !isNaN(point.y))\n .x((point: PlotPoint) => this.scaleX(point.x))\n .y((point: PlotPoint) => this.scaleY(point.y));\n private area = area()\n .defined((point: PlotPoint) => !isNaN(point.y))\n .x((point: PlotPoint) => this.scaleX(point.x))\n .y1((point: PlotPoint) => this.scaleY(point.y));\n private xAxis = axisBottom(this.scaleX).tickFormat((value: number) => this.tickFormat(value, 'x'));\n private yAxis = axisLeft(this.scaleY).tickFormat((value: number) => this.tickFormat(value, 'y'));\n private xGrid = axisBottom(this.scaleX).tickFormat(() => '');\n private yGrid = axisLeft(this.scaleY).tickFormat(() => '');\n\n private meterFormat: Intl.NumberFormat | null = null;\n private kilometerFormat: Intl.NumberFormat | null = null;\n\n override updated(changedProperties: PropertyValues) {\n if (changedProperties.has('locale')) {\n this.meterFormat = new Intl.NumberFormat(this.locale, {\n style: 'unit',\n unit: 'meter',\n });\n\n this.kilometerFormat = new Intl.NumberFormat(this.locale, {\n style: 'unit',\n unit: 'kilometer',\n });\n }\n }\n\n override willUpdate(changedProperties: PropertyValues) {\n if (changedProperties.has('lines')) {\n this.plotData.length = 0;\n for (const line of this.lines) {\n const data = line.map((coordinate) => ({x: coordinate[3], y: coordinate[2], coordinate}));\n this.plotData.push(...simplify(data, this.tolerance));\n this.plotData.push({x: line[line.length - 1][3], y: NaN, coordinate: []});\n }\n\n this.scaleX.domain(extent(this.plotData, (data: PlotPoint) => data.x));\n this.scaleY.domain(extent(this.plotData, (data: PlotPoint) => data.y)).nice();\n\n this.updateScale(this.scaleX, this.scaleY, this.offsetWidth, this.offsetHeight);\n }\n if (changedProperties.has('points')) {\n this.pointsData.length = 0;\n for (const point of this.points) {\n this.pointsData.push({x: point[3], y: point[2], coordinate: point});\n }\n }\n }\n\n override render() {\n const [width, height] = this.resizeController.value ?? [0, 0];\n\n this.scaleX.range([this.margin.left, width - this.margin.right]);\n this.scaleY.range([height - this.margin.bottom, this.margin.top]);\n\n this.area.y0(height - this.margin.bottom);\n\n this.yGrid.tickSize(-width + this.margin.left + this.margin.right);\n this.xGrid.tickSize(height - this.margin.top - this.margin.bottom);\n\n const xTicks = width / this.tickSize.x;\n const yTicks = height / this.tickSize.y;\n this.xAxis.ticks(xTicks);\n this.xGrid.ticks(xTicks);\n this.yAxis.ticks(yTicks);\n this.yGrid.ticks(yTicks);\n\n select(this.querySelector('.axis.x')).call(this.xAxis);\n select(this.querySelector('.axis.y')).call(this.yAxis);\n select(this.querySelector('.grid.x')).call(this.xGrid);\n select(this.querySelector('.grid.y')).call(this.yGrid);\n\n return svg`\n <svg width=\"${width}\" height=\"${height}\" xmlns=\"http://www.w3.org/2000/svg\">\n <g class=\"grid y\" transform=\"translate(${this.margin.left}, 0)\" />\n <g class=\"grid x\" transform=\"translate(0, ${this.margin.bottom})\" />\n <g class=\"axis x\" transform=\"translate(0, ${height - this.margin.bottom})\" />\n <g class=\"axis y\" transform=\"translate(${this.margin.left}, 0)\" />\n\n ${guard([this.lines, width, height], () => svg`\n <path class=\"area\" d=\"${this.area(this.plotData)}\" />\n <path class=\"elevation\" d=\"${this.line(this.plotData)}\" fill=\"none\" />`\n )}\n\n <g style=\"visibility: ${this.pointer.x > 0 ? 'visible' : 'hidden'}\">\n <g clip-path=\"polygon(0 0, ${this.pointer.x - this.margin.left} 0, ${this.pointer.x - this.margin.left} 100%, 0 100%)\">\n ${guard([this.lines, width, height], () => svg`<path class=\"elevation highlight\" d=\"${this.line(this.plotData)}\" fill=\"none\" />`)}\n </g>\n <line\n class=\"pointer-line\"\n x1=\"${this.pointer.x}\"\n y1=\"${this.margin.top}\"\n x2=\"${this.pointer.x}\"\n y2=\"${height - this.margin.bottom}\"\n />\n <circle class=\"pointer-circle-outline\" cx=\"${this.pointer.x}\" cy=\"${this.pointer.y}\" r=\"16\"/>\n <circle class=\"pointer-circle\" cx=\"${this.pointer.x}\" cy=\"${this.pointer.y}\" r=\"6\"/>\n </g>\n\n ${this.pointsData.map((point, index) => this.pointSvg(this.scaleX(point.x), this.scaleY(point.y), index))}\n\n <rect\n width=\"${width}\"\n height=\"${height}\"\n fill=\"none\"\n pointer-events=\"${this.pointerEvents ? 'all' : 'none'}\"\n style=\"display: block; touch-action: none;\"\n @pointermove=\"${this.pointerMove}\"\n @pointerout=\"${this.pointerOut}\"\n />\n </svg>\n `;\n }\n\n public tickFormat(value: number, axis: 'x' | 'y') {\n if (axis === 'y' || value < 1000) {\n return this.meterFormat!.format(value);\n } else {\n return this.kilometerFormat!.format(value / 1000);\n }\n }\n\n public pointSvg(x: number, y: number, index: number): TemplateResult {\n return svg`<circle class=\"point\" cx=\"${x}\" cy=\"${y}\" r=\"10\"/>`;\n }\n\n override firstUpdated() {\n // FIXME: because the ref element are used before render is done, we need to force an update\n this.requestUpdate();\n }\n\n private pointerMove(event: PointerEvent) {\n const pointerDistance = this.scaleX.invert(pointer(event)[0]);\n const index = Math.min(this.bisectDistance.left(this.plotData, pointerDistance), this.plotData.length - 1);\n\n if (index < 0) {\n return;\n }\n // FIXME:\n // var d0 = this.plotData[index - 1]\n // var d1 = this.plotData[index];\n // // work out which date value is closest to the mouse\n // var d = mouseDate - d0[0] > d1[0] - mouseDate ? d1 : d0;\n\n const data = this.plotData[index];\n\n if (isNaN(data.y)) {\n return;\n }\n\n this.pointer = {\n x: this.scaleX(data.x),\n y: this.scaleY(data.y),\n };\n\n this.dispatchEvent(\n new CustomEvent<OverDetails>('over', {\n detail: {\n coordinate: this.plotData[index].coordinate,\n position: this.pointer\n }\n }),\n );\n }\n\n private pointerOut() {\n this.pointer = {\n x: 0,\n y: 0,\n };\n this.dispatchEvent(new CustomEvent('out'));\n }\n\n override createRenderRoot() {\n return this;\n }\n}\n\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'elevation-profile': ElevationProfile;\n }\n}\n"],"names":[],"version":3,"file":"elevation-profile.d.ts.map"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {svg as $agntW$svg, LitElement as $agntW$LitElement} from "lit";
|
|
2
2
|
import {property as $agntW$property, state as $agntW$state, customElement as $agntW$customElement} from "lit/decorators.js";
|
|
3
3
|
import {ResizeController as $agntW$ResizeController} from "@lit-labs/observers/resize-controller.js";
|
|
4
|
+
import {guard as $agntW$guard} from "lit/directives/guard.js";
|
|
4
5
|
import {bisector as $agntW$bisector, extent as $agntW$extent} from "d3-array";
|
|
5
6
|
import {scaleLinear as $agntW$scaleLinear} from "d3-scale";
|
|
6
7
|
import {line as $agntW$line, area as $agntW$area} from "d3-shape";
|
|
@@ -17,6 +18,7 @@ import $agntW$simplifyjs from "simplify-js";
|
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
|
|
21
|
+
|
|
20
22
|
var $916babf1e6dc2c08$var$__decorate = undefined && undefined.__decorate || function(decorators, target, key, desc) {
|
|
21
23
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
22
24
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -29,6 +31,7 @@ let $916babf1e6dc2c08$var$ElevationProfile = class ElevationProfile extends (0,
|
|
|
29
31
|
this.tolerance = 1;
|
|
30
32
|
this.locale = navigator.language;
|
|
31
33
|
this.lines = [];
|
|
34
|
+
this.points = [];
|
|
32
35
|
this.updateScale = (x, y, width, height)=>{};
|
|
33
36
|
this.margin = {
|
|
34
37
|
top: 20,
|
|
@@ -45,8 +48,14 @@ let $916babf1e6dc2c08$var$ElevationProfile = class ElevationProfile extends (0,
|
|
|
45
48
|
x: 0,
|
|
46
49
|
y: 0
|
|
47
50
|
};
|
|
48
|
-
this.
|
|
51
|
+
this.resizeController = new (0, $agntW$ResizeController)(this, {
|
|
52
|
+
callback: ()=>[
|
|
53
|
+
this.offsetWidth,
|
|
54
|
+
this.offsetHeight
|
|
55
|
+
]
|
|
56
|
+
});
|
|
49
57
|
this.plotData = [];
|
|
58
|
+
this.pointsData = [];
|
|
50
59
|
this.scaleX = (0, $agntW$scaleLinear)();
|
|
51
60
|
this.scaleY = (0, $agntW$scaleLinear)();
|
|
52
61
|
this.bisectDistance = (0, $agntW$bisector)((point)=>point.x);
|
|
@@ -91,10 +100,20 @@ let $916babf1e6dc2c08$var$ElevationProfile = class ElevationProfile extends (0,
|
|
|
91
100
|
this.scaleY.domain((0, $agntW$extent)(this.plotData, (data)=>data.y)).nice();
|
|
92
101
|
this.updateScale(this.scaleX, this.scaleY, this.offsetWidth, this.offsetHeight);
|
|
93
102
|
}
|
|
103
|
+
if (changedProperties.has("points")) {
|
|
104
|
+
this.pointsData.length = 0;
|
|
105
|
+
for (const point of this.points)this.pointsData.push({
|
|
106
|
+
x: point[3],
|
|
107
|
+
y: point[2],
|
|
108
|
+
coordinate: point
|
|
109
|
+
});
|
|
110
|
+
}
|
|
94
111
|
}
|
|
95
112
|
render() {
|
|
96
|
-
const width = this.
|
|
97
|
-
|
|
113
|
+
const [width, height] = this.resizeController.value ?? [
|
|
114
|
+
0,
|
|
115
|
+
0
|
|
116
|
+
];
|
|
98
117
|
this.scaleX.range([
|
|
99
118
|
this.margin.left,
|
|
100
119
|
width - this.margin.right
|
|
@@ -122,14 +141,25 @@ let $916babf1e6dc2c08$var$ElevationProfile = class ElevationProfile extends (0,
|
|
|
122
141
|
<g class="grid x" transform="translate(0, ${this.margin.bottom})" />
|
|
123
142
|
<g class="axis x" transform="translate(0, ${height - this.margin.bottom})" />
|
|
124
143
|
<g class="axis y" transform="translate(${this.margin.left}, 0)" />
|
|
125
|
-
|
|
126
|
-
|
|
144
|
+
|
|
145
|
+
${(0, $agntW$guard)([
|
|
146
|
+
this.lines,
|
|
147
|
+
width,
|
|
148
|
+
height
|
|
149
|
+
], ()=>(0, $agntW$svg)`
|
|
150
|
+
<path class="area" d="${this.area(this.plotData)}" />
|
|
151
|
+
<path class="elevation" d="${this.line(this.plotData)}" fill="none" />`)}
|
|
152
|
+
|
|
127
153
|
<g style="visibility: ${this.pointer.x > 0 ? "visible" : "hidden"}">
|
|
128
|
-
<path
|
|
129
|
-
|
|
130
|
-
|
|
154
|
+
<g clip-path="polygon(0 0, ${this.pointer.x - this.margin.left} 0, ${this.pointer.x - this.margin.left} 100%, 0 100%)">
|
|
155
|
+
${(0, $agntW$guard)([
|
|
156
|
+
this.lines,
|
|
157
|
+
width,
|
|
158
|
+
height
|
|
159
|
+
], ()=>(0, $agntW$svg)`<path class="elevation highlight" d="${this.line(this.plotData)}" fill="none" />`)}
|
|
160
|
+
</g>
|
|
131
161
|
<line
|
|
132
|
-
class="pointer-line
|
|
162
|
+
class="pointer-line"
|
|
133
163
|
x1="${this.pointer.x}"
|
|
134
164
|
y1="${this.margin.top}"
|
|
135
165
|
x2="${this.pointer.x}"
|
|
@@ -138,6 +168,9 @@ let $916babf1e6dc2c08$var$ElevationProfile = class ElevationProfile extends (0,
|
|
|
138
168
|
<circle class="pointer-circle-outline" cx="${this.pointer.x}" cy="${this.pointer.y}" r="16"/>
|
|
139
169
|
<circle class="pointer-circle" cx="${this.pointer.x}" cy="${this.pointer.y}" r="6"/>
|
|
140
170
|
</g>
|
|
171
|
+
|
|
172
|
+
${this.pointsData.map((point, index)=>this.pointSvg(this.scaleX(point.x), this.scaleY(point.y), index))}
|
|
173
|
+
|
|
141
174
|
<rect
|
|
142
175
|
width="${width}"
|
|
143
176
|
height="${height}"
|
|
@@ -154,6 +187,9 @@ let $916babf1e6dc2c08$var$ElevationProfile = class ElevationProfile extends (0,
|
|
|
154
187
|
if (axis === "y" || value < 1000) return this.meterFormat.format(value);
|
|
155
188
|
else return this.kilometerFormat.format(value / 1000);
|
|
156
189
|
}
|
|
190
|
+
pointSvg(x, y, index) {
|
|
191
|
+
return (0, $agntW$svg)`<circle class="point" cx="${x}" cy="${y}" r="10"/>`;
|
|
192
|
+
}
|
|
157
193
|
firstUpdated() {
|
|
158
194
|
// FIXME: because the ref element are used before render is done, we need to force an update
|
|
159
195
|
this.requestUpdate();
|
|
@@ -206,6 +242,11 @@ $916babf1e6dc2c08$var$__decorate([
|
|
|
206
242
|
type: Array
|
|
207
243
|
})
|
|
208
244
|
], $916babf1e6dc2c08$var$ElevationProfile.prototype, "lines", void 0);
|
|
245
|
+
$916babf1e6dc2c08$var$__decorate([
|
|
246
|
+
(0, $agntW$property)({
|
|
247
|
+
type: Array
|
|
248
|
+
})
|
|
249
|
+
], $916babf1e6dc2c08$var$ElevationProfile.prototype, "points", void 0);
|
|
209
250
|
$916babf1e6dc2c08$var$__decorate([
|
|
210
251
|
(0, $agntW$property)()
|
|
211
252
|
], $916babf1e6dc2c08$var$ElevationProfile.prototype, "updateScale", void 0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"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","sources":["elevation-profile.ts"],"sourcesContent":["import {LitElement, svg} from 'lit';\nimport {customElement, state, property} from 'lit/decorators.js';\nimport {ResizeController} from '@lit-labs/observers/resize-controller.js';\nimport type {PropertyValues} from 'lit';\n\nimport {extent, bisector} from 'd3-array';\nimport {scaleLinear} from 'd3-scale';\nimport {line, area} from 'd3-shape';\nimport {axisBottom, axisLeft} from 'd3-axis';\nimport {select, pointer} from 'd3-selection';\n\nimport simplify from 'simplify-js';\n\ntype PlotPoint = {\n x: number;\n y: number;\n coordinate: number[];\n};\n\nexport type OverDetails = {\n coordinate: number[];\n position: {x: number; y: number};\n};\n\n@customElement('elevation-profile')\nexport default class ElevationProfile extends LitElement {\n @property({type: Number}) tolerance = 1;\n @property({type: String}) locale = navigator.language;\n @property({type: Array}) lines: number[][][] = [];\n @property() updateScale = (x: scaleLinear, y: scaleLinear, width: number, height: number): void => {};\n @property({type: Object}) margin = {top: 20, right: 20, bottom: 20, left: 40};\n @property({type: Object}) tickSize = {x: 100, y: 40};\n @property({type: Boolean}) pointerEvents = true;\n\n @state() pointer = {x: 0, y: 0};\n private _resizeController = new ResizeController(this, {});\n\n private plotData: PlotPoint[] = [];\n private scaleX = scaleLinear();\n private scaleY = scaleLinear();\n\n private bisectDistance = bisector((point: PlotPoint) => point.x);\n\n private line = line()\n .defined((point: PlotPoint) => !isNaN(point.y))\n .x((point: PlotPoint) => this.scaleX(point.x))\n .y((point: PlotPoint) => this.scaleY(point.y));\n private area = area()\n .defined((point: PlotPoint) => !isNaN(point.y))\n .x((point: PlotPoint) => this.scaleX(point.x))\n .y1((point: PlotPoint) => this.scaleY(point.y));\n private xAxis = axisBottom(this.scaleX).tickFormat((value: number) => this.tickFormat(value, 'x'));\n private yAxis = axisLeft(this.scaleY).tickFormat((value: number) => this.tickFormat(value, 'y'));\n private xGrid = axisBottom(this.scaleX).tickFormat(() => '');\n private yGrid = axisLeft(this.scaleY).tickFormat(() => '');\n\n private meterFormat: Intl.NumberFormat | null = null;\n private kilometerFormat: Intl.NumberFormat | null = null;\n\n override updated(changedProperties: PropertyValues) {\n if (changedProperties.has('locale')) {\n this.meterFormat = new Intl.NumberFormat(this.locale, {\n style: 'unit',\n unit: 'meter',\n });\n\n this.kilometerFormat = new Intl.NumberFormat(this.locale, {\n style: 'unit',\n unit: 'kilometer',\n });\n }\n }\n\n override willUpdate(changedProperties: PropertyValues) {\n if (changedProperties.has('lines')) {\n this.plotData.length = 0;\n for (const line of this.lines) {\n const data = line.map((coordinate) => ({x: coordinate[3], y: coordinate[2], coordinate}));\n this.plotData.push(...simplify(data, this.tolerance));\n this.plotData.push({x: line[line.length - 1][3], y: NaN, coordinate: []});\n }\n\n this.scaleX.domain(extent(this.plotData, (data: PlotPoint) => data.x));\n this.scaleY.domain(extent(this.plotData, (data: PlotPoint) => data.y)).nice();\n\n this.updateScale(this.scaleX, this.scaleY, this.offsetWidth, this.offsetHeight);\n }\n }\n\n override render() {\n const width = this.offsetWidth;\n const height = this.offsetHeight;\n\n this.scaleX.range([this.margin.left, width - this.margin.right]);\n this.scaleY.range([height - this.margin.bottom, this.margin.top]);\n\n this.area.y0(height - this.margin.bottom);\n\n this.yGrid.tickSize(-width + this.margin.left + this.margin.right);\n this.xGrid.tickSize(height - this.margin.top - this.margin.bottom);\n\n const xTicks = width / this.tickSize.x;\n const yTicks = height / this.tickSize.y;\n this.xAxis.ticks(xTicks);\n this.xGrid.ticks(xTicks);\n this.yAxis.ticks(yTicks);\n this.yGrid.ticks(yTicks);\n\n select(this.querySelector('.axis.x')).call(this.xAxis);\n select(this.querySelector('.axis.y')).call(this.yAxis);\n select(this.querySelector('.grid.x')).call(this.xGrid);\n select(this.querySelector('.grid.y')).call(this.yGrid);\n\n return svg`\n <svg width=\"${width}\" height=\"${height}\" xmlns=\"http://www.w3.org/2000/svg\">\n <g class=\"grid y\" transform=\"translate(${this.margin.left}, 0)\" />\n <g class=\"grid x\" transform=\"translate(0, ${this.margin.bottom})\" />\n <g class=\"axis x\" transform=\"translate(0, ${height - this.margin.bottom})\" />\n <g class=\"axis y\" transform=\"translate(${this.margin.left}, 0)\" />\n <path class=\"area\" d=\"${this.area(this.plotData)}\" />\n <path class=\"elevation\" d=\"${this.line(this.plotData)}\" fill=\"none\" />\n <g style=\"visibility: ${this.pointer.x > 0 ? 'visible' : 'hidden'}\">\n <path class=\"elevation highlight\" d=\"${this.line(this.plotData)}\" fill=\"none\"\n clip-path=\"polygon(0 0, ${this.pointer.x - this.margin.left} 0, ${this.pointer.x - this.margin.left} 100%, 0 100%)\"\n />\n <line\n class=\"pointer-line y\"\n x1=\"${this.pointer.x}\"\n y1=\"${this.margin.top}\"\n x2=\"${this.pointer.x}\"\n y2=\"${height - this.margin.bottom}\"\n />\n <circle class=\"pointer-circle-outline\" cx=\"${this.pointer.x}\" cy=\"${this.pointer.y}\" r=\"16\"/>\n <circle class=\"pointer-circle\" cx=\"${this.pointer.x}\" cy=\"${this.pointer.y}\" r=\"6\"/>\n </g>\n <rect\n width=\"${width}\"\n height=\"${height}\"\n fill=\"none\"\n pointer-events=\"${this.pointerEvents ? 'all' : 'none'}\"\n style=\"display: block; touch-action: none;\"\n @pointermove=\"${this.pointerMove}\"\n @pointerout=\"${this.pointerOut}\"\n />\n </svg>\n `;\n }\n\n public tickFormat(value: number, axis: 'x' | 'y') {\n if (axis === 'y' || value < 1000) {\n return this.meterFormat!.format(value);\n } else {\n return this.kilometerFormat!.format(value / 1000);\n }\n }\n\n override firstUpdated() {\n // FIXME: because the ref element are used before render is done, we need to force an update\n this.requestUpdate();\n }\n\n private pointerMove(event: PointerEvent) {\n const pointerDistance = this.scaleX.invert(pointer(event)[0]);\n const index = Math.min(this.bisectDistance.left(this.plotData, pointerDistance), this.plotData.length - 1);\n\n if (index < 0) {\n return;\n }\n // FIXME:\n // var d0 = this.plotData[index - 1]\n // var d1 = this.plotData[index];\n // // work out which date value is closest to the mouse\n // var d = mouseDate - d0[0] > d1[0] - mouseDate ? d1 : d0;\n\n const data = this.plotData[index];\n\n if (isNaN(data.y)) {\n return;\n }\n\n this.pointer = {\n x: this.scaleX(data.x),\n y: this.scaleY(data.y),\n };\n\n this.dispatchEvent(\n new CustomEvent<OverDetails>('over', {\n detail: {\n coordinate: this.plotData[index].coordinate,\n position: this.pointer\n }\n }),\n );\n }\n\n private pointerOut() {\n this.pointer = {\n x: 0,\n y: 0,\n };\n this.dispatchEvent(new CustomEvent('out'));\n }\n\n override createRenderRoot() {\n return this;\n }\n}\n\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'elevation-profile': ElevationProfile;\n }\n}\n"],"names":[],"version":3,"file":"elevation-profile.js.map"}
|
|
1
|
+
{"mappings":"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","sources":["elevation-profile.ts"],"sourcesContent":["import {LitElement, svg} from 'lit';\nimport {customElement, state, property} from 'lit/decorators.js';\nimport {ResizeController} from '@lit-labs/observers/resize-controller.js';\nimport {guard} from 'lit/directives/guard.js';\nimport type {PropertyValues, TemplateResult} from 'lit';\n\nimport {extent, bisector} from 'd3-array';\nimport {scaleLinear} from 'd3-scale';\nimport {line, area} from 'd3-shape';\nimport {axisBottom, axisLeft} from 'd3-axis';\nimport {select, pointer} from 'd3-selection';\n\nimport simplify from 'simplify-js';\n\ntype PlotPoint = {\n x: number;\n y: number;\n coordinate: number[];\n};\n\nexport type OverDetails = {\n coordinate: number[];\n position: {x: number; y: number};\n};\n\n@customElement('elevation-profile')\nexport default class ElevationProfile extends LitElement {\n @property({type: Number}) tolerance = 1;\n @property({type: String}) locale = navigator.language;\n @property({type: Array}) lines: number[][][] = [];\n @property({type: Array}) points: number[][] = [];\n @property() updateScale = (x: scaleLinear, y: scaleLinear, width: number, height: number): void => {};\n @property({type: Object}) margin = {top: 20, right: 20, bottom: 20, left: 40};\n @property({type: Object}) tickSize = {x: 100, y: 40};\n @property({type: Boolean}) pointerEvents = true;\n\n @state() pointer = {x: 0, y: 0};\n private resizeController = new ResizeController(this, {\n callback: () => [this.offsetWidth, this.offsetHeight],\n });\n\n private plotData: PlotPoint[] = [];\n private pointsData: PlotPoint[] = [];\n private scaleX = scaleLinear();\n private scaleY = scaleLinear();\n\n private bisectDistance = bisector((point: PlotPoint) => point.x);\n\n private line = line()\n .defined((point: PlotPoint) => !isNaN(point.y))\n .x((point: PlotPoint) => this.scaleX(point.x))\n .y((point: PlotPoint) => this.scaleY(point.y));\n private area = area()\n .defined((point: PlotPoint) => !isNaN(point.y))\n .x((point: PlotPoint) => this.scaleX(point.x))\n .y1((point: PlotPoint) => this.scaleY(point.y));\n private xAxis = axisBottom(this.scaleX).tickFormat((value: number) => this.tickFormat(value, 'x'));\n private yAxis = axisLeft(this.scaleY).tickFormat((value: number) => this.tickFormat(value, 'y'));\n private xGrid = axisBottom(this.scaleX).tickFormat(() => '');\n private yGrid = axisLeft(this.scaleY).tickFormat(() => '');\n\n private meterFormat: Intl.NumberFormat | null = null;\n private kilometerFormat: Intl.NumberFormat | null = null;\n\n override updated(changedProperties: PropertyValues) {\n if (changedProperties.has('locale')) {\n this.meterFormat = new Intl.NumberFormat(this.locale, {\n style: 'unit',\n unit: 'meter',\n });\n\n this.kilometerFormat = new Intl.NumberFormat(this.locale, {\n style: 'unit',\n unit: 'kilometer',\n });\n }\n }\n\n override willUpdate(changedProperties: PropertyValues) {\n if (changedProperties.has('lines')) {\n this.plotData.length = 0;\n for (const line of this.lines) {\n const data = line.map((coordinate) => ({x: coordinate[3], y: coordinate[2], coordinate}));\n this.plotData.push(...simplify(data, this.tolerance));\n this.plotData.push({x: line[line.length - 1][3], y: NaN, coordinate: []});\n }\n\n this.scaleX.domain(extent(this.plotData, (data: PlotPoint) => data.x));\n this.scaleY.domain(extent(this.plotData, (data: PlotPoint) => data.y)).nice();\n\n this.updateScale(this.scaleX, this.scaleY, this.offsetWidth, this.offsetHeight);\n }\n if (changedProperties.has('points')) {\n this.pointsData.length = 0;\n for (const point of this.points) {\n this.pointsData.push({x: point[3], y: point[2], coordinate: point});\n }\n }\n }\n\n override render() {\n const [width, height] = this.resizeController.value ?? [0, 0];\n\n this.scaleX.range([this.margin.left, width - this.margin.right]);\n this.scaleY.range([height - this.margin.bottom, this.margin.top]);\n\n this.area.y0(height - this.margin.bottom);\n\n this.yGrid.tickSize(-width + this.margin.left + this.margin.right);\n this.xGrid.tickSize(height - this.margin.top - this.margin.bottom);\n\n const xTicks = width / this.tickSize.x;\n const yTicks = height / this.tickSize.y;\n this.xAxis.ticks(xTicks);\n this.xGrid.ticks(xTicks);\n this.yAxis.ticks(yTicks);\n this.yGrid.ticks(yTicks);\n\n select(this.querySelector('.axis.x')).call(this.xAxis);\n select(this.querySelector('.axis.y')).call(this.yAxis);\n select(this.querySelector('.grid.x')).call(this.xGrid);\n select(this.querySelector('.grid.y')).call(this.yGrid);\n\n return svg`\n <svg width=\"${width}\" height=\"${height}\" xmlns=\"http://www.w3.org/2000/svg\">\n <g class=\"grid y\" transform=\"translate(${this.margin.left}, 0)\" />\n <g class=\"grid x\" transform=\"translate(0, ${this.margin.bottom})\" />\n <g class=\"axis x\" transform=\"translate(0, ${height - this.margin.bottom})\" />\n <g class=\"axis y\" transform=\"translate(${this.margin.left}, 0)\" />\n\n ${guard([this.lines, width, height], () => svg`\n <path class=\"area\" d=\"${this.area(this.plotData)}\" />\n <path class=\"elevation\" d=\"${this.line(this.plotData)}\" fill=\"none\" />`\n )}\n\n <g style=\"visibility: ${this.pointer.x > 0 ? 'visible' : 'hidden'}\">\n <g clip-path=\"polygon(0 0, ${this.pointer.x - this.margin.left} 0, ${this.pointer.x - this.margin.left} 100%, 0 100%)\">\n ${guard([this.lines, width, height], () => svg`<path class=\"elevation highlight\" d=\"${this.line(this.plotData)}\" fill=\"none\" />`)}\n </g>\n <line\n class=\"pointer-line\"\n x1=\"${this.pointer.x}\"\n y1=\"${this.margin.top}\"\n x2=\"${this.pointer.x}\"\n y2=\"${height - this.margin.bottom}\"\n />\n <circle class=\"pointer-circle-outline\" cx=\"${this.pointer.x}\" cy=\"${this.pointer.y}\" r=\"16\"/>\n <circle class=\"pointer-circle\" cx=\"${this.pointer.x}\" cy=\"${this.pointer.y}\" r=\"6\"/>\n </g>\n\n ${this.pointsData.map((point, index) => this.pointSvg(this.scaleX(point.x), this.scaleY(point.y), index))}\n\n <rect\n width=\"${width}\"\n height=\"${height}\"\n fill=\"none\"\n pointer-events=\"${this.pointerEvents ? 'all' : 'none'}\"\n style=\"display: block; touch-action: none;\"\n @pointermove=\"${this.pointerMove}\"\n @pointerout=\"${this.pointerOut}\"\n />\n </svg>\n `;\n }\n\n public tickFormat(value: number, axis: 'x' | 'y') {\n if (axis === 'y' || value < 1000) {\n return this.meterFormat!.format(value);\n } else {\n return this.kilometerFormat!.format(value / 1000);\n }\n }\n\n public pointSvg(x: number, y: number, index: number): TemplateResult {\n return svg`<circle class=\"point\" cx=\"${x}\" cy=\"${y}\" r=\"10\"/>`;\n }\n\n override firstUpdated() {\n // FIXME: because the ref element are used before render is done, we need to force an update\n this.requestUpdate();\n }\n\n private pointerMove(event: PointerEvent) {\n const pointerDistance = this.scaleX.invert(pointer(event)[0]);\n const index = Math.min(this.bisectDistance.left(this.plotData, pointerDistance), this.plotData.length - 1);\n\n if (index < 0) {\n return;\n }\n // FIXME:\n // var d0 = this.plotData[index - 1]\n // var d1 = this.plotData[index];\n // // work out which date value is closest to the mouse\n // var d = mouseDate - d0[0] > d1[0] - mouseDate ? d1 : d0;\n\n const data = this.plotData[index];\n\n if (isNaN(data.y)) {\n return;\n }\n\n this.pointer = {\n x: this.scaleX(data.x),\n y: this.scaleY(data.y),\n };\n\n this.dispatchEvent(\n new CustomEvent<OverDetails>('over', {\n detail: {\n coordinate: this.plotData[index].coordinate,\n position: this.pointer\n }\n }),\n );\n }\n\n private pointerOut() {\n this.pointer = {\n x: 0,\n y: 0,\n };\n this.dispatchEvent(new CustomEvent('out'));\n }\n\n override createRenderRoot() {\n return this;\n }\n}\n\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'elevation-profile': ElevationProfile;\n }\n}\n"],"names":[],"version":3,"file":"elevation-profile.js.map"}
|
package/elevation-profile.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {LitElement, svg} from 'lit';
|
|
2
2
|
import {customElement, state, property} from 'lit/decorators.js';
|
|
3
3
|
import {ResizeController} from '@lit-labs/observers/resize-controller.js';
|
|
4
|
-
import
|
|
4
|
+
import {guard} from 'lit/directives/guard.js';
|
|
5
|
+
import type {PropertyValues, TemplateResult} from 'lit';
|
|
5
6
|
|
|
6
7
|
import {extent, bisector} from 'd3-array';
|
|
7
8
|
import {scaleLinear} from 'd3-scale';
|
|
@@ -27,15 +28,19 @@ export default class ElevationProfile extends LitElement {
|
|
|
27
28
|
@property({type: Number}) tolerance = 1;
|
|
28
29
|
@property({type: String}) locale = navigator.language;
|
|
29
30
|
@property({type: Array}) lines: number[][][] = [];
|
|
31
|
+
@property({type: Array}) points: number[][] = [];
|
|
30
32
|
@property() updateScale = (x: scaleLinear, y: scaleLinear, width: number, height: number): void => {};
|
|
31
33
|
@property({type: Object}) margin = {top: 20, right: 20, bottom: 20, left: 40};
|
|
32
34
|
@property({type: Object}) tickSize = {x: 100, y: 40};
|
|
33
35
|
@property({type: Boolean}) pointerEvents = true;
|
|
34
36
|
|
|
35
37
|
@state() pointer = {x: 0, y: 0};
|
|
36
|
-
private
|
|
38
|
+
private resizeController = new ResizeController(this, {
|
|
39
|
+
callback: () => [this.offsetWidth, this.offsetHeight],
|
|
40
|
+
});
|
|
37
41
|
|
|
38
42
|
private plotData: PlotPoint[] = [];
|
|
43
|
+
private pointsData: PlotPoint[] = [];
|
|
39
44
|
private scaleX = scaleLinear();
|
|
40
45
|
private scaleY = scaleLinear();
|
|
41
46
|
|
|
@@ -85,11 +90,16 @@ export default class ElevationProfile extends LitElement {
|
|
|
85
90
|
|
|
86
91
|
this.updateScale(this.scaleX, this.scaleY, this.offsetWidth, this.offsetHeight);
|
|
87
92
|
}
|
|
93
|
+
if (changedProperties.has('points')) {
|
|
94
|
+
this.pointsData.length = 0;
|
|
95
|
+
for (const point of this.points) {
|
|
96
|
+
this.pointsData.push({x: point[3], y: point[2], coordinate: point});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
88
99
|
}
|
|
89
100
|
|
|
90
101
|
override render() {
|
|
91
|
-
const width = this.
|
|
92
|
-
const height = this.offsetHeight;
|
|
102
|
+
const [width, height] = this.resizeController.value ?? [0, 0];
|
|
93
103
|
|
|
94
104
|
this.scaleX.range([this.margin.left, width - this.margin.right]);
|
|
95
105
|
this.scaleY.range([height - this.margin.bottom, this.margin.top]);
|
|
@@ -117,14 +127,18 @@ export default class ElevationProfile extends LitElement {
|
|
|
117
127
|
<g class="grid x" transform="translate(0, ${this.margin.bottom})" />
|
|
118
128
|
<g class="axis x" transform="translate(0, ${height - this.margin.bottom})" />
|
|
119
129
|
<g class="axis y" transform="translate(${this.margin.left}, 0)" />
|
|
120
|
-
|
|
121
|
-
|
|
130
|
+
|
|
131
|
+
${guard([this.lines, width, height], () => svg`
|
|
132
|
+
<path class="area" d="${this.area(this.plotData)}" />
|
|
133
|
+
<path class="elevation" d="${this.line(this.plotData)}" fill="none" />`
|
|
134
|
+
)}
|
|
135
|
+
|
|
122
136
|
<g style="visibility: ${this.pointer.x > 0 ? 'visible' : 'hidden'}">
|
|
123
|
-
<path
|
|
124
|
-
|
|
125
|
-
|
|
137
|
+
<g clip-path="polygon(0 0, ${this.pointer.x - this.margin.left} 0, ${this.pointer.x - this.margin.left} 100%, 0 100%)">
|
|
138
|
+
${guard([this.lines, width, height], () => svg`<path class="elevation highlight" d="${this.line(this.plotData)}" fill="none" />`)}
|
|
139
|
+
</g>
|
|
126
140
|
<line
|
|
127
|
-
class="pointer-line
|
|
141
|
+
class="pointer-line"
|
|
128
142
|
x1="${this.pointer.x}"
|
|
129
143
|
y1="${this.margin.top}"
|
|
130
144
|
x2="${this.pointer.x}"
|
|
@@ -133,6 +147,9 @@ export default class ElevationProfile extends LitElement {
|
|
|
133
147
|
<circle class="pointer-circle-outline" cx="${this.pointer.x}" cy="${this.pointer.y}" r="16"/>
|
|
134
148
|
<circle class="pointer-circle" cx="${this.pointer.x}" cy="${this.pointer.y}" r="6"/>
|
|
135
149
|
</g>
|
|
150
|
+
|
|
151
|
+
${this.pointsData.map((point, index) => this.pointSvg(this.scaleX(point.x), this.scaleY(point.y), index))}
|
|
152
|
+
|
|
136
153
|
<rect
|
|
137
154
|
width="${width}"
|
|
138
155
|
height="${height}"
|
|
@@ -154,6 +171,10 @@ export default class ElevationProfile extends LitElement {
|
|
|
154
171
|
}
|
|
155
172
|
}
|
|
156
173
|
|
|
174
|
+
public pointSvg(x: number, y: number, index: number): TemplateResult {
|
|
175
|
+
return svg`<circle class="point" cx="${x}" cy="${y}" r="10"/>`;
|
|
176
|
+
}
|
|
177
|
+
|
|
157
178
|
override firstUpdated() {
|
|
158
179
|
// FIXME: because the ref element are used before render is done, we need to force an update
|
|
159
180
|
this.requestUpdate();
|