diff --git a/.gitignore b/.gitignore index 0f0d92c..57c0bdc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ bower_components/leaflet-routing/libs/ +bower_components/Leaflet.Elevation/* +!bower_components/Leaflet.Elevation/dist/ +!bower_components/Leaflet.Elevation/src/ nbproject/ diff --git a/bower.json b/bower.json index f795ba0..eb2ad1b 100644 --- a/bower.json +++ b/bower.json @@ -11,6 +11,7 @@ "leaflet-gpx": "mpetazzoni/leaflet-gpx", "leaflet-search": "*", "leaflet-plugins": "*", - "leaflet-routing": "nrenner/leaflet-routing#reroute" + "leaflet-routing": "nrenner/leaflet-routing#reroute", + "Leaflet.Elevation": "MrMufflon/Leaflet.Elevation#master" } } diff --git a/bower_components/Leaflet.Elevation/dist/Leaflet.Elevation-0.0.1.css b/bower_components/Leaflet.Elevation/dist/Leaflet.Elevation-0.0.1.css new file mode 100644 index 0000000..07123a8 --- /dev/null +++ b/bower_components/Leaflet.Elevation/dist/Leaflet.Elevation-0.0.1.css @@ -0,0 +1 @@ +.lime-theme .leaflet-control.elevation .background{background-color:rgba(156,194,34,.2);-webkit-border-radius:5px;-moz-border-radius:5px;-ms-border-radius:5px;-o-border-radius:5px;border-radius:5px}.lime-theme .leaflet-control.elevation .axis path,.lime-theme .leaflet-control.elevation .axis line{fill:none;stroke:#566b13;stroke-width:2}.lime-theme .leaflet-control.elevation .area{fill:#9cc222}.lime-theme .leaflet-control.elevation .mouse-focus-line{pointer-events:none;stroke-width:1;stroke:#101404}.lime-theme .leaflet-control.elevation .elevation-toggle{cursor:pointer;box-shadow:0 1px 7px rgba(0,0,0,.4);-webkit-border-radius:5px;border-radius:5px;width:36px;height:36px;background:url(images/elevation.png) no-repeat center center #f8f8f9}.lime-theme .leaflet-control.elevation-collapsed .background{display:none}.lime-theme .leaflet-control.elevation-collapsed .elevation-toggle{display:block}.lime-theme .leaflet-overlay-pane .height-focus{stroke:#9cc222;fill:#9cc222}.lime-theme .leaflet-overlay-pane .height-focus.line{pointer-events:none;stroke-width:2}.steelblue-theme .leaflet-control.elevation .background{background-color:rgba(70,130,180,.2);-webkit-border-radius:5px;-moz-border-radius:5px;-ms-border-radius:5px;-o-border-radius:5px;border-radius:5px}.steelblue-theme .leaflet-control.elevation .axis path,.steelblue-theme .leaflet-control.elevation .axis line{fill:none;stroke:#0d1821;stroke-width:2}.steelblue-theme .leaflet-control.elevation .area{fill:#4682b4}.steelblue-theme .leaflet-control.elevation .mouse-focus-line{pointer-events:none;stroke-width:1;stroke:#0d1821}.steelblue-theme .leaflet-control.elevation .elevation-toggle{cursor:pointer;box-shadow:0 1px 7px rgba(0,0,0,.4);-webkit-border-radius:5px;border-radius:5px;width:36px;height:36px;background:url(images/elevation.png) no-repeat center center #f8f8f9}.steelblue-theme .leaflet-control.elevation-collapsed .background{display:none}.steelblue-theme .leaflet-control.elevation-collapsed .elevation-toggle{display:block}.steelblue-theme .leaflet-overlay-pane .height-focus{stroke:#4682b4;fill:#4682b4}.steelblue-theme .leaflet-overlay-pane .height-focus.line{pointer-events:none;stroke-width:2}.purple-theme .leaflet-control.elevation .background{background-color:rgba(115,44,123,.2);-webkit-border-radius:5px;-moz-border-radius:5px;-ms-border-radius:5px;-o-border-radius:5px;border-radius:5px}.purple-theme .leaflet-control.elevation .axis path,.purple-theme .leaflet-control.elevation .axis line{fill:none;stroke:#2d1130;stroke-width:2}.purple-theme .leaflet-control.elevation .area{fill:#732c7b}.purple-theme .leaflet-control.elevation .mouse-focus-line{pointer-events:none;stroke-width:1;stroke:#000}.purple-theme .leaflet-control.elevation .elevation-toggle{cursor:pointer;box-shadow:0 1px 7px rgba(0,0,0,.4);-webkit-border-radius:5px;border-radius:5px;width:36px;height:36px;background:url(images/elevation.png) no-repeat center center #f8f8f9}.purple-theme .leaflet-control.elevation-collapsed .background{display:none}.purple-theme .leaflet-control.elevation-collapsed .elevation-toggle{display:block}.purple-theme .leaflet-overlay-pane .height-focus{stroke:#732c7b;fill:#732c7b}.purple-theme .leaflet-overlay-pane .height-focus.line{pointer-events:none;stroke-width:2} \ No newline at end of file diff --git a/bower_components/Leaflet.Elevation/dist/Leaflet.Elevation-0.0.1.min.js b/bower_components/Leaflet.Elevation/dist/Leaflet.Elevation-0.0.1.min.js new file mode 100644 index 0000000..fbaa0ad --- /dev/null +++ b/bower_components/Leaflet.Elevation/dist/Leaflet.Elevation-0.0.1.min.js @@ -0,0 +1,2 @@ +/*! Leaflet.Elevation 25-10-2013 */ +L.Control.Elevation=L.Control.extend({options:{position:"topright",theme:"lime-theme",width:600,height:175,margins:{top:10,right:20,bottom:30,left:50},useHeightIndicator:!0,interpolation:"linear",hoverNumber:{decimalsX:3,decimalsY:0,formatter:void 0},xTicks:void 0,yTicks:void 0,collapsed:!1},onRemove:function(){this._container=null,this._data=null,this._dist=null},onAdd:function(a){this._map=a;var b=this.options,c=b.margins;b.width=b.width-c.left-c.right,b.height=b.height-c.top-c.bottom,b.xTicks=b.xTicks||Math.round(b.width/75),b.yTicks=b.yTicks||Math.round(b.height/30),b.hoverNumber.formatter=b.hoverNumber.formatter||this._formatter,d3.select("body").classed(b.theme,!0);var d=this._x=d3.scale.linear().range([0,b.width]),e=this._y=d3.scale.linear().range([b.height,0]);this._area=d3.svg.area().interpolate(b.interpolation).x(function(a){return d(a.dist)}).y0(b.height).y1(function(a){return e(a.altitude)});var f=this._container=L.DomUtil.create("div","elevation");this._initToggle();var g=b.width+c.left+c.right,h=d3.select(f);h.attr("width",g);var i=h.append("svg");i.attr("width",g).attr("class","background").attr("height",b.height+c.top+c.bottom).append("g").attr("transform","translate("+c.left+","+c.top+")");var j=d3.svg.line();j=j.x(function(){return d3.mouse(i.select("g"))[0]}).y(function(){return b.height});var k=d3.select(this._container).select("svg").select("g");this._areapath=k.append("path").attr("class","area");var l=this._background=k.append("rect").attr("width",b.width).attr("height",b.height).style("fill","none").style("stroke","none").style("pointer-events","all");l.on("mousemove",this._mousemoveHandler.bind(this)),l.on("mouseout",this._mouseoutHandler.bind(this)),this._xaxisgraphicnode=k.append("g"),this._yaxisgraphicnode=k.append("g"),this._appendXaxis(this._xaxisgraphicnode),this._appendYaxis(this._yaxisgraphicnode);var m=this._focusG=k.append("g");return this._mousefocus=m.append("svg:line").attr("class","mouse-focus-line").attr("x2","0").attr("y2","0").attr("x1","0").attr("y1","0"),this._focuslabelX=m.append("svg:text").style("pointer-events","none").attr("class","mouse-focus-label-x"),this._focuslabelY=m.append("svg:text").style("pointer-events","none").attr("class","mouse-focus-label-y"),f},_initToggle:function(){var a=this._container;if(a.setAttribute("aria-haspopup",!0),L.Browser.touch?L.DomEvent.on(a,"click",L.DomEvent.stopPropagation):L.DomEvent.disableClickPropagation(a),this.options.collapsed){this._collapse(),L.Browser.android||L.DomEvent.on(a,"mouseover",this._expand,this).on(a,"mouseout",this._collapse,this);var b=this._button=L.DomUtil.create("a","elevation-toggle",a);b.href="#",b.title="Elevation",L.Browser.touch?L.DomEvent.on(b,"click",L.DomEvent.stop).on(b,"click",this._expand,this):L.DomEvent.on(b,"focus",this._expand,this),this._map.on("click",this._collapse,this)}},_expand:function(){this._container.className=this._container.className.replace(" elevation-collapsed","")},_collapse:function(){L.DomUtil.addClass(this._container,"elevation-collapsed")},_formatter:function(a,b,c){var d;d=0===b?Math.round(a)+"":L.Util.formatNum(a,b)+"";var e=d.split(".");if(e[1]){for(var f=b-e[1].length;f>0;f--)e[1]+="0";d=e.join(c||".")}return d},_appendYaxis:function(a){a.attr("class","y axis").call(d3.svg.axis().scale(this._y).ticks(this.options.yTicks).orient("left")).append("text").attr("x",15).style("text-anchor","end").text("m")},_appendXaxis:function(a){a.attr("class","x axis").attr("transform","translate(0,"+this.options.height+")").call(d3.svg.axis().scale(this._x).ticks(this.options.xTicks).orient("bottom")).append("text").attr("x",this.options.width+15).style("text-anchor","end").text("km")},_updateAxis:function(){this._xaxisgraphicnode.selectAll("axis").remove(),this._yaxisgraphicnode.selectAll("axis").remove(),this._appendXaxis(this._xaxisgraphicnode),this._appendYaxis(this._yaxisgraphicnode)},_mouseoutHandler:function(){this._marker&&(this._map.removeLayer(this._marker),this._marker=null),this._mouseHeightFocus&&(this._mouseHeightFocus.style("visibility","hidden"),this._mouseHeightFocusLabel.style("visibility","hidden")),this._pointG&&this._pointG.style("visibility","hidden"),this._focusG.style("visibility","hidden")},_mousemoveHandler:function(){if(this._data&&0!==this._data.length){var a=d3.mouse(this._background.node()),b=this.options;this._focusG.style("visibility","visible"),this._mousefocus.attr("x1",a[0]).attr("y1",0).attr("x2",a[0]).attr("y2",b.height).classed("hidden",!1);var c=d3.bisector(function(a){return a.dist}).left,d=this._x.invert(a[0]),e=c(this._data,d),f=this._data[e].altitude,g=this._data[e].dist,h=this._data[e].latlng,i=b.hoverNumber.formatter(f,b.hoverNumber.decimalsY),j=b.hoverNumber.formatter(g,b.hoverNumber.decimalsX);this._focuslabelX.attr("x",a[0]).text(i+" m"),this._focuslabelY.attr("y",b.height-5).attr("x",a[0]).text(j+" km");var k=this._map.latLngToLayerPoint(h);if(b.useHeightIndicator){if(!this._mouseHeightFocus){var l=d3.select(".leaflet-overlay-pane svg").append("g");this._mouseHeightFocus=l.append("svg:line").attr("class","height-focus line").attr("x2","0").attr("y2","0").attr("x1","0").attr("y1","0");var m=this._pointG=l.append("g");m.append("svg:circle").attr("r",6).attr("cx",0).attr("cy",0).attr("class","height-focus circle-lower"),this._mouseHeightFocusLabel=l.append("svg:text").attr("class","height-focus-label").style("pointer-events","none")}var n=this.options.height/this._maxElevation*f,o=k.y-n;this._mouseHeightFocus.attr("x1",k.x).attr("x2",k.x).attr("y1",k.y).attr("y2",o).style("visibility","visible"),this._pointG.attr("transform","translate("+k.x+","+k.y+")").style("visibility","visible"),this._mouseHeightFocusLabel.attr("x",k.x).attr("y",o).text(f+" m").style("visibility","visible")}else this._marker?this._marker.setLatLng(h):this._marker=new L.Marker(h).addTo(this._map)}},_addGeoJSONData:function(a){if(a){for(var b=this._data||[],c=this._dist||0,d=this._maxElevation||0,e=0;e 0; d--) { + numbers[1] += "0"; + } + res = numbers.join(sep || "."); + } + return res; + }, + + _appendYaxis: function(y) { + y.attr("class", "y axis") + .call(d3.svg.axis() + .scale(this._y) + .ticks(this.options.yTicks) + .orient("left")) + .append("text") + .attr("x", 15) + .style("text-anchor", "end") + .text("m"); + }, + + _appendXaxis: function(x) { + x.attr("class", "x axis") + .attr("transform", "translate(0," + this.options.height + ")") + .call(d3.svg.axis() + .scale(this._x) + .ticks(this.options.xTicks) + .orient("bottom")) + .append("text") + .attr("x", this.options.width + 15) + .style("text-anchor", "end") + .text("km"); + }, + + _updateAxis: function() { + this._xaxisgraphicnode.selectAll("axis").remove(); + this._yaxisgraphicnode.selectAll("axis").remove(); + this._appendXaxis(this._xaxisgraphicnode); + this._appendYaxis(this._yaxisgraphicnode); + }, + + _mouseoutHandler: function() { + if (this._marker) { + this._map.removeLayer(this._marker); + this._marker = null; + } + if (this._mouseHeightFocus) { + this._mouseHeightFocus.style("visibility", "hidden"); + this._mouseHeightFocusLabel.style("visibility", "hidden"); + } + if (this._pointG) { + this._pointG.style("visibility", "hidden"); + } + this._focusG.style("visibility", "hidden"); + }, + + _mousemoveHandler: function(d, i, ctx) { + if (!this._data || this._data.length === 0) { + return; + } + var coords = d3.mouse(this._background.node()); + var opts = this.options; + this._focusG.style("visibility", "visible"); + this._mousefocus.attr('x1', coords[0]) + .attr('y1', 0) + .attr('x2', coords[0]) + .attr('y2', opts.height) + .classed('hidden', false); + var bisect = d3.bisector(function(d) { + return d.dist; + }).left; + + var xinvert = this._x.invert(coords[0]), + item = bisect(this._data, xinvert), + alt = this._data[item].altitude, + dist = this._data[item].dist, + ll = this._data[item].latlng, + numY = opts.hoverNumber.formatter(alt, opts.hoverNumber.decimalsY), + numX = opts.hoverNumber.formatter(dist, opts.hoverNumber.decimalsX); + + this._focuslabelX.attr("x", coords[0]) + .text(numY + " m"); + this._focuslabelY.attr("y", opts.height - 5) + .attr("x", coords[0]) + .text(numX + " km"); + + var layerpoint = this._map.latLngToLayerPoint(ll); + + //if we use a height indicator we create one with SVG + //otherwise we show a marker + if (opts.useHeightIndicator) { + + if (!this._mouseHeightFocus) { + + var heightG = d3.select(".leaflet-overlay-pane svg") + .append("g"); + this._mouseHeightFocus = heightG.append('svg:line') + .attr('class', 'height-focus line') + .attr('x2', '0') + .attr('y2', '0') + .attr('x1', '0') + .attr('y1', '0'); + + var pointG = this._pointG = heightG.append("g"); + pointG.append("svg:circle") + .attr("r", 6) + .attr("cx", 0) + .attr("cy", 0) + .attr("class", "height-focus circle-lower"); + + this._mouseHeightFocusLabel = heightG.append("svg:text") + .attr("class", "height-focus-label") + .style("pointer-events", "none"); + + } + + var normalizedAlt = this.options.height / this._maxElevation * alt; + var normalizedY = layerpoint.y - normalizedAlt; + this._mouseHeightFocus.attr("x1", layerpoint.x) + .attr("x2", layerpoint.x) + .attr("y1", layerpoint.y) + .attr("y2", normalizedY) + .style("visibility", "visible"); + + this._pointG.attr("transform", "translate(" + layerpoint.x + "," + layerpoint.y + ")") + .style("visibility", "visible"); + + this._mouseHeightFocusLabel.attr("x", layerpoint.x) + .attr("y", normalizedY) + .text(alt + " m") + .style("visibility", "visible"); + + } else { + + if (!this._marker) { + + this._marker = new L.Marker(ll).addTo(this._map); + + } else { + + this._marker.setLatLng(ll); + + } + + } + + }, + + /* + * Parsing of GeoJSON data lines and their elevation in z-coordinate + */ + _addGeoJSONData: function(coords) { + if (coords) { + var data = this._data || []; + var dist = this._dist || 0; + var ele = this._maxElevation || 0; + for (var i = 0; i < coords.length; i++) { + var s = new L.LatLng(coords[i][1], coords[i][0]); + var e = new L.LatLng(coords[i ? i - 1 : 0][1], coords[i ? i - 1 : 0][0]); + var newdist = s.distanceTo(e); + dist = dist + newdist / 1000; + ele = ele < coords[i][2] ? coords[i][2] : ele; + data.push({ + dist: dist, + altitude: coords[i][2], + x: coords[i][0], + y: coords[i][1], + latlng: s + }); + } + this._dist = dist; + this._data = data; + this._maxElevation = ele; + } + }, + + /* + * Parsing function for GPX data as used by https://github.com/mpetazzoni/leaflet-gpx + */ + _addGPXdata: function(coords) { + if (coords) { + var data = this._data || []; + var dist = this._dist || 0; + var ele = this._maxElevation || 0; + for (var i = 0; i < coords.length; i++) { + var s = coords[i]; + var e = coords[i ? i - 1 : 0]; + var newdist = s.distanceTo(e); + dist = dist + newdist / 1000; + ele = ele < s.meta.ele ? s.meta.ele : ele; + data.push({ + dist: dist, + altitude: s.meta.ele, + x: s.lng, + y: s.lat, + latlng: s + }); + } + this._dist = dist; + this._data = data; + this._maxElevation = ele; + } + }, + + _addData: function(d) { + var geom = d && d.geometry && d.geometry; + var i; + + if (geom) { + switch (geom.type) { + case 'LineString': + this._addGeoJSONData(geom.coordinates); + break; + + case 'MultiLineString': + for (i = 0; i < geom.coordinates.length; i++) { + this._addGeoJSONData(geom.coordinates[i]); + } + break; + + default: + throw new Error('Invalid GeoJSON object.'); + } + } + + var feat = d && d.type === "FeatureCollection"; + if (feat) { + for (i = 0; i < d.features.length; i++) { + this._addData(d.features[i]); + } + } + + if (d && d._latlngs) { + this._addGPXdata(d._latlngs); + } + }, + + /* + * Add data to the diagram either from GPX or GeoJSON and + * update the axis domain and data + */ + addData: function(d) { + + this._addData(d); + + var xdomain = d3.extent(this._data, function(d) { + return d.dist; + }); + var ydomain = d3.extent(this._data, function(d) { + return d.altitude; + }); + + this._x.domain(xdomain); + this._y.domain(ydomain); + this._areapath.datum(this._data) + .attr("d", this._area); + this._updateAxis(); + return; + } + +}); + +L.control.elevation = function(options) { + return new L.Control.Elevation(options); +}; \ No newline at end of file diff --git a/bower_components/Leaflet.Elevation/dist/images/elevation.png b/bower_components/Leaflet.Elevation/dist/images/elevation.png new file mode 100644 index 0000000..68fb537 Binary files /dev/null and b/bower_components/Leaflet.Elevation/dist/images/elevation.png differ diff --git a/bower_components/Leaflet.Elevation/src/L.Control.Elevation.js b/bower_components/Leaflet.Elevation/src/L.Control.Elevation.js new file mode 100644 index 0000000..c58edd0 --- /dev/null +++ b/bower_components/Leaflet.Elevation/src/L.Control.Elevation.js @@ -0,0 +1,479 @@ +L.Control.Elevation = L.Control.extend({ + options: { + position: "topright", + theme: "lime-theme", + width: 600, + height: 175, + margins: { + top: 10, + right: 20, + bottom: 30, + left: 50 + }, + useHeightIndicator: true, + interpolation: "linear", + hoverNumber: { + decimalsX: 3, + decimalsY: 0, + formatter: undefined + }, + xTicks: undefined, + yTicks: undefined, + collapsed: false + }, + + onRemove: function(map) { + this._container = null; + this._data = null; + this._dist = null; + }, + + onAdd: function(map) { + this._map = map; + + var opts = this.options; + var margin = opts.margins; + opts.width = opts.width - margin.left - margin.right; + opts.height = opts.height - margin.top - margin.bottom; + opts.xTicks = opts.xTicks || Math.round(opts.width / 75); + opts.yTicks = opts.yTicks || Math.round(opts.height / 30); + opts.hoverNumber.formatter = opts.hoverNumber.formatter || this._formatter; + + //append theme name on body + d3.select("body").classed(opts.theme, true); + + var x = this._x = d3.scale.linear() + .range([0, opts.width]); + + var y = this._y = d3.scale.linear() + .range([opts.height, 0]); + + var area = this._area = d3.svg.area() + .interpolate(opts.interpolation) + .x(function(d) { + return x(d.dist); + }) + .y0(opts.height) + .y1(function(d) { + return y(d.altitude); + }); + + var container = this._container = L.DomUtil.create("div", "elevation"); + + this._initToggle(); + + var complWidth = opts.width + margin.left + margin.right; + var cont = d3.select(container); + cont.attr("width", complWidth); + var svg = cont.append("svg"); + svg.attr("width", complWidth) + .attr("class", "background") + .attr("height", opts.height + margin.top + margin.bottom) + .append("g") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); + + var line = d3.svg.line(); + line = line + .x(function(d) { + return d3.mouse(svg.select("g"))[0]; + }) + .y(function(d) { + return opts.height; + }); + + var g = d3.select(this._container).select("svg").select("g"); + + this._areapath = g.append("path") + .attr("class", "area"); + + var background = this._background = g.append("rect") + .attr("width", opts.width) + .attr("height", opts.height) + .style("fill", "none") + .style("stroke", "none") + .style("pointer-events", "all"); + + background.on("mousemove", this._mousemoveHandler.bind(this)); + background.on("mouseout", this._mouseoutHandler.bind(this)); + + this._xaxisgraphicnode = g.append("g"); + this._yaxisgraphicnode = g.append("g"); + this._appendXaxis(this._xaxisgraphicnode); + this._appendYaxis(this._yaxisgraphicnode); + + var focusG = this._focusG = g.append("g"); + this._mousefocus = focusG.append('svg:line') + .attr('class', 'mouse-focus-line') + .attr('x2', '0') + .attr('y2', '0') + .attr('x1', '0') + .attr('y1', '0'); + this._focuslabelX = focusG.append("svg:text") + .style("pointer-events", "none") + .attr("class", "mouse-focus-label-x"); + this._focuslabelY = focusG.append("svg:text") + .style("pointer-events", "none") + .attr("class", "mouse-focus-label-y"); + + return container; + }, + + _initToggle: function () { + + /* inspired by L.Control.Layers */ + + var container = this._container; + + //Makes this work on IE10 Touch devices by stopping it from firing a mouseout event when the touch is released + container.setAttribute('aria-haspopup', true); + + if (!L.Browser.touch) { + L.DomEvent + .disableClickPropagation(container); + //.disableScrollPropagation(container); + } else { + L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation); + } + + if (this.options.collapsed) + { + this._collapse(); + + if (!L.Browser.android) { + L.DomEvent + .on(container, 'mouseover', this._expand, this) + .on(container, 'mouseout', this._collapse, this); + } + var link = this._button = L.DomUtil.create('a', 'elevation-toggle', container); + link.href = '#'; + link.title = 'Elevation'; + + if (L.Browser.touch) { + L.DomEvent + .on(link, 'click', L.DomEvent.stop) + .on(link, 'click', this._expand, this); + } + else { + L.DomEvent.on(link, 'focus', this._expand, this); + } + + this._map.on('click', this._collapse, this); + // TODO keyboard accessibility + } + }, + + _expand: function () { + this._container.className = this._container.className.replace(' elevation-collapsed', ''); + }, + + _collapse: function () { + L.DomUtil.addClass(this._container, 'elevation-collapsed'); + }, + + /* + * Fromatting funciton using the given decimals and seperator + */ + _formatter: function(num, dec, sep) { + var res; + if (dec === 0) { + res = Math.round(num) + ""; + } else { + res = L.Util.formatNum(num, dec) + ""; + } + var numbers = res.split("."); + if (numbers[1]) { + var d = dec - numbers[1].length; + for (; d > 0; d--) { + numbers[1] += "0"; + } + res = numbers.join(sep || "."); + } + return res; + }, + + _appendYaxis: function(y) { + y.attr("class", "y axis") + .call(d3.svg.axis() + .scale(this._y) + .ticks(this.options.yTicks) + .orient("left")) + .append("text") + .attr("x", 15) + .style("text-anchor", "end") + .text("m"); + }, + + _appendXaxis: function(x) { + x.attr("class", "x axis") + .attr("transform", "translate(0," + this.options.height + ")") + .call(d3.svg.axis() + .scale(this._x) + .ticks(this.options.xTicks) + .orient("bottom")) + .append("text") + .attr("x", this.options.width + 15) + .style("text-anchor", "end") + .text("km"); + }, + + _updateAxis: function() { + this._xaxisgraphicnode.selectAll("g").remove(); + this._xaxisgraphicnode.selectAll("path").remove(); + this._xaxisgraphicnode.selectAll("text").remove(); + this._yaxisgraphicnode.selectAll("g").remove(); + this._yaxisgraphicnode.selectAll("path").remove(); + this._yaxisgraphicnode.selectAll("text").remove(); + this._appendXaxis(this._xaxisgraphicnode); + this._appendYaxis(this._yaxisgraphicnode); + }, + + _mouseoutHandler: function() { + if (this._marker) { + this._map.removeLayer(this._marker); + this._marker = null; + } + if (this._mouseHeightFocus) { + this._mouseHeightFocus.style("visibility", "hidden"); + this._mouseHeightFocusLabel.style("visibility", "hidden"); + } + if (this._pointG) { + this._pointG.style("visibility", "hidden"); + } + this._focusG.style("visibility", "hidden"); + }, + + _mousemoveHandler: function(d, i, ctx) { + if (!this._data || this._data.length === 0) { + return; + } + var coords = d3.mouse(this._background.node()); + var opts = this.options; + this._focusG.style("visibility", "visible"); + this._mousefocus.attr('x1', coords[0]) + .attr('y1', 0) + .attr('x2', coords[0]) + .attr('y2', opts.height) + .classed('hidden', false); + var bisect = d3.bisector(function(d) { + return d.dist; + }).left; + + var xinvert = this._x.invert(coords[0]), + item = bisect(this._data, xinvert), + alt = this._data[item].altitude, + dist = this._data[item].dist, + ll = this._data[item].latlng, + numY = opts.hoverNumber.formatter(alt, opts.hoverNumber.decimalsY), + numX = opts.hoverNumber.formatter(dist, opts.hoverNumber.decimalsX); + + this._focuslabelX.attr("x", coords[0]) + .text(numY + " m"); + this._focuslabelY.attr("y", opts.height - 5) + .attr("x", coords[0]) + .text(numX + " km"); + + var layerpoint = this._map.latLngToLayerPoint(ll); + + //if we use a height indicator we create one with SVG + //otherwise we show a marker + if (opts.useHeightIndicator) { + + if (!this._mouseHeightFocus) { + + var heightG = d3.select(".leaflet-overlay-pane svg") + .append("g"); + this._mouseHeightFocus = heightG.append('svg:line') + .attr('class', 'height-focus line') + .attr('x2', '0') + .attr('y2', '0') + .attr('x1', '0') + .attr('y1', '0'); + + var pointG = this._pointG = heightG.append("g"); + pointG.append("svg:circle") + .attr("r", 6) + .attr("cx", 0) + .attr("cy", 0) + .attr("class", "height-focus circle-lower"); + + this._mouseHeightFocusLabel = heightG.append("svg:text") + .attr("class", "height-focus-label") + .style("pointer-events", "none"); + + } + + var normalizedAlt = this.options.height / this._maxElevation * alt; + var normalizedY = layerpoint.y - normalizedAlt; + this._mouseHeightFocus.attr("x1", layerpoint.x) + .attr("x2", layerpoint.x) + .attr("y1", layerpoint.y) + .attr("y2", normalizedY) + .style("visibility", "visible"); + + this._pointG.attr("transform", "translate(" + layerpoint.x + "," + layerpoint.y + ")") + .style("visibility", "visible"); + + this._mouseHeightFocusLabel.attr("x", layerpoint.x) + .attr("y", normalizedY) + .text(alt + " m") + .style("visibility", "visible"); + + } else { + + if (!this._marker) { + + this._marker = new L.Marker(ll).addTo(this._map); + + } else { + + this._marker.setLatLng(ll); + + } + + } + + }, + + /* + * Parsing of GeoJSON data lines and their elevation in z-coordinate + */ + _addGeoJSONData: function(coords) { + if (coords) { + var data = this._data || []; + var dist = this._dist || 0; + var ele = this._maxElevation || 0; + for (var i = 0; i < coords.length; i++) { + var s = new L.LatLng(coords[i][1], coords[i][0]); + var e = new L.LatLng(coords[i ? i - 1 : 0][1], coords[i ? i - 1 : 0][0]); + var newdist = s.distanceTo(e); + dist = dist + newdist / 1000; + ele = ele < coords[i][2] ? coords[i][2] : ele; + data.push({ + dist: dist, + altitude: coords[i][2], + x: coords[i][0], + y: coords[i][1], + latlng: s + }); + } + this._dist = dist; + this._data = data; + this._maxElevation = ele; + } + }, + + /* + * Parsing function for GPX data as used by https://github.com/mpetazzoni/leaflet-gpx + */ + _addGPXdata: function(coords) { + if (coords) { + var data = this._data || []; + var dist = this._dist || 0; + var ele = this._maxElevation || 0; + for (var i = 0; i < coords.length; i++) { + var s = coords[i]; + var e = coords[i ? i - 1 : 0]; + var newdist = s.distanceTo(e); + dist = dist + newdist / 1000; + ele = ele < s.meta.ele ? s.meta.ele : ele; + data.push({ + dist: dist, + altitude: s.meta.ele, + x: s.lng, + y: s.lat, + latlng: s + }); + } + this._dist = dist; + this._data = data; + this._maxElevation = ele; + } + }, + + _addData: function(d) { + var geom = d && d.geometry && d.geometry; + var i; + + if (geom) { + switch (geom.type) { + case 'LineString': + this._addGeoJSONData(geom.coordinates); + break; + + case 'MultiLineString': + for (i = 0; i < geom.coordinates.length; i++) { + this._addGeoJSONData(geom.coordinates[i]); + } + break; + + default: + throw new Error('Invalid GeoJSON object.'); + } + } + + var feat = d && d.type === "FeatureCollection"; + if (feat) { + for (i = 0; i < d.features.length; i++) { + this._addData(d.features[i]); + } + } + + if (d && d._latlngs) { + this._addGPXdata(d._latlngs); + } + }, + + /* + * Add data to the diagram either from GPX or GeoJSON and + * update the axis domain and data + */ + addData: function(d) { + + this._addData(d); + + var xdomain = d3.extent(this._data, function(d) { + return d.dist; + }); + var ydomain = d3.extent(this._data, function(d) { + return d.altitude; + }); + + this._x.domain(xdomain); + this._y.domain(ydomain); + this._areapath.datum(this._data) + .attr("d", this._area); + this._updateAxis(); + return; + }, + + /* + * Reset data + */ + _clearData: function() { + this._data = null; + this._dist = null; + this._maxElevation = null; + }, + + /* + * Reset data and display + */ + clear: function() { + + this._clearData(); + + // workaround for 'Error: Problem parsing d=""' in Webkit when empty data + // https://groups.google.com/d/msg/d3-js/7rFxpXKXFhI/HzIO_NPeDuMJ + //this._areapath.datum(this._data).attr("d", this._area); + this._areapath.attr("d", "M0 0"); + + this._x.domain([0,1]); + this._y.domain([0,1]); + this._updateAxis(); + } + +}); + +L.control.elevation = function(options) { + return new L.Control.Elevation(options); +}; \ No newline at end of file diff --git a/bower_components/Leaflet.Elevation/src/css/L.Control.Elevation.less b/bower_components/Leaflet.Elevation/src/css/L.Control.Elevation.less new file mode 100644 index 0000000..f111f51 --- /dev/null +++ b/bower_components/Leaflet.Elevation/src/css/L.Control.Elevation.less @@ -0,0 +1,67 @@ +.@{theme}{ + + .leaflet-control.elevation .background{ + background-color:@background; + .rounded-corners; + } + + .leaflet-control.elevation{ + .axis path, + .axis line { + fill: none; + stroke: @axis-color; + stroke-width:@stroke-width-axis; + } + } + + .leaflet-control.elevation .area { + fill: @base-color; + } + + .leaflet-control.elevation .mouse-focus-line { + pointer-events: none; + stroke-width:@stroke-width-mouse-focus; + stroke:@stroke-color; + } + + .leaflet-control.elevation .elevation-toggle { + cursor: pointer; + box-shadow: 0 1px 7px rgba(0,0,0,0.4); + -webkit-border-radius: 5px; + border-radius: 5px; + width: 36px; + height: 36px; + background: url('images/elevation.png') no-repeat center center #f8f8f9; + } + + .leaflet-control.elevation-collapsed .background { + display: none; + } + .leaflet-control.elevation-collapsed .elevation-toggle { + display: block; + } + + .leaflet-control.elevation{ + .mouse-focus-label-y, + .mouse-focus-label-x { + + } + } + + .leaflet-overlay-pane { + .height-focus{ + stroke:@base-color; + fill:@base-color; + } + .height-focus.line{ + pointer-events: none; + stroke-width:@stroke-width-height-focus; + } + .height-focus-label{ + + } + .height-focus.circle-lower { + + } + } +} diff --git a/bower_components/Leaflet.Elevation/src/css/themes/lime.less b/bower_components/Leaflet.Elevation/src/css/themes/lime.less new file mode 100644 index 0000000..6b7efd8 --- /dev/null +++ b/bower_components/Leaflet.Elevation/src/css/themes/lime.less @@ -0,0 +1,18 @@ +@theme : lime-theme; +@base-color : #9CC222; +@background : fade(@base-color,20%); +@axis-color : darken(@base-color,20%); +@stroke-color : darken(@base-color,40%); +@stroke-width-mouse-focus : 1; +@stroke-width-height-focus: 2; +@stroke-width-axis : 2; + +.rounded-corners (@radius: 5px) { + -webkit-border-radius: @radius; + -moz-border-radius: @radius; + -ms-border-radius: @radius; + -o-border-radius: @radius; + border-radius: @radius; +} + +@import "../L.Control.Elevation.less"; \ No newline at end of file diff --git a/bower_components/Leaflet.Elevation/src/css/themes/purple.less b/bower_components/Leaflet.Elevation/src/css/themes/purple.less new file mode 100644 index 0000000..c96082b --- /dev/null +++ b/bower_components/Leaflet.Elevation/src/css/themes/purple.less @@ -0,0 +1,18 @@ +@theme : purple-theme; +@base-color : #732C7B; +@background : fade(@base-color,20%); +@axis-color : darken(@base-color,20%); +@stroke-color : darken(@base-color,40%); +@stroke-width-mouse-focus : 1; +@stroke-width-height-focus: 2; +@stroke-width-axis : 2; + +.rounded-corners (@radius: 5px) { + -webkit-border-radius: @radius; + -moz-border-radius: @radius; + -ms-border-radius: @radius; + -o-border-radius: @radius; + border-radius: @radius; +} + +@import "../L.Control.Elevation.less"; \ No newline at end of file diff --git a/bower_components/Leaflet.Elevation/src/css/themes/steelblue.less b/bower_components/Leaflet.Elevation/src/css/themes/steelblue.less new file mode 100644 index 0000000..f38ec38 --- /dev/null +++ b/bower_components/Leaflet.Elevation/src/css/themes/steelblue.less @@ -0,0 +1,18 @@ +@theme : steelblue-theme; +@base-color : steelblue; +@background : fade(@base-color,20%); +@axis-color : darken(@base-color,40%); +@stroke-color : darken(@base-color,40%); +@stroke-width-mouse-focus : 1; +@stroke-width-height-focus: 2; +@stroke-width-axis : 2; + +.rounded-corners (@radius: 5px) { + -webkit-border-radius: @radius; + -moz-border-radius: @radius; + -ms-border-radius: @radius; + -o-border-radius: @radius; + border-radius: @radius; +} + +@import "../L.Control.Elevation.less"; \ No newline at end of file diff --git a/bower_components/Leaflet.Elevation/src/images/elevation.png b/bower_components/Leaflet.Elevation/src/images/elevation.png new file mode 100644 index 0000000..e3d9348 Binary files /dev/null and b/bower_components/Leaflet.Elevation/src/images/elevation.png differ diff --git a/bower_components/Leaflet.Elevation/src/images/elevation.svg b/bower_components/Leaflet.Elevation/src/images/elevation.svg new file mode 100644 index 0000000..f141cd6 --- /dev/null +++ b/bower_components/Leaflet.Elevation/src/images/elevation.svg @@ -0,0 +1,93 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/index.html b/index.html index 2113c0b..2f62c83 100644 --- a/index.html +++ b/index.html @@ -9,7 +9,7 @@ - + @@ -79,7 +79,7 @@ - +