ol-elevation-profile 0.5.0 → 1.0.0
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 +18 -4
- package/dist/ol-elevation-profile.css +24 -6
- package/dist/ol-elevation-profile.esm.js +488 -57
- package/dist/ol-elevation-profile.esm.min.js +7 -5
- package/dist/ol-elevation-profile.js +488 -57
- package/dist/ol-elevation-profile.min.js +7 -5
- package/package.json +3 -3
- package/src/ol-elevation-profile.css +24 -6
- package/src/ol-elevation-profile.js +488 -57
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
A synchronized, themeable **elevation profile control for [OpenLayers](https://openlayers.org/)**, rendered with [d3](https://d3js.org/).
|
|
11
11
|
|
|
12
|
-
It reads elevation (**Z**) directly from a track's geometry (`[lon, lat, z]` GPX/GeoJSON)
|
|
12
|
+
It reads elevation (**Z**) directly from a track's geometry (`[lon, lat, z]` GPX/GeoJSON) — distance, ascent/descent and min/max are computed from the track itself, with no service involved. A track that carries **no** Z is filled from keyless [terrain tiles](#terrain-model), which is on by default; `dem: null` turns it off. Clicking (or hovering) a track shows its profile; a marker stays synchronized on both the map and the chart, and a click on the empty map hides it. The control is fully responsive (full-width docked bar on phones), supports slope-class colouring, metric smoothing, an A↔B crop, six themes, transparency, and a track-colour mode.
|
|
13
13
|
|
|
14
14
|
## Screenshots
|
|
15
15
|
|
|
@@ -68,29 +68,43 @@ vectorSource.addFeatures(feats)
|
|
|
68
68
|
profile.setFeature(feats[0]) // or let a click on the track select it
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
+
Any OpenLayers-readable format works (GeoJSON, GPX, KML, …) — the control only consumes OL `Feature`s. Read GPX/KML with `ol.format.GPX` / `ol.format.KML` and pass the line feature to `setFeature`.
|
|
72
|
+
|
|
71
73
|
Full option reference: see the [documentation](#documentation).
|
|
72
74
|
|
|
73
75
|
## Features
|
|
74
76
|
|
|
75
77
|
### Slope classes
|
|
76
78
|
|
|
77
|
-
With `slope: true`, the profile is split into contiguous portions of the same slope class (width `slopeClassSize`, in %), capped at `maxClasses` (default 8). Colours run from **blue** (flattest class) to **red** (steepest), via cyan/green and a pure yellow, spread across the classes that are actually present
|
|
79
|
+
With `slope: true`, the profile is split into contiguous portions of the same slope class (width `slopeClassSize`, in %), capped at `maxClasses` (default 8). Colours run from **blue** (flattest class) to **red** (steepest), via cyan/green and a pure yellow, spread across the classes that are actually present (not stretched on the real maximum slope). A vertical separator marks each class change, and a legend appears under the title.
|
|
78
80
|
|
|
79
81
|
### Smoothing
|
|
80
82
|
|
|
81
83
|
`smoothing` is a sliding-window average over **metres** of track (`0` = none, the default). Because the unit is metric, the result is independent of GPS point density; it softens both the profile and the slope.
|
|
82
84
|
|
|
85
|
+
### Terrain model
|
|
86
|
+
|
|
87
|
+
A track with **no Z** — drawn by hand, traced over a basemap, exported by a tool that drops the third dimension — would get no profile. The missing elevations are read from [AWS Terrain Tiles](https://registry.opendata.aws/terrain-tiles/) instead, **by default**: PNG tiles carrying elevation in their R/G/B channels, **no API key, no quota, no rate limit**. A 10 000-point track costs a handful of tiles where a free elevation API would cost 100 requests. Pass `dem: null` to disable it and keep the control off the network.
|
|
88
|
+
|
|
89
|
+
Elevation is interpolated bilinearly between the four surrounding pixels — reading the containing pixel would make the profile advance in stairs, and every stair counts as a climb then a descent in the D+. A track is filled **entirely or not at all**: a profile missing a few points dives to sea level and its D+ becomes absurd. Tracks that already carry their own Z are untouched.
|
|
90
|
+
|
|
91
|
+
Accuracy is roughly 30–90 m depending on the region (mean 16 m from IGN's 1 m reference on steep alpine terrain). Any XYZ tile set in `terrarium` or `mapbox` encoding can be used instead. See the [guide](https://lc-4918.github.io/ol-elevation-profile/guide/features#terrain-model).
|
|
92
|
+
|
|
83
93
|
### Attributions
|
|
84
94
|
|
|
85
95
|
When the profile occupies the bottom-right corner (or full-width at the bottom), the OpenLayers attribution control is automatically lifted **above** the profile, right-aligned, with a vertical gap equal to the map-edge-to-profile-bottom gap. Other placements leave the attribution untouched.
|
|
86
96
|
|
|
97
|
+
### Time
|
|
98
|
+
|
|
99
|
+
If the track carries time data (`coordTimes` ISO timestamps from GPX `<time>`, `coordinateProperties.times`, or a 4th `M` coordinate), add `'duration'` to `headerItems` for the **total elapsed time** in the title, and `'time'` to `tooltipItems` for the **elapsed time at the cursor**. The unit adapts: `7 sec`, `26 min`, `1 h 48 min`, `2 j 3 h`. By default this is **moving time** (stopped segments below `stopSpeed`, 0.5 m/s, are excluded); set `ignoreStops: false` for raw wall-clock time. Detect availability with `OlElevationProfile.featureHasTime(feature)`.
|
|
100
|
+
|
|
87
101
|
## Demo
|
|
88
102
|
|
|
89
|
-
Live, interactive demo
|
|
103
|
+
Live, interactive [demo](https://lc-4918.github.io/ol-elevation-profile/demo/) (toggle every option)
|
|
90
104
|
|
|
91
105
|
## Documentation
|
|
92
106
|
|
|
93
|
-
Full guide and API
|
|
107
|
+
[Full guide and API](https://lc-4918.github.io/ol-elevation-profile/) (English & French)
|
|
94
108
|
|
|
95
109
|
## License
|
|
96
110
|
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
}
|
|
22
22
|
.ol-elevation-profile.oep-transparent { backdrop-filter: blur(2px); }
|
|
23
23
|
|
|
24
|
-
/* ---- 8
|
|
24
|
+
/* ---- 8 anchors ---- */
|
|
25
25
|
.ol-elevation-profile.oep-pos-bottom { left: 50%; bottom: .6em; transform: translateX(-50%); }
|
|
26
26
|
.ol-elevation-profile.oep-pos-top { left: 50%; top: .6em; transform: translateX(-50%); }
|
|
27
27
|
.ol-elevation-profile.oep-pos-left { left: .6em; top: 50%; transform: translateY(-50%); }
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
.ol-elevation-profile.oep-pos-bottom-left { left: .6em; bottom: .6em; }
|
|
32
32
|
.ol-elevation-profile.oep-pos-bottom-right { right: .6em; bottom: .6em; }
|
|
33
33
|
|
|
34
|
-
/* ---- mobile
|
|
34
|
+
/* ---- mobile: full width, pinned to the top or the bottom ---- */
|
|
35
35
|
.ol-elevation-profile.oep-mobile { left: 0 !important; right: 0 !important; width: auto !important; transform: none !important; }
|
|
36
36
|
.ol-elevation-profile.oep-mobile.oep-pos-bottom { bottom: 0; border-radius: 10px 10px 0 0; }
|
|
37
37
|
.ol-elevation-profile.oep-mobile.oep-pos-top { top: 0; border-radius: 0 0 10px 10px; }
|
|
38
38
|
|
|
39
|
-
/* ----
|
|
39
|
+
/* ---- header ---- */
|
|
40
40
|
.oep-header { display: flex; align-items: center; gap: 8px; width: 100%; padding: 0 2px 2px; }
|
|
41
41
|
.oep-title { flex: 0 1 auto; min-width: 0; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
42
42
|
.oep-title a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
.oep-stats .oep-up { color: #c0392b; }
|
|
46
46
|
.oep-stats .oep-down { color: #2471a3; }
|
|
47
47
|
|
|
48
|
-
/* ----
|
|
48
|
+
/* ---- zoom toolbar ---- */
|
|
49
49
|
.oep-toolbar { flex: 0 0 auto; display: inline-flex; gap: 2px; margin-left: auto; }
|
|
50
50
|
.oep-tbtn {
|
|
51
51
|
display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 22px;
|
|
@@ -65,14 +65,14 @@
|
|
|
65
65
|
.oep-toggle:hover { opacity: 1; background: rgba(0, 0, 0, .07); }
|
|
66
66
|
.oep-toggle svg { width: 15px; height: 15px; display: block; }
|
|
67
67
|
|
|
68
|
-
/* ----
|
|
68
|
+
/* ---- slope legend ---- */
|
|
69
69
|
.oep-legend { display: flex; flex-wrap: wrap; gap: 4px 10px; padding: 0 2px 4px; font-size: 10px; opacity: .9; }
|
|
70
70
|
.oep-leg-it { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
|
|
71
71
|
.oep-sw { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }
|
|
72
72
|
|
|
73
73
|
.oep-body { overflow: hidden; }
|
|
74
74
|
|
|
75
|
-
/* ---- mode
|
|
75
|
+
/* ---- collapsed mode: the control shrinks to title + button width ---- */
|
|
76
76
|
.ol-elevation-profile.oep-collapsed { width: auto !important; }
|
|
77
77
|
.ol-elevation-profile.oep-collapsed .oep-body,
|
|
78
78
|
.ol-elevation-profile.oep-collapsed .oep-legend { display: none !important; }
|
|
@@ -80,6 +80,22 @@
|
|
|
80
80
|
.ol-elevation-profile.oep-collapsed .oep-toolbar { display: none !important; }
|
|
81
81
|
.ol-elevation-profile.oep-collapsed .oep-header { width: auto; }
|
|
82
82
|
|
|
83
|
+
/* ---- terrain-model loading spinner ---- */
|
|
84
|
+
/* Takes --oep-area, so it wears the theme colour - and the track colour under color:'auto'. */
|
|
85
|
+
.oep-loading { display: flex; align-items: center; justify-content: center; }
|
|
86
|
+
.oep-spinner {
|
|
87
|
+
width: 26px; height: 26px; box-sizing: border-box;
|
|
88
|
+
border: 3px solid var(--oep-area);
|
|
89
|
+
border-top-color: transparent;
|
|
90
|
+
border-radius: 50%;
|
|
91
|
+
opacity: .9;
|
|
92
|
+
animation: oep-spin .8s linear infinite;
|
|
93
|
+
}
|
|
94
|
+
@keyframes oep-spin { to { transform: rotate(360deg); } }
|
|
95
|
+
/* A still ring reads as a broken control rather than a wait, so the motion stays -
|
|
96
|
+
slowed to a crawl, which is what the reduced-motion request is really about. */
|
|
97
|
+
@media (prefers-reduced-motion: reduce) { .oep-spinner { animation-duration: 4s; } }
|
|
98
|
+
|
|
83
99
|
/* ---- chart ---- */
|
|
84
100
|
.oep-svg { display: block; }
|
|
85
101
|
.oep-area { fill: var(--oep-area); opacity: .6; }
|
|
@@ -108,3 +124,5 @@
|
|
|
108
124
|
box-shadow: 0 0 0 1px rgba(0, 0, 0, .35); pointer-events: none;
|
|
109
125
|
}
|
|
110
126
|
.ol-elevation-profile.oep-floating { transform: none; }
|
|
127
|
+
|
|
128
|
+
.oep-time{opacity:.85}
|