@raruto/leaflet-elevation 2.0.2 → 2.0.5

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.
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><defs/><path fill="#4682b4" fill-opacity=".8" d="M23.1 20.8l-4.2-8-3.9-.2-2.7 1.8-2.4-5.5-5.2 6.2v5.7" opacity=".9"/><path fill="none" stroke="#174a75" stroke-width="1.5" d="M4.7 14.8h1.1l4.3-6.2 2.5 5.6 2.5-2.3 3.7.2 4 8.3v.4"/><path fill="none" stroke="#737373" stroke-linecap="square" d="M3.4 5v19.8M24.7 22.4H.9"/></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 26 26"><defs/><path fill="#4682b4" fill-opacity=".8" d="M23.1 20.8l-4.2-8-3.9-.2-2.7 1.8-2.4-5.5-5.2 6.2v5.7" opacity=".9"/><path fill="none" stroke="#174a75" stroke-width="1.5" d="M4.7 14.8h1.1l4.3-6.2 2.5 5.6 2.5-2.3 3.7.2 4 8.3v.4"/><path fill="none" stroke="#737373" stroke-linecap="square" d="M3.4 5v19.8M24.7 22.4H.9"/></svg>
@@ -0,0 +1,13 @@
1
+ html,
2
+ body,
3
+ .leaflet-map {
4
+ height: 100%;
5
+ width: 100%;
6
+ padding: 0px;
7
+ margin: 0px;
8
+ }
9
+
10
+ body {
11
+ display: flex;
12
+ flex-direction: column;
13
+ }
@@ -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 ?? (geojson[0] && geojson[0].properties.name) ?? track.split('/').pop().split('#')[0].split('?')[0];
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(){var inputs=this._layerControlInputs,input,layer,addedLayers=[],removedLayers=[];this._handlingClick=!0;for(var i=inputs.length-1;i>=0;i--)input=inputs[i],layer=this._getLayer(input.layerId).layer,input.checked&&this._map.fireEvent("legend_selected",{layer:layer,input:input},!0);this._handlingClick=!1,this._refocusOnMap()}}),L.control.layersLegend=function(baseLayers,overlays,options){return 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",detachedView:!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._routes={},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);var icon=L.icon(this.options.points_options.icon);this.options.points.forEach((function(poi){var marker=L.marker(poi.latlng,{icon:icon});marker.bindTooltip(poi.name,{direction:"auto"}),marker.addTo(this._markers)}),this),void 0===L.DistanceMarkers&&(this.options.distanceMarkers=!1)},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(doc=>{let geojson;doc=doc.trim();try{let xml=(new DOMParser).parseFromString(doc,"text/xml"),type=xml.documentElement.tagName.toLowerCase(),name=xml.getElementsByTagName("name");type in toGeoJSON||(type="TrainingCenterDatabase"==xml.documentElement.tagName?"tcx":"gpx"),geojson=toGeoJSON[type](xml),geojson.name=name.length>0?Array.from(name).find(tag=>"trk"==tag.parentElement.tagName).textContent:""}catch(e){try{geojson=JSON.parse(doc.toString())}catch(e){console.warn("Error parsing track: "+track)}}geojson.name||(geojson.name=track.split("/").pop().split("#")[0].split("?")[0]),this._loadRoute(geojson)})})},_loadRoute:function(data){if(data){var colors,color,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});route.addTo(this._layers),this._routes[route._leaflet_id]=route,route.eachLayer(layer=>this._onRouteLayer(route,layer)),this._onRouteLoaded(route)}},_onRouteLayer: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})},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)},_onRouteLoaded: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))},_onSelectionChanged:function(e){var elevation=this._elevation,eleDiv=elevation.getContainer(),route=this.getSelection();elevation.clear(),route&&route.isSelected()?(eleDiv||elevation.loadChart(this._map),route.getLayers().forEach((function(layer){layer instanceof L.Polyline&&(elevation.addData(layer,!1),layer.bringToFront())}))):eleDiv&&elevation.remove()},_onLegendSelected:function(e){var layers=this._layers._layers,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())}var parent=e.input.closest(".leaflet-control-layers-list"),offset=e.input.offsetTop-parent.offsetTop;for(var j in parent.scroll({top:offset||0,behavior:"smooth"}),layers){var selected=layers[j].isSelected(),legend=L.DomUtil.get("legend_"+layers[j]._leaflet_id);legend.querySelector("line").style.stroke=selected?this.options.highlight.color:"",legend.parentNode.style.fontWeight=selected?"700":""}},_uniqueColors:function(count){if(0===count)return[];if(1===count)return["#0000ff"];for(var increment=1/count,colors=[],i=0;i<count;++i){var hue=i*increment,rgb=this._hsvToRgb(hue,1,.7),hex="#"+this._rgbToHex(rgb[0],rgb[1],rgb[2]);colors.push(hex)}return colors},_hsvToRgb:function(h,s,v){var r,g,b,i=Math.floor(6*h),f=6*h-i,p=v*(1-s),q=v*(1-f*s),t=v*(1-(1-f)*s);switch(i%6){case 0:r=v,g=t,b=p;break;case 1:r=q,g=v,b=p;break;case 2:r=p,g=v,b=t;break;case 3:r=p,g=q,b=v;break;case 4:r=t,g=p,b=v;break;case 5:r=v,g=p,b=q}return[255*r,255*g,255*b]},_rgbToHex:function(r,g,b){return this._byteToHex(r)+this._byteToHex(g)+this._byteToHex(b)},_byteToHex:function(n){return(n>>4&15).toString(16)+(15&n).toString(16)},removeFrom:function(map){this._layers.removeFrom(map)}}),L.GpxGroup.include(L.Mixin.Events),L.GpxGroup.include(L.Mixin.Selection),L.gpxGroup=function(tracks,options){return new L.GpxGroup(tracks,options)};
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raruto/leaflet-elevation",
3
- "version": "2.0.2",
3
+ "version": "2.0.5",
4
4
  "description": "A Leaflet plugin that allows to add elevation profiles using d3js",
5
5
  "main": "dist/leaflet-elevation.min.js",
6
6
  "module": "src/index.js",
@@ -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
- if (this.options.legend === false) return;
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 = this._legend.selectAll('.legend-item');
543
+ let items = Object.keys(this._paths);
556
544
 
557
545
  // Calculate legend item positions
558
- let n = items.nodes().length;
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
- // Get chart margins
568
- let xAxesB = this._axis.selectAll('.x.axis.bottom').nodes().length;
569
- let marginB = 30 + (xAxesB * 2);
570
- let marginR = n * 30;
556
+ // Reset legend items
557
+ this._legend.selectAll('g').remove();
571
558
 
572
- // Adjust chart right margins
573
- if (n && this.options.margins.right < marginR) {
574
- this.options.margins.right = marginR;
575
- this.fire('margins_updated');
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
- // Adjust chart bottom margins
579
- if(xAxesB && this.options.margins.bottom < marginB) {
580
- this.options.margins.bottom = marginB;
581
- this.fire('margins_updated');
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
- items
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
- let tx = v[i] * 55;
593
- let ty = xAxesB * 2;
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
- legend
603
+ let marginsUpdated = false
596
604
 
597
- // Adjust legend item positions
598
- .attr("transform", "translate(" + tx + ", " + ty + ")");
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
- // Set initial state (disabled controls)
601
- if (name in this.options && this.options[name] == 'disabled') {
602
- path.classed('leaflet-hidden', true);
603
- legend.select('text').style('text-decoration-line', 'line-through');
604
- legend.select('rect').style('fill-opacity', '0');
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
- // Apply d3-zoom (bind <clipPath> mask)
608
- if (this._mask) {
609
- path.attr('mask', 'url(#' + this._mask.attr('id') + ')');
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
  },
@@ -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
- "x": 0,
304
- "y": height,
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
- .data([{ w: width }]);
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
- "type": d3.symbolTriangle,
320
- "x": width + 7,
321
- "y": 0,
322
- "angle": -90,
323
- "size": 50
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();
package/src/control.js CHANGED
@@ -7,8 +7,8 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
7
7
  includes: L.Evented ? L.Evented.prototype : L.Mixin.Events,
8
8
 
9
9
  options: Options,
10
- __mileFactor: 0.621371, // 1 km = (0.621371 mi)
11
- __footFactor: 3.28084, // 1 m = (3.28084 ft)
10
+ __mileFactor: 0.621371, // 1 km = (0.621371 mi)
11
+ __footFactor: 3.28084, // 1 m = (3.28084 ft)
12
12
  __D3: 'https://unpkg.com/d3@6.5.0/dist/d3.min.js',
13
13
  __TOGEOJSON: 'https://unpkg.com/@tmcw/togeojson@4.6.0/dist/togeojson.umd.js',
14
14
  __LGEOMUTIL: 'https://unpkg.com/leaflet-geometryutil@0.9.3/src/leaflet.geometryutil.js',
@@ -37,9 +37,9 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
37
37
  */
38
38
  addTo: function(map) {
39
39
  if (this.options.detached) {
40
- let eleDiv = this._initElevationDiv();
41
- if (!eleDiv.isConnected) _.insert(map.getContainer(), eleDiv, 'afterend');
42
- _.append(eleDiv, this.onAdd(map));
40
+ let parent = _.select(this.options.elevationDiv);
41
+ let eleDiv = this.onAdd(map);
42
+ parent ? _.append(parent, eleDiv) : _.insert(map.getContainer(), eleDiv, 'afterend');
43
43
  } else {
44
44
  L.Control.prototype.addTo.call(this, map);
45
45
  }
@@ -221,7 +221,9 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
221
221
  onAdd: function(map) {
222
222
  this._map = map;
223
223
 
224
- let container = this._container = _.create("div", "elevation-control elevation " + this.options.theme + " " + (this.options.detached ? '' : 'leaflet-control'));
224
+ let container = this._container = _.create("div", "elevation-control " + this.options.theme + " " + (this.options.detached ? 'elevation-detached' : 'leaflet-control'), this.options.detached ? { id: 'elevation-' + _.randomId() } : {});
225
+
226
+ if (!this.eleDiv) this.eleDiv = container;
225
227
 
226
228
  let modules = [
227
229
  "Distance",
@@ -413,21 +415,6 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
413
415
  }
414
416
  },
415
417
 
416
- /**
417
- * Adds the control to the given "detached" div.
418
- */
419
- _initElevationDiv: function() {
420
- let eleDiv = _.select(this.options.elevationDiv);
421
- if (!eleDiv) {
422
- this.options.elevationDiv = '#elevation-div_' + _.randomId();
423
- eleDiv = _.create('div', 'leaflet-control elevation elevation-div', { id: this.options.elevationDiv.substr(1) });
424
- }
425
- if (this.options.detached) {
426
- _.replaceClass(eleDiv, 'leaflet-control', 'elevation-detached');
427
- }
428
- return this.eleDiv = eleDiv;
429
- },
430
-
431
418
  /**
432
419
  * Initialize "L.AlmostOver" and "L.DistanceMarkers"
433
420
  */
@@ -446,6 +433,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
446
433
  } else if (control.options.polyline) {
447
434
  layer.addTo(map);
448
435
  }
436
+ map.invalidateSize();
449
437
  } else {
450
438
  console.warn("Undefined elevation map object");;
451
439
  }
@@ -472,7 +460,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
472
460
  let oldProto = L.Canvas.prototype._fillStroke;
473
461
  let control = this;
474
462
 
475
- let theme = this.options.theme.replace('-theme', '');
463
+ let theme = this.options.theme.split(' ')[0].replace('-theme', '');
476
464
  let color = _.Colors[theme] || {};
477
465
 
478
466
  L.Canvas.include({
@@ -593,10 +581,6 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
593
581
  .on('margins_updated', this._resizeChart, this);
594
582
 
595
583
 
596
- this.fire("elechart_axis");
597
-
598
- if (this.options.legend) this.fire("elechart_legend");
599
-
600
584
  this.fire("elechart_init");
601
585
 
602
586
  map
@@ -661,23 +645,19 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
661
645
  .disableScrollPropagation(container);
662
646
  }
663
647
 
664
- if (!this.options.detached) {
665
- let link = this._button = _.create('a', "elevation-toggle elevation-toggle-icon" + (this.options.autohide ? "" : " close-button"), { href: '#', title: L._('Elevation') }, container);
666
-
667
- if (this.options.collapsed) {
668
- this._collapse();
669
- if (this.options.autohide) {
670
- _.on(container, 'mouseover', this._expand, this);
671
- _.on(container, 'mouseout', this._collapse, this);
672
- } else {
673
- _.on(link, 'click', L.DomEvent.stop);
674
- _.on(link, 'click', this._toggle, this);
675
- }
648
+ this.options.collapsed ? this._collapse() : this._expand();
676
649
 
677
- _.on(link, 'focus', this._toggle, this);
650
+ if (this.options.autohide) {
651
+ _.on(container, 'mouseover', this._expand, this);
652
+ _.on(container, 'mouseout', this._collapse, this);
653
+ this._map.on('click', this._collapse, this);
654
+ }
678
655
 
679
- this._map.on('click', this._collapse, this);
680
- }
656
+ if (this.options.closeBtn) {
657
+ let link = this._button = _.create('a', "elevation-toggle-icon", { href: '#', title: L._('Elevation'), }, container);
658
+ _.on(link, 'click', L.DomEvent.stop);
659
+ _.on(link, 'click', this._toggle, this);
660
+ _.on(link, 'focus', this._toggle, this);
681
661
  }
682
662
  },
683
663
 
@@ -1205,7 +1185,6 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
1205
1185
  _updateChart: function() {
1206
1186
  if (this._chart && this._container) {
1207
1187
  this.fire("elechart_axis");
1208
- this.fire("elechart_area");
1209
1188
 
1210
1189
  this._chart.update({ data: this._data, options: this.options });
1211
1190
 
@@ -1253,27 +1232,26 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
1253
1232
  * Update chart summary.
1254
1233
  */
1255
1234
  _updateSummary: function() {
1256
- if (!this._summary) return;
1257
-
1258
- this._summary.reset();
1259
-
1260
- if (this.options.summary) {
1261
- this.fire("elechart_summary");
1262
- this._summary.update();
1235
+ if (this._summary) {
1236
+ this._summary.reset();
1237
+ if (this.options.summary) {
1238
+ this.fire("elechart_summary");
1239
+ this._summary.update();
1240
+ }
1241
+ if (this.options.downloadLink && this._downloadURL) { // TODO: generate dynamically file content instead of using static file urls.
1242
+ this._summary._container.innerHTML += '<span class="download"><a href="#">' + L._('Download') + '</a></span>'
1243
+ _.select('.download a', this._summary._container).onclick = (e) => {
1244
+ e.preventDefault();
1245
+ let event = { downloadLink: this.options.downloadLink, confirm: _.saveFile.bind(this, this._downloadURL) };
1246
+ if (this.options.downloadLink == 'modal' && typeof CustomEvent === "function") {
1247
+ document.dispatchEvent(new CustomEvent("eletrack_download", { detail: event }));
1248
+ } else if (this.options.downloadLink == 'link' || this.options.downloadLink === true) {
1249
+ event.confirm();
1250
+ }
1251
+ this.fire('eletrack_download', event);
1252
+ };
1253
+ }
1263
1254
  }
1264
- if (this.options.downloadLink && this._downloadURL) { // TODO: generate dynamically file content instead of using static file urls.
1265
- this._summary._container.innerHTML += '<span class="download"><a href="#">' + L._('Download') + '</a></span>'
1266
- _.select('.download a', this._summary._container).onclick = ({preventDefault}) => {
1267
- preventDefault();
1268
- let event = { downloadLink: this.options.downloadLink, confirm: _.saveFile.bind(this, this._downloadURL) };
1269
- if (this.options.downloadLink == 'modal' && typeof CustomEvent === "function") {
1270
- document.dispatchEvent(new CustomEvent("eletrack_download", { detail: event }));
1271
- } else if (this.options.downloadLink == 'link' || this.options.downloadLink === true) {
1272
- event.confirm();
1273
- }
1274
- this.fire('eletrack_download', event);
1275
- };
1276
- };
1277
1255
  },
1278
1256
 
1279
1257
 
@@ -5,7 +5,7 @@ export function Altitude() {
5
5
  let opts = this.options;
6
6
  let altitude = {};
7
7
 
8
- let theme = opts.theme.replace('-theme', '');
8
+ let theme = opts.theme.split(' ')[0].replace('-theme', '');
9
9
  let color = _.Colors[theme] || {};
10
10
 
11
11
  opts.altitudeFactor = opts.imperial ? this.__footFactor : (opts.altitudeFactor || 1); // 1 m = (1 m)
@@ -13,8 +13,8 @@ export function Heart() {
13
13
  axis : "y",
14
14
  position : "left",
15
15
  scale : { min: -1, max: +1 },
16
- tickPadding: 16,
17
- labelX : -18,
16
+ tickPadding: 25,
17
+ labelX : -30,
18
18
  labelY : -8,
19
19
  },
20
20
  path: {