@raruto/leaflet-elevation 2.0.1 → 2.0.4
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 +7 -4
- package/dist/leaflet-elevation.css +114 -137
- package/dist/leaflet-elevation.js +175 -232
- package/dist/leaflet-elevation.js.map +1 -1
- package/dist/leaflet-elevation.min.css +1 -1
- package/dist/leaflet-elevation.min.js +1 -1
- package/dist/leaflet-elevation.min.js.map +1 -1
- package/images/elevation-steelblue.svg +1 -1
- package/libs/fullpage.css +13 -0
- package/libs/leaflet-distance-marker.js +97 -92
- package/libs/leaflet-distance-marker.min.js +1 -1
- package/libs/leaflet-gpxgroup.js +1 -1
- package/libs/leaflet-gpxgroup.min.js +1 -1
- package/package.json +1 -1
- package/src/components/chart.js +82 -54
- package/src/components/d3.js +74 -17
- package/src/control.js +159 -218
- package/src/handlers/altitude.js +5 -2
- package/src/handlers/heart.js +2 -2
- package/src/index.css +114 -137
- package/src/options.js +9 -9
- package/src/utils.js +5 -5
|
@@ -143,102 +143,107 @@ L.DistanceMarkers = L.LayerGroup.extend({
|
|
|
143
143
|
// Get line coords as an array
|
|
144
144
|
let coords = typeof line.getLatLngs == 'function' ? line.getLatLngs() : line;
|
|
145
145
|
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
146
|
+
// Handle "MultiLineString" features
|
|
147
|
+
coords = L.LineUtil.isFlat(coords) ? [coords] : coords;
|
|
148
|
+
|
|
149
|
+
coords.forEach(latlngs => {
|
|
150
|
+
// Get accumulated line lengths as well as overall length
|
|
151
|
+
let accumulated = L.GeometryUtil.accumulatedLengths(latlngs);
|
|
152
|
+
let length = accumulated.length > 0 ? accumulated[accumulated.length - 1] : 0;
|
|
153
|
+
|
|
154
|
+
// count = Number of distance markers to be added
|
|
155
|
+
// j = Position in accumulated line length array
|
|
156
|
+
for (let i = 1, count = Math.floor(length / options.offset), j = 0; i <= count; ++i) {
|
|
157
|
+
|
|
158
|
+
let distance = options.offset * i;
|
|
159
|
+
|
|
160
|
+
// Find the first accumulated distance that is greater than the distance of this marker
|
|
161
|
+
while (j < accumulated.length - 1 && accumulated[j] < distance) ++j;
|
|
162
|
+
|
|
163
|
+
// Grab two nearest points either side marker position
|
|
164
|
+
let p1 = latlngs[j - 1];
|
|
165
|
+
let p2 = latlngs[j];
|
|
166
|
+
let m_line = L.polyline([p1, p2]);
|
|
167
|
+
|
|
168
|
+
// and create a simple line to interpolate on
|
|
169
|
+
let ratio = (distance - accumulated[j - 1]) / (accumulated[j] - accumulated[j - 1]);
|
|
170
|
+
let position = L.GeometryUtil.interpolateOnLine(map, m_line, ratio);
|
|
171
|
+
let delta = map.project(p2).subtract(map.project(p1));
|
|
172
|
+
let angle = Math.atan2(delta.y, delta.x);
|
|
173
|
+
|
|
174
|
+
// Generate distance marker label
|
|
175
|
+
let text = options.textFunction.call(this, distance, i, options.offset);
|
|
176
|
+
|
|
177
|
+
// Grouping layer of visible layers at zoom level (arrow + distance)
|
|
178
|
+
let zoom = this._minimumZoomLevelForItem(i, showAll);
|
|
179
|
+
let markers = this._zoomLayers[zoom] = this._zoomLayers[zoom] || L.layerGroup()
|
|
180
|
+
|
|
181
|
+
// create arrow markers
|
|
182
|
+
if (options.direction && ((options.distance && i % 2 == 1) || !options.distance)) {
|
|
183
|
+
if (preferCanvas) {
|
|
184
|
+
markers.addLayer(
|
|
185
|
+
new L.DistanceMarker(p1, {
|
|
186
|
+
radius: 0,
|
|
187
|
+
icon: {
|
|
188
|
+
url: options.arrowUrl, //image link
|
|
189
|
+
size: options.arrowSize, //image size ( default [40, 40] )
|
|
190
|
+
rotate: 0, //image base rotate ( default 0 )
|
|
191
|
+
offset: { x: 0, y: 0 }, //image offset ( default { x: 0, y: 0 } )
|
|
192
|
+
},
|
|
193
|
+
rotation: angle,
|
|
194
|
+
interactive: false,
|
|
195
|
+
// label: '⮞', //'➜',
|
|
196
|
+
// font: 'normal 20pt "Helvetica Neue", Arial, Helvetica, sans-serif',
|
|
197
|
+
// fillStyle: 'white',//'#3366CC',
|
|
198
|
+
// strokeStyle: 'black',
|
|
199
|
+
})
|
|
200
|
+
);
|
|
201
|
+
} else {
|
|
202
|
+
markers.addLayer(
|
|
203
|
+
L.marker(position.latLng, {
|
|
204
|
+
icon: L.icon({
|
|
205
|
+
iconUrl: options.arrowUrl,
|
|
206
|
+
iconSize: options.arrowSize,
|
|
207
|
+
}),
|
|
208
|
+
// NB the following option is added by "leaflet-rotate"
|
|
209
|
+
rotation: angle * L.DomUtil.RAD_TO_DEG,
|
|
210
|
+
interactive: false,
|
|
211
|
+
})
|
|
212
|
+
);
|
|
213
|
+
}
|
|
209
214
|
}
|
|
210
|
-
}
|
|
211
215
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
216
|
+
// create distance markers
|
|
217
|
+
if (options.distance && i % 2 == 0) {
|
|
218
|
+
if (preferCanvas) {
|
|
219
|
+
markers.addLayer(
|
|
220
|
+
new L.DistanceMarker(position.latLng, {
|
|
221
|
+
label: text, // TODO: handle text rotation (leaflet-rotate)
|
|
222
|
+
radius: 7,
|
|
223
|
+
fillColor: '#fff',
|
|
224
|
+
fillOpacity: 1,
|
|
225
|
+
fillStyle: 'black',
|
|
226
|
+
color: '#777',
|
|
227
|
+
weight: 1,
|
|
228
|
+
interactive: false,
|
|
229
|
+
})
|
|
230
|
+
);
|
|
231
|
+
} else {
|
|
232
|
+
markers.addLayer(
|
|
233
|
+
L.marker(position.latLng, {
|
|
234
|
+
title: text,
|
|
235
|
+
icon: L.divIcon({
|
|
236
|
+
className: options.cssClass,
|
|
237
|
+
html: text,
|
|
238
|
+
iconSize: options.iconSize
|
|
239
|
+
}),
|
|
240
|
+
interactive: false,
|
|
241
|
+
})
|
|
242
|
+
);
|
|
243
|
+
}
|
|
239
244
|
}
|
|
240
245
|
}
|
|
241
|
-
}
|
|
246
|
+
});
|
|
242
247
|
|
|
243
248
|
const updateMarkerVisibility = () => {
|
|
244
249
|
let oldZoom = this._lastZoomLevel || 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
L.DistanceMarker=L.CircleMarker.extend({_updatePath:function(){let ctx=this._renderer._ctx,p=this._point;if(this.options.rotation=this.options.rotation||0,this.options.radius&&this._renderer._updateCircle&&this._renderer._updateCircle(this),this.options.icon&&this.options.icon.url)if(this.options.icon.element){const icon=this.options.icon;let cx=p.x+icon.offset.x,cy=p.y+icon.offset.y;ctx.save(),icon.rotate&&(ctx.translate(p.x,p.y),ctx.rotate(icon.rotate),cx=0,cy=0),ctx.drawImage(icon.element,cx-icon.size[0]/2,cy-icon.size[1]/2,icon.size[0],icon.size[1]),ctx.restore()}else{const icon=document.createElement("img");this.options.icon=L.extend({rotate:0,size:[40,40],offset:{x:0,y:0}},this.options.icon),this.options.icon.rotate+=this.options.rotation,this.options.icon.element=icon,icon.src=this.options.icon.url,icon.onload=()=>this.redraw(),icon.onerror=()=>this.options.icon=null}if(this.options.label){let cx=p.x,cy=p.y;ctx.save(),ctx.font=this.options.font||'normal 7pt "Helvetica Neue", Arial, Helvetica, sans-serif',ctx.textAlign="center",ctx.textBaseline="middle",ctx.fillStyle=this.options.fillStyle||"black",this.options.rotation&&(ctx.translate(p.x,p.y),ctx.rotate(this.options.rotation),cx=0,cy=0),this._map.getZoom()>17&&(ctx.fillStyle=this.options.strokeStyle||"black"),ctx.fillText(this.options.label,cx,cy),this.options.strokeStyle&&this._map.getZoom()<=17&&(ctx.strokeStyle=this.options.strokeStyle,ctx.strokeText(this.options.label,cx,cy)),ctx.restore()}}}),L.DistanceMarkers=L.LayerGroup.extend({options:{cssClass:"dist-marker",iconSize:[12,12],arrowSize:[10,10],arrowUrl:"data:image/svg+xml,%3Csvg transform='rotate(90)' xmlns='http://www.w3.org/2000/svg' width='560px' height='560px' viewBox='0 0 560 560'%3E%3Cpath stroke-width='35' fill='%23000' stroke='%23FFF' d='M280,40L522,525L280,420L38,525z'/%3E%3C/svg%3E",offset:1e3,showAll:12,textFunction:(distance,i,offset)=>i,distance:!0,direction:!0},initialize:function(line,map,options){this._layers={},this._zoomLayers={},options=L.setOptions(this,options);let preferCanvas=map.options.preferCanvas,showAll=Math.min(map.getMaxZoom(),options.showAll);preferCanvas||map.options.rotate||console.warn('Missing dependency: "leaflet-rotate"');let coords="function"==typeof line.getLatLngs?line.getLatLngs():line,accumulated=L.GeometryUtil.accumulatedLengths(
|
|
1
|
+
L.DistanceMarker=L.CircleMarker.extend({_updatePath:function(){let ctx=this._renderer._ctx,p=this._point;if(this.options.rotation=this.options.rotation||0,this.options.radius&&this._renderer._updateCircle&&this._renderer._updateCircle(this),this.options.icon&&this.options.icon.url)if(this.options.icon.element){const icon=this.options.icon;let cx=p.x+icon.offset.x,cy=p.y+icon.offset.y;ctx.save(),icon.rotate&&(ctx.translate(p.x,p.y),ctx.rotate(icon.rotate),cx=0,cy=0),ctx.drawImage(icon.element,cx-icon.size[0]/2,cy-icon.size[1]/2,icon.size[0],icon.size[1]),ctx.restore()}else{const icon=document.createElement("img");this.options.icon=L.extend({rotate:0,size:[40,40],offset:{x:0,y:0}},this.options.icon),this.options.icon.rotate+=this.options.rotation,this.options.icon.element=icon,icon.src=this.options.icon.url,icon.onload=()=>this.redraw(),icon.onerror=()=>this.options.icon=null}if(this.options.label){let cx=p.x,cy=p.y;ctx.save(),ctx.font=this.options.font||'normal 7pt "Helvetica Neue", Arial, Helvetica, sans-serif',ctx.textAlign="center",ctx.textBaseline="middle",ctx.fillStyle=this.options.fillStyle||"black",this.options.rotation&&(ctx.translate(p.x,p.y),ctx.rotate(this.options.rotation),cx=0,cy=0),this._map.getZoom()>17&&(ctx.fillStyle=this.options.strokeStyle||"black"),ctx.fillText(this.options.label,cx,cy),this.options.strokeStyle&&this._map.getZoom()<=17&&(ctx.strokeStyle=this.options.strokeStyle,ctx.strokeText(this.options.label,cx,cy)),ctx.restore()}}}),L.DistanceMarkers=L.LayerGroup.extend({options:{cssClass:"dist-marker",iconSize:[12,12],arrowSize:[10,10],arrowUrl:"data:image/svg+xml,%3Csvg transform='rotate(90)' xmlns='http://www.w3.org/2000/svg' width='560px' height='560px' viewBox='0 0 560 560'%3E%3Cpath stroke-width='35' fill='%23000' stroke='%23FFF' d='M280,40L522,525L280,420L38,525z'/%3E%3C/svg%3E",offset:1e3,showAll:12,textFunction:(distance,i,offset)=>i,distance:!0,direction:!0},initialize:function(line,map,options){this._layers={},this._zoomLayers={},options=L.setOptions(this,options);let preferCanvas=map.options.preferCanvas,showAll=Math.min(map.getMaxZoom(),options.showAll);preferCanvas||map.options.rotate||console.warn('Missing dependency: "leaflet-rotate"');let coords="function"==typeof line.getLatLngs?line.getLatLngs():line;coords=L.LineUtil.isFlat(coords)?[coords]:coords,coords.forEach(latlngs=>{let accumulated=L.GeometryUtil.accumulatedLengths(latlngs),length=accumulated.length>0?accumulated[accumulated.length-1]:0;for(let i=1,count=Math.floor(length/options.offset),j=0;i<=count;++i){let distance=options.offset*i;for(;j<accumulated.length-1&&accumulated[j]<distance;)++j;let p1=latlngs[j-1],p2=latlngs[j],m_line=L.polyline([p1,p2]),ratio=(distance-accumulated[j-1])/(accumulated[j]-accumulated[j-1]),position=L.GeometryUtil.interpolateOnLine(map,m_line,ratio),delta=map.project(p2).subtract(map.project(p1)),angle=Math.atan2(delta.y,delta.x),text=options.textFunction.call(this,distance,i,options.offset),zoom=this._minimumZoomLevelForItem(i,showAll),markers=this._zoomLayers[zoom]=this._zoomLayers[zoom]||L.layerGroup();options.direction&&(options.distance&&i%2==1||!options.distance)&&(preferCanvas?markers.addLayer(new L.DistanceMarker(p1,{radius:0,icon:{url:options.arrowUrl,size:options.arrowSize,rotate:0,offset:{x:0,y:0}},rotation:angle,interactive:!1})):markers.addLayer(L.marker(position.latLng,{icon:L.icon({iconUrl:options.arrowUrl,iconSize:options.arrowSize}),rotation:angle*L.DomUtil.RAD_TO_DEG,interactive:!1}))),options.distance&&i%2==0&&(preferCanvas?markers.addLayer(new L.DistanceMarker(position.latLng,{label:text,radius:7,fillColor:"#fff",fillOpacity:1,fillStyle:"black",color:"#777",weight:1,interactive:!1})):markers.addLayer(L.marker(position.latLng,{title:text,icon:L.divIcon({className:options.cssClass,html:text,iconSize:options.iconSize}),interactive:!1})))}});const updateMarkerVisibility=()=>{let oldZoom=this._lastZoomLevel||0,newZoom=map.getZoom();if(newZoom>oldZoom)for(let i=oldZoom+1;i<=newZoom;++i)void 0!==this._zoomLayers[i]&&this.addLayer(this._zoomLayers[i]);else if(newZoom<oldZoom)for(let i=oldZoom;i>newZoom;--i)void 0!==this._zoomLayers[i]&&this.removeLayer(this._zoomLayers[i]);this._lastZoomLevel=newZoom};map.on("zoomend",updateMarkerVisibility),updateMarkerVisibility()},_minimumZoomLevelForItem:function(i,zoom){for(;i>0&&i%2==0;)--zoom,i=Math.floor(i/2);return zoom}}),L.Polyline.include({_originalOnAdd:L.Polyline.prototype.onAdd,_originalOnRemove:L.Polyline.prototype.onRemove,addDistanceMarkers:function(){this._map&&this._distanceMarkers&&this._map.addLayer(this._distanceMarkers)},removeDistanceMarkers:function(){this._map&&this._distanceMarkers&&this._map.removeLayer(this._distanceMarkers)},onAdd:function(map){this._originalOnAdd(map);let opts=this.options.distanceMarkers||{};this.options.distanceMarkers&&(this._distanceMarkers=this._distanceMarkers||new L.DistanceMarkers(this,map,opts)),void 0!==opts.lazy&&!1!==opts.lazy||this.addDistanceMarkers()},onRemove:function(map){this.removeDistanceMarkers(),this._originalOnRemove(map)}});
|
package/libs/leaflet-gpxgroup.js
CHANGED
|
@@ -164,7 +164,7 @@ L.GpxGroup = L.Class.extend({
|
|
|
164
164
|
.then(text => this._elevation._parseFromString(text))
|
|
165
165
|
.then(geojson => {
|
|
166
166
|
if(geojson) {
|
|
167
|
-
geojson.name = geojson.name
|
|
167
|
+
geojson.name = geojson.name || (geojson[0] && geojson[0].properties.name) || track.split('/').pop().split('#')[0].split('?')[0];
|
|
168
168
|
this._loadRoute(geojson);
|
|
169
169
|
}
|
|
170
170
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
L.Mixin.Selectable={includes:L.Mixin.Events,setSelected:function(s){var selected=!!s;this._selected!==selected&&(this._selected=selected,this.fire("selected"))},isSelected:function(){return!!this._selected}},L.Mixin.Selection={includes:L.Mixin.Events,getSelection:function(){return this._selected},setSelection:function(item){this._selected===item?null!==item&&(item.setSelected(!item.isSelected()),item.isSelected()||(this._selected=null)):(this._selected&&this._selected.setSelected(!1),this._selected=item,this._selected&&this._selected.setSelected(!0)),this.fire("selection_changed")}},L.Control.LayersLegend=L.Control.Layers.extend({_onInputClick:function(){
|
|
1
|
+
L.Mixin.Selectable={includes:L.Mixin.Events,setSelected:function(s){var selected=!!s;this._selected!==selected&&(this._selected=selected,this.fire("selected"))},isSelected:function(){return!!this._selected}},L.Mixin.Selection={includes:L.Mixin.Events,getSelection:function(){return this._selected},setSelection:function(item){this._selected===item?null!==item&&(item.setSelected(!item.isSelected()),item.isSelected()||(this._selected=null)):(this._selected&&this._selected.setSelected(!1),this._selected=item,this._selected&&this._selected.setSelected(!0)),this.fire("selection_changed")}},L.Control.LayersLegend=L.Control.Layers.extend({_onInputClick:function(){this._handlingClick=!0,this._layerControlInputs.reduceRight((_,input)=>{if(input.checked)return this._map.fireEvent("legend_selected",{layer:this._getLayer(input.layerId).layer,input:input},!0),input},0),this._handlingClick=!1,this._refocusOnMap()}}),L.control.layersLegend=(baseLayers,overlays,options)=>new L.Control.LayersLegend(baseLayers,overlays,options),L.GeoJSON.include(L.Mixin.Selectable),L.GpxGroup=L.Class.extend({options:{highlight:{color:"#ff0",opacity:1},points:[],points_options:{icon:{iconUrl:"../images/elevation-poi.png",iconSize:[12,12]}},flyToBounds:!0,legend:!1,legend_options:{position:"topright",collapsed:!1},elevation:!0,elevation_options:{theme:"yellow-theme",detached:!0,elevationDiv:"#elevation-div"},distanceMarkers:!0,distanceMarkers_options:{lazy:!0}},initialize:function(tracks,options){L.Util.setOptions(this,options),this._count=0,this._loadedCount=0,this._tracks=tracks,this._layers=L.featureGroup(),this._markers=L.featureGroup(),this._elevation=L.control.elevation(this.options.elevation_options),this._legend=L.control.layersLegend(null,null,this.options.legend_options),this.options.points.forEach(poi=>L.marker(poi.latlng,{icon:L.icon(this.options.points_options.icon)}).bindTooltip(poi.name,{direction:"auto"}).addTo(this._markers))},getBounds:function(){return this._layers.getBounds()},addTo:function(map){this._layers.addTo(map),this._markers.addTo(map),this._map=map,this.on("selection_changed",this._onSelectionChanged,this),this._map.on("legend_selected",this._onLegendSelected,this),this._tracks.forEach(this.addTrack,this)},addTrack:function(track){fetch(track).then(response=>response.ok&&response.text()).then(text=>this._elevation._parseFromString(text)).then(geojson=>{geojson&&(geojson.name=geojson.name||geojson[0]&&geojson[0].properties.name||track.split("/").pop().split("#")[0].split("?")[0],this._loadRoute(geojson))})},_loadRoute:function(data){if(data){var line_style={color:this._uniqueColors(this._tracks.length)[this._count++],opacity:.75,weight:5,distanceMarkers:this.options.distanceMarkers_options},route=L.geoJson(data,{name:data.name||"",style:feature=>line_style,distanceMarkers:line_style.distanceMarkers,originalStyle:line_style,filter:feature=>"Point"!=feature.geometry.type});this._elevation.import(this._elevation.__LGEOMUTIL).then(()=>{route.addTo(this._layers),route.eachLayer(layer=>this._onEachRouteLayer(route,layer)),this._onEachRouteLoaded(route)})}},_onEachRouteLayer:function(route,layer){var polyline=layer;route.on("selected",L.bind(this._onRouteSelected,this,route,polyline)),polyline.on("mouseover",L.bind(this._onRouteMouseOver,this,route,polyline)),polyline.on("mouseout",L.bind(this._onRouteMouseOut,this,route,polyline)),polyline.on("click",L.bind(this._onRouteClick,this,route,polyline)),polyline.bindTooltip(route.options.name,{direction:"auto",sticky:!0})},_onEachRouteLoaded:function(route){this.options.legend&&this._legend.addBaseLayer(route,'<svg id="legend_'+route._leaflet_id+'" width="25" height="10" version="1.1" xmlns="http://www.w3.org/2000/svg"><line x1="0" x2="50" y1="5" y2="5" stroke="'+route.options.originalStyle.color+'" fill="transparent" stroke-width="5" /></svg> '+route.options.name),this.fire("route_loaded",{route:route}),++this._loadedCount===this._tracks.length&&(this.fire("loaded"),this.options.flyToBounds&&this._map.flyToBounds(this.getBounds(),{duration:.25,easeLinearity:.25,noMoveStart:!0}),this.options.legend&&this._legend.addTo(this._map))},highlight:function(route,polyline){polyline.setStyle(this.options.highlight),this.options.distanceMarkers&&polyline.addDistanceMarkers()},unhighlight:function(route,polyline){polyline.setStyle(route.options.originalStyle),this.options.distanceMarkers&&polyline.removeDistanceMarkers()},_onRouteMouseOver:function(route,polyline){route.isSelected()||(this.highlight(route,polyline),this.options.legend&&(this.setSelection(route),L.DomUtil.get("legend_"+route._leaflet_id).parentNode.previousSibling.click())),this.fire("route_mouseover",{route:route,polyline:polyline})},_onRouteMouseOut:function(route,polyline){route.isSelected()||this.unhighlight(route,polyline),this.fire("route_mouseout",{route:route,polyline:polyline})},_onRouteClick:function(route,polyline){this.setSelection(route)},_onRouteSelected:function(route,polyline){route.isSelected()||this.unhighlight(route,polyline)},_onSelectionChanged:function(e){var elevation=this._elevation,eleDiv=elevation.getContainer(),route=this.getSelection();elevation.clear(),route&&route.isSelected()?(eleDiv||elevation.addTo(this._map),route.getLayers().forEach((function(layer){layer instanceof L.Polyline&&(elevation.addData(layer,!1),layer.bringToFront())}))):eleDiv&&elevation.remove()},_onLegendSelected:function(e){var parent=e.input.closest(".leaflet-control-layers-list"),route=e.layer;if(!route.isSelected()){for(var i in this.setSelection(route),route._layers)this.highlight(route,route._layers[i]);this._map.flyToBounds(e.layer.getBounds())}parent.scroll({top:e.input.offsetTop-parent.offsetTop||0,behavior:"smooth"}),this._layers.eachLayer(layer=>{var legend=L.DomUtil.get("legend_"+layer._leaflet_id);legend.querySelector("line").style.stroke=layer.isSelected()?this.options.highlight.color:"",legend.parentNode.style.fontWeight=layer.isSelected()?"700":""})},_uniqueColors:function(count){return 1===count?["#0000ff"]:new Array(count).fill(null).map((_,i)=>this._hsvToHex(i*(1/count),1,.7))},_hsvToHex:function(h,s,v){var i=Math.floor(6*h),f=6*h-i,p=v*(1-s),q=v*(1-f*s),t=v*(1-(1-f)*s),rgb;return{0:[v,t,p],1:[q,v,p],2:[p,v,t],3:[p,q,v],4:[t,p,v],5:[v,p,q]}[i%6].map(d=>255*d).reduce((hex,byte)=>hex+(byte>>4&15).toString(16)+(15&byte).toString(16),"#")},removeFrom:function(map){this._layers.removeFrom(map)}}),L.GpxGroup.include(L.Mixin.Events),L.GpxGroup.include(L.Mixin.Selection),L.gpxGroup=(tracks,options)=>new L.GpxGroup(tracks,options);
|
package/package.json
CHANGED
package/src/components/chart.js
CHANGED
|
@@ -85,6 +85,7 @@ export var Chart = L.Control.Elevation.Chart = L.Class.extend({
|
|
|
85
85
|
this._updateScale();
|
|
86
86
|
this._updateArea();
|
|
87
87
|
this._updateAxis();
|
|
88
|
+
this._updateMargins();
|
|
88
89
|
this._updateLegend();
|
|
89
90
|
this._updateClipper();
|
|
90
91
|
|
|
@@ -350,7 +351,7 @@ export var Chart = L.Control.Elevation.Chart = L.Class.extend({
|
|
|
350
351
|
if (typeof props.preferCanvas === "undefined") props.preferCanvas = opts.preferCanvas;
|
|
351
352
|
|
|
352
353
|
// Save paths in memory for latter usage
|
|
353
|
-
this._paths[props.name] = D3.Path(props)
|
|
354
|
+
this._paths[props.name] = D3.Path(props);
|
|
354
355
|
this._props.areas[props.name] = props;
|
|
355
356
|
|
|
356
357
|
if (opts.legend) {
|
|
@@ -536,80 +537,92 @@ export var Chart = L.Control.Elevation.Chart = L.Class.extend({
|
|
|
536
537
|
},
|
|
537
538
|
|
|
538
539
|
_updateLegend: function () {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
const legendOpts = {
|
|
543
|
-
width : this._width(),
|
|
544
|
-
height : this._height(),
|
|
545
|
-
margins: this.options.margins,
|
|
546
|
-
};
|
|
547
|
-
|
|
548
|
-
// Reset legend items
|
|
549
|
-
this._legend.selectAll('g').remove();
|
|
550
|
-
_.each(this._props.legendItems, (legend) => {
|
|
551
|
-
this._legend.append("g").call(D3.LegendItem(L.extend(legendOpts, legend)));
|
|
552
|
-
});
|
|
553
|
-
|
|
540
|
+
let xAxesB = this._axis.selectAll('.x.axis.bottom').nodes().length;
|
|
541
|
+
|
|
554
542
|
// Get legend items
|
|
555
|
-
let items
|
|
543
|
+
let items = Object.keys(this._paths);
|
|
556
544
|
|
|
557
545
|
// Calculate legend item positions
|
|
558
|
-
let n = items.
|
|
546
|
+
let n = items.length;
|
|
559
547
|
let v = Array(Math.floor(n / 2)).fill(null).map((d, i) => (i + 1) * 2 - (1 - Math.sign(n % 2)));
|
|
560
548
|
let rev = v.slice().reverse().map((d) => -(d));
|
|
561
549
|
|
|
550
|
+
// push a fake element to handle center alignment (odd numbers)
|
|
562
551
|
if (n % 2 !== 0) {
|
|
563
552
|
rev.push(0);
|
|
564
553
|
}
|
|
565
554
|
v = rev.concat(v);
|
|
566
555
|
|
|
567
|
-
//
|
|
568
|
-
|
|
569
|
-
let marginB = 30 + (xAxesB * 2);
|
|
570
|
-
let marginR = n * 30;
|
|
556
|
+
// Reset legend items
|
|
557
|
+
this._legend.selectAll('g').remove();
|
|
571
558
|
|
|
572
|
-
//
|
|
573
|
-
|
|
574
|
-
this.
|
|
575
|
-
|
|
576
|
-
|
|
559
|
+
// Render legend items
|
|
560
|
+
_.each(this._props.legendItems, (legend) => {
|
|
561
|
+
this._legend.append("g").call(
|
|
562
|
+
D3.LegendItem(L.extend({
|
|
563
|
+
width : this._width(),
|
|
564
|
+
height : this._height(),
|
|
565
|
+
margins: this.options.margins,
|
|
566
|
+
}, legend))
|
|
567
|
+
);
|
|
568
|
+
});
|
|
577
569
|
|
|
578
|
-
//
|
|
579
|
-
if(
|
|
580
|
-
this.
|
|
581
|
-
|
|
570
|
+
// Render legend item switcher
|
|
571
|
+
if (n > 1) {
|
|
572
|
+
this._legend.append("g").call(
|
|
573
|
+
D3.LegendSmall({
|
|
574
|
+
width : this._width(),
|
|
575
|
+
height : this._height(),
|
|
576
|
+
items : items,
|
|
577
|
+
onClick: (selected) => {
|
|
578
|
+
_.each(items, name => this._togglePath(name, selected == name, true));
|
|
579
|
+
this._updateArea();
|
|
580
|
+
}
|
|
581
|
+
})
|
|
582
|
+
);
|
|
582
583
|
}
|
|
584
|
+
|
|
585
|
+
_.each(items, (name, i) => {
|
|
586
|
+
// Adjust legend item positions
|
|
587
|
+
this._legend.select('[data-name=' + name + ']').attr("transform", "translate(" + (v[i] * 55) + ", " + (xAxesB * 2) + ")");
|
|
588
|
+
// Set initial state (disabled controls)
|
|
589
|
+
this._togglePath(name, !(name in this.options && this.options[name] == 'disabled'), true);
|
|
590
|
+
});
|
|
583
591
|
|
|
584
|
-
|
|
585
|
-
.each((d, i, n) => {
|
|
586
|
-
|
|
587
|
-
let legend = d3.select(n[i]);
|
|
588
|
-
let rect = legend.select('rect');
|
|
589
|
-
let name = legend.attr('data-name');
|
|
590
|
-
let path = this._paths[name];
|
|
592
|
+
},
|
|
591
593
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
+
_updateMargins: function() {
|
|
595
|
+
// Get chart margins
|
|
596
|
+
let xAxesB = this._axis.selectAll('.x.axis.bottom').nodes().length;
|
|
597
|
+
let xAxesL = this._axis.selectAll('.y.axis.left').nodes().length;
|
|
598
|
+
let xAxesR = this._axis.selectAll('.y.axis.right').nodes().length;
|
|
599
|
+
let marginB = 60 + (xAxesB * 2);
|
|
600
|
+
let marginL = 10 + (xAxesL * 30);
|
|
601
|
+
let marginR = 40 + (xAxesR * 30);
|
|
594
602
|
|
|
595
|
-
|
|
603
|
+
let marginsUpdated = false
|
|
596
604
|
|
|
597
|
-
|
|
598
|
-
|
|
605
|
+
// Adjust right margin
|
|
606
|
+
if (xAxesR && this.options.margins.right < marginR) {
|
|
607
|
+
this.options.margins.right = marginR;
|
|
608
|
+
marginsUpdated = true;
|
|
609
|
+
}
|
|
599
610
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
}
|
|
611
|
+
// Adjust left margin
|
|
612
|
+
if(xAxesL && this.options.margins.left < marginL) {
|
|
613
|
+
this.options.margins.left = marginL;
|
|
614
|
+
marginsUpdated = true;
|
|
615
|
+
}
|
|
606
616
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
617
|
+
// Adjust bottom margin
|
|
618
|
+
if(xAxesB && this.options.margins.bottom < marginB) {
|
|
619
|
+
this.options.margins.bottom = marginB;
|
|
620
|
+
marginsUpdated = true;
|
|
621
|
+
}
|
|
611
622
|
|
|
612
|
-
|
|
623
|
+
if (marginsUpdated) {
|
|
624
|
+
this.fire('margins_updated');
|
|
625
|
+
}
|
|
613
626
|
},
|
|
614
627
|
|
|
615
628
|
_updateScale: function() {
|
|
@@ -733,6 +746,21 @@ export var Chart = L.Control.Elevation.Chart = L.Class.extend({
|
|
|
733
746
|
}));
|
|
734
747
|
},
|
|
735
748
|
|
|
749
|
+
_togglePath(name, enabled = true, lazy = false) {
|
|
750
|
+
let path = this._paths[name];
|
|
751
|
+
let legend = this._container.select('.legend [data-name=' + name + ']');
|
|
752
|
+
path.classed('leaflet-hidden', !enabled);
|
|
753
|
+
legend.select('text').style('text-decoration-line', enabled ? '' : 'line-through');
|
|
754
|
+
legend.select('rect').style('fill-opacity', enabled ? '': '0');
|
|
755
|
+
// Apply d3-zoom (bind <clipPath> mask)
|
|
756
|
+
if (this._mask) {
|
|
757
|
+
path.attr('mask', 'url(#' + this._mask.attr('id') + ')');
|
|
758
|
+
}
|
|
759
|
+
if (!lazy) {
|
|
760
|
+
this._updateArea();
|
|
761
|
+
}
|
|
762
|
+
},
|
|
763
|
+
|
|
736
764
|
_hideDiagramIndicator: function() {
|
|
737
765
|
this._tooltip.attr("display", 'none');
|
|
738
766
|
},
|
package/src/components/d3.js
CHANGED
|
@@ -205,7 +205,6 @@ export const LegendItem = ({
|
|
|
205
205
|
.text(L._(label || name))
|
|
206
206
|
.attr("x", (width / 2) + 5)
|
|
207
207
|
.attr("font-size", 10)
|
|
208
|
-
.style("text-decoration-thickness", "2px")
|
|
209
208
|
.style("font-weight", "700")
|
|
210
209
|
.attr('y', height + margins.bottom / 2)
|
|
211
210
|
.attr('dy', "0.75em");
|
|
@@ -214,6 +213,70 @@ export const LegendItem = ({
|
|
|
214
213
|
}
|
|
215
214
|
};
|
|
216
215
|
|
|
216
|
+
export const LegendSmall = ({
|
|
217
|
+
width,
|
|
218
|
+
height,
|
|
219
|
+
items,
|
|
220
|
+
onClick
|
|
221
|
+
}) => {
|
|
222
|
+
return g => {
|
|
223
|
+
let idx = 0;
|
|
224
|
+
|
|
225
|
+
g.data([{
|
|
226
|
+
x: width,
|
|
227
|
+
y: height + 40
|
|
228
|
+
}]).attr("transform", d => "translate(" + d.x + "," + d.y + ")").classed('legend-switcher');
|
|
229
|
+
|
|
230
|
+
// let label = g.selectAll(".legend-switcher-label") .data([{ idx: 0 }]);
|
|
231
|
+
let label = g.append('svg:text')
|
|
232
|
+
.attr("class", "legend-switcher-label")
|
|
233
|
+
.attr("text-anchor", "end")
|
|
234
|
+
.attr("x", -25)
|
|
235
|
+
.attr("y", 5)
|
|
236
|
+
.on("mousedown", (e, d) => setIdx(L.Util.wrapNum((idx + 1), [0, items.length])));
|
|
237
|
+
|
|
238
|
+
let symbol = g.selectAll(".legend-switcher-symbol").data([
|
|
239
|
+
{ type: d3.symbolTriangle, x: 0, y: 3, angle: 0, size: 50, id: "down" },
|
|
240
|
+
{ type: d3.symbolTriangle, x: -13, y: 0, angle: 180, size: 50, id: "up" }
|
|
241
|
+
]);
|
|
242
|
+
|
|
243
|
+
symbol.exit().remove();
|
|
244
|
+
label.exit().remove();
|
|
245
|
+
|
|
246
|
+
symbol.enter()
|
|
247
|
+
.append("svg:path")
|
|
248
|
+
.attr("class", "legend-switcher-symbol")
|
|
249
|
+
.attr("cursor", 'pointer')
|
|
250
|
+
.attr("fill", "#000")
|
|
251
|
+
.merge(symbol)
|
|
252
|
+
.attr("d",
|
|
253
|
+
d3.symbol()
|
|
254
|
+
.type(d => d.type)
|
|
255
|
+
.size(d => d.size)
|
|
256
|
+
)
|
|
257
|
+
.attr("transform", d => "translate(" + d.x + "," + d.y + ") rotate(" + d.angle + ")")
|
|
258
|
+
.on("mousedown", (e, d) => setIdx(L.Util.wrapNum((d.id === "up" ? idx + 1 : idx - 1), [0, items.length])));
|
|
259
|
+
|
|
260
|
+
const setIdx = (id) => {
|
|
261
|
+
idx = id;
|
|
262
|
+
// label.enter()
|
|
263
|
+
// .append('text')
|
|
264
|
+
// .attr("class", "legend-switcher-label")
|
|
265
|
+
// .attr("text-anchor", "end")
|
|
266
|
+
// .attr("x", -25)
|
|
267
|
+
// .attr("y", 5)
|
|
268
|
+
// .merge(label.data([{ idx: id }]))
|
|
269
|
+
// .text(d => items.length ? (items[idx][0].toUpperCase() + items[idx].slice(1)) : '')
|
|
270
|
+
// .on("mousedown", (e, d) => setIdx(L.Util.wrapNum((idx + 1), [0, items.length])));
|
|
271
|
+
label.text(items.length ? (items[idx][0].toUpperCase() + items[idx].slice(1)) : '');
|
|
272
|
+
onClick(items[idx]);
|
|
273
|
+
};
|
|
274
|
+
setIdx(0);
|
|
275
|
+
|
|
276
|
+
return g;
|
|
277
|
+
};
|
|
278
|
+
};
|
|
279
|
+
|
|
217
280
|
export const Tooltip = ({
|
|
218
281
|
xCoord,
|
|
219
282
|
yCoord,
|
|
@@ -300,27 +363,21 @@ export const Ruler = ({
|
|
|
300
363
|
return g => {
|
|
301
364
|
|
|
302
365
|
g.data([{
|
|
303
|
-
|
|
304
|
-
|
|
366
|
+
x: 0,
|
|
367
|
+
y: height,
|
|
305
368
|
}])
|
|
306
369
|
.attr("transform", d => "translate(" + d.x + "," + d.y + ")");
|
|
307
370
|
|
|
308
|
-
let rect = g.selectAll('.horizontal-drag-rect')
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
let line = g.selectAll('.horizontal-drag-line')
|
|
312
|
-
.data([{ w: width }]);
|
|
313
|
-
|
|
314
|
-
let label = g.selectAll('.horizontal-drag-label')
|
|
315
|
-
.data([{ w: width - 8 }]);
|
|
316
|
-
|
|
371
|
+
let rect = g.selectAll('.horizontal-drag-rect').data([{ w: width }]);
|
|
372
|
+
let line = g.selectAll('.horizontal-drag-line').data([{ w: width }]);
|
|
373
|
+
let label = g.selectAll('.horizontal-drag-label').data([{ w: width - 8 }]);
|
|
317
374
|
let symbol = g.selectAll('.horizontal-drag-symbol')
|
|
318
375
|
.data([{
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
376
|
+
type: d3.symbolTriangle,
|
|
377
|
+
x: width + 7,
|
|
378
|
+
y: 0,
|
|
379
|
+
angle: -90,
|
|
380
|
+
size: 50
|
|
324
381
|
}]);
|
|
325
382
|
|
|
326
383
|
rect.exit().remove();
|