123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692 |
-
- L.Control.Elevation = L.Control.extend({
- options: {
- position: "bottomcenter",
- theme: "lime-theme",
- width: 600,
- height: 175,
- margins: {
- top: 10,
- right: 20,
- bottom: 30,
- left: 60
- },
- useHeightIndicator: true,
- interpolation: "linear",
- hoverNumber: {
- decimalsX: 3,
- decimalsY: 0,
- formatter: undefined
- },
- xTicks: undefined,
- yTicks: undefined,
- collapsed: false,
- yAxisMin: undefined,
- yAxisMax: undefined,
- forceAxisBounds: false
- },
- onRemove: function(map) {
- this._container = null;
- },
- onAdd: function(map) {
- this._map = map;
- if(ONEMAP.M.pcLayout.status.showSideBar){
- var curWidth = $(window).width()-410;
- }else{
- var curWidth = $(window).width();
- }
- var opts = this.options;
- var margin = opts.margins;
- opts.width = curWidth;
- opts.xTicks = opts.xTicks || Math.round(this._width() / 75);
- opts.yTicks = opts.yTicks || Math.round(this._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, this._width()]);
- var y = this._y = d3.scale.linear()
- .range([this._height(), 0]);
- var area = this._area = d3.svg.area()
- .interpolate(opts.interpolation)
- .x(function(d) {
- return x(d.dist);
- })
- .y0(this._height())
- .y1(function(d) {
- return y(d.altitude);
- });
- var container = this._container = L.DomUtil.create("div", "elevation leaflet-control-elevation");
- this._initToggle();
- var cont = d3.select(container);
- cont.attr("width", opts.width);
- var svg = cont.append("svg");
- svg.attr("width", opts.width)
- .attr("class", "background")
- .attr("height", opts.height)
- .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 this._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", this._width())
- .attr("height", this._height())
- .style("fill", "none")
- .style("fillOpacity", "0")
- .style("stroke", "none")
- .style("pointer-events", "all");
- //if (L.Browser.touch) {
- // background.on("touchmove.drag", this._dragHandler.bind(this)).
- // on("touchstart.drag", this._dragStartHandler.bind(this)).
- // on("touchstart.focus", this._mousemoveHandler.bind(this));
- // L.DomEvent.on(this._container, 'touchend', this._dragEndHandler, this);
- //} else {
- background.on("mousemove.focus", this._mousemoveHandler.bind(this)).
- on("mouseout.focus", this._mouseoutHandler.bind(this)).
- on("mousedown.drag", this._dragStartHandler.bind(this)).
- on("mousemove.drag", this._dragHandler.bind(this));
- L.DomEvent.on(this._container, 'mouseup', this._dragEndHandler, 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");
- if (this._data) {
- this._applyData();
- }
- return container;
- },
- _dragHandler: function() {
- //we don´t want map events to occur here
- d3.event.preventDefault();
- d3.event.stopPropagation();
- this._gotDragged = true;
- this._drawDragRectangle();
- },
- /*
- * Draws the currently dragged rectabgle over the chart.
- */
- _drawDragRectangle: function() {
- if (!this._dragStartCoords) {
- return;
- }
- var dragEndCoords = this._dragCurrentCoords = d3.mouse(this._background.node());
- var x1 = Math.min(this._dragStartCoords[0], dragEndCoords[0]),
- x2 = Math.max(this._dragStartCoords[0], dragEndCoords[0]);
- if (!this._dragRectangle && !this._dragRectangleG) {
- var g = d3.select(this._container).select("svg").select("g");
- this._dragRectangleG = g.append("g");
- this._dragRectangle = this._dragRectangleG.append("rect")
- .attr("width", x2 - x1)
- .attr("height", this._height())
- .attr("x", x1)
- .attr('class', 'mouse-drag')
- .style("pointer-events", "none")
- .style("opacity", "0.4");
- } else {
- this._dragRectangle.attr("width", x2 - x1)
- .attr("x", x1);
- }
- },
- /*
- * Removes the drag rectangle and zoms back to the total extent of the data.
- */
- _resetDrag: function() {
- if (this._dragRectangleG) {
- this._dragRectangleG.remove();
- this._dragRectangleG = null;
- this._dragRectangle = null;
- this._hidePositionMarker();
- this._map.fitBounds(this._fullExtent);
- }
- },
- /*
- * Handles end of dragg operations. Zooms the map to the selected items extent.
- */
- _dragEndHandler: function() {
- if (!this._dragStartCoords || !this._gotDragged) {
- this._dragStartCoords = null;
- this._gotDragged = false;
- this._resetDrag();
- return;
- }
- this._hidePositionMarker();
- var item1 = this._findItemForX(this._dragStartCoords[0]),
- item2 = this._findItemForX(this._dragCurrentCoords[0]);
- this._fitSection(item1, item2);
- this._dragStartCoords = null;
- this._gotDragged = false;
- },
- _dragStartHandler: function() {
- d3.event.preventDefault();
- d3.event.stopPropagation();
- this._gotDragged = false;
- this._dragStartCoords = d3.mouse(this._background.node());
- },
- /*
- * Finds a data entry for a given x-coordinate of the diagram
- */
- _findItemForX: function(x) {
- var bisect = d3.bisector(function(d) {
- return d.dist;
- }).left;
- var xinvert = this._x.invert(x);
- return bisect(this._data, xinvert);
- },
- /** Make the map fit the route section between given indexes. */
- _fitSection: function(index1, index2) {
- var start = Math.min(index1, index2),
- end = Math.max(index1, index2);
- var ext = this._calculateFullExtent(this._data.slice(start, end));
- this._map.fitBounds(ext);
- },
- _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');
- },
- _width: function() {
- var opts = this.options;
- return opts.width - opts.margins.left - opts.margins.right;
- },
- _height: function() {
- var opts = this.options;
- return opts.height - opts.margins.top - opts.margins.bottom;
- },
- /*
- * 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", -45)
- .attr("y", 3)
- .style("text-anchor", "end")
- .text("米");
- },
- _appendXaxis: function(x) {
- x.attr("class", "x axis")
- .attr("transform", "translate(0," + this._height() + ")")
- .call(d3.svg.axis()
- .scale(this._x)
- .ticks(this.options.xTicks)
- .orient("bottom"))
- .append("text")
- .attr("x", this._width() + 20)
- .attr("y", 15)
- .style("text-anchor", "end")
- .text("公里");
- },
- _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() {
- this._hidePositionMarker();
- },
- /*
- * Hides the position-/heigth indication marker drawn onto the map
- */
- _hidePositionMarker: 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");
- },
- /*
- * Handles the moueseover the chart and displays distance and altitude level
- */
- _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', this._height())
- .classed('hidden', false);
- var bisect = d3.bisector(function(d) {
- return d.dist;
- }).left;
- var item = this._data[this._findItemForX(coords[0])],
- alt = item.altitude,
- dist = item.dist,
- ll = 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 + " 米");
- this._focuslabelY.attr("y", this._height() - 5)
- .attr("x", coords[0])
- .text(numX + " 公里");
- 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._height() / this._maxElevation * alt;
- var normalizedAlt = 50;
- var normalizedY = layerpoint.y - normalizedAlt;
- //var normalizedY = layerpoint.y;
- 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(numY + " 米")
- .style("visibility", "visible");
- } else {
- /*if (!this._marker) {
- this._marker = new L.Marker(ll).addTo(this._map);
- } else {
- this._marker.setLatLng(ll);
- }*/
- }
- 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 + Math.round(newdist / 1000 * 100000) / 100000;
- 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 + Math.round(newdist / 1000 * 100000) / 100000;
- 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);
- }
- },
- /*
- * Calculates the full extent of the data array
- */
- _calculateFullExtent: function(data) {
- if (!data || data.length < 1) {
- throw new Error("no data in parameters");
- }
- var ext = new L.latLngBounds(data[0].latlng, data[0].latlng);
- data.forEach(function(item) {
- ext.extend(item.latlng);
- });
- return ext;
- },
- /*
- * Add data to the diagram either from GPX or GeoJSON and
- * update the axis domain and data
- */
- addData: function(d) {
- this._addData(d);
- if (this._container) {
- this._applyData();
- }
- },
- _applyData: function() {
- var xdomain = d3.extent(this._data, function(d) {
- return d.dist;
- });
- var ydomain = d3.extent(this._data, function(d) {
- return d.altitude;
- });
- var opts = this.options;
- if (opts.yAxisMin !== undefined && (opts.yAxisMin < ydomain[0] || opts.forceAxisBounds)) {
- ydomain[0] = opts.yAxisMin;
- }
- if (opts.yAxisMax !== undefined && (opts.yAxisMax > ydomain[1] || opts.forceAxisBounds)) {
- ydomain[1] = opts.yAxisMax;
- }
- this._x.domain(xdomain);
- this._y.domain(ydomain);
- this._areapath.datum(this._data)
- .attr("d", this._area);
- this._updateAxis();
- this._fullExtent = this._calculateFullExtent(this._data);
- },
- /*
- * Reset data
- */
- _clearData: function() {
- this._data = null;
- this._dist = null;
- this._maxElevation = null;
- },
- /*
- * Reset data and display
- */
- clear: function() {
- this._clearData();
- if (!this._areapath) {
- return;
- }
- // 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);
- };
|