Add scale bar to edge of map
This commit is contained in:
parent
9acab81e98
commit
050402b623
3 changed files with 301 additions and 0 deletions
|
@ -17,6 +17,7 @@ Successfully built this on Raspbian OS (Raspberry Pi 3) and Ubuntu 18.04.2 (64-b
|
|||
- Multiple map layers, including topology, streets, and satellite ([Bing API key](https://www.bingmapsportal.com) required for satellite)
|
||||
- Automatically acquires new data and adds markers to the map
|
||||
- Tracking tool to use device GPS to place marker of current position (and direction) on map
|
||||
- Scale bar at edge of map
|
||||
- SSL/HTTPS (required for location tracking)
|
||||
- Measuring tool for measure distances between points
|
||||
- Map stays focused on the same point across page refreshes
|
||||
|
|
297
flask_app/static/leaflet-edgescalebar/leaflet.edgescalebar.js
Normal file
297
flask_app/static/leaflet-edgescalebar/leaflet.edgescalebar.js
Normal file
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
* Draws the metric scale bars in Web Mercator map along top and right edges.
|
||||
* Authors: Dražen Tutić (dtutic@geof.hr), Ana Kuveždić Divjak (akuvezdic@geof.hr)
|
||||
* University of Zagreb, Faculty of Geodesy, GEOF-OSGL Lab
|
||||
* Inspired by LatLonGraticule Leaflet plugin by: lanwei@cloudybay.com.tw
|
||||
*/
|
||||
|
||||
L.EdgeScaleBar = L.Layer.extend({
|
||||
includes: L.Mixin.Events,
|
||||
|
||||
options: {
|
||||
opacity: 1,
|
||||
weight: 0.8,
|
||||
color: '#000',
|
||||
font: '11px Arial',
|
||||
zoomInterval: [
|
||||
{start: 1, end: 2, interval: 5000000},
|
||||
{start: 3, end: 3, interval: 2000000},
|
||||
{start: 4, end: 4, interval: 1000000},
|
||||
{start: 5, end: 5, interval: 500000},
|
||||
{start: 6, end: 7, interval: 200000},
|
||||
{start: 8, end: 8, interval: 100000},
|
||||
{start: 9, end: 9, interval: 50000},
|
||||
{start: 10, end: 10, interval: 20000},
|
||||
{start: 11, end: 11, interval: 10000},
|
||||
{start: 12, end: 12, interval: 5000},
|
||||
{start: 13, end: 13, interval: 2000},
|
||||
{start: 14, end: 14, interval: 1000},
|
||||
{start: 15, end: 15, interval: 500},
|
||||
{start: 16, end: 16, interval: 200},
|
||||
{start: 17, end: 17, interval: 100},
|
||||
{start: 18, end: 18, interval: 50},
|
||||
{start: 19, end: 19, interval: 20},
|
||||
{start: 20, end: 20, interval: 10}
|
||||
]
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
|
||||
L.setOptions(this, options);
|
||||
|
||||
//Constants of the WGS84 ellipsoid needed to calculate meridian length or latitute
|
||||
this._a = 6378137.0;
|
||||
this._b = 6356752.3142;
|
||||
this._e2 = (this._a*this._a - this._b*this._b)/(this._a*this._a);
|
||||
this._n = (this._a - this._b)/(this._a + this._b);
|
||||
this._n2 = this._n * this._n;
|
||||
this._A = this._a*(1.0 - this._n)*(1.0 - this._n2)*(1.0 + 9.0/4.0*this._n2 + 225.0/64.0*this._n2*this._n2);
|
||||
this._ic1 = 1.5*this._n - 29.0/12.0*this._n2*this._n + 553.0/80.0*this._n2*this._n2*this._n;
|
||||
this._ic2 = 21.0/8.0*this._n2 - 1537.0/128.0*this._n2*this._n2;
|
||||
this._ic3 = 151.0/24.0*this._n2*this._n - 32373.0/640.0*this._n2*this._n2*this._n;
|
||||
this._ic4 = 1097.0/64.0*this._n2*this._n2;
|
||||
this._ic5 = 8011.0/150.0*this._n2*this._n2*this._n;
|
||||
this._c1 = -1.5*this._n + 31.0/24.0*this._n2*this._n - 669.0/640.0*this._n2*this._n2*this._n;
|
||||
this._c2 = 15.0/18.0*this._n2 - 435.0/128.0*this._n2*this._n2;
|
||||
this._c3 = -35.0/12.0*this._n2*this._n + 651.0/80.0*this._n2*this._n2*this._n;
|
||||
this._c4 = 315.0/64.0*this._n2*this._n2;
|
||||
this._c5 = -693.0/80.0*this._n2*this._n2*this._n;
|
||||
|
||||
//Latitude limit of the Web Mercator projection
|
||||
this._LIMIT_PHI = 1.484419982;
|
||||
},
|
||||
|
||||
onAdd: function (map) {
|
||||
this._map = map;
|
||||
|
||||
if (!this._container) {
|
||||
this._initCanvas();
|
||||
}
|
||||
|
||||
map._panes.overlayPane.appendChild(this._container);
|
||||
|
||||
map.on('viewreset', this._reset, this);
|
||||
map.on('move', this._reset, this);
|
||||
map.on('moveend', this._reset, this);
|
||||
|
||||
this._reset();
|
||||
},
|
||||
|
||||
onRemove: function (map) {
|
||||
map.getPanes().overlayPane.removeChild(this._container);
|
||||
|
||||
map.off('viewreset', this._reset, this);
|
||||
map.off('move', this._reset, this);
|
||||
map.off('moveend', this._reset, this);
|
||||
},
|
||||
|
||||
addTo: function (map) {
|
||||
map.addLayer(this);
|
||||
return this;
|
||||
},
|
||||
|
||||
setOpacity: function (opacity) {
|
||||
this.options.opacity = opacity;
|
||||
this._updateOpacity();
|
||||
return this;
|
||||
},
|
||||
|
||||
bringToFront: function () {
|
||||
if (this._canvas) {
|
||||
this._map._panes.overlayPane.appendChild(this._canvas);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
bringToBack: function () {
|
||||
var pane = this._map._panes.overlayPane;
|
||||
if (this._canvas) {
|
||||
pane.insertBefore(this._canvas, pane.firstChild);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
getAttribution: function () {
|
||||
return this.options.attribution;
|
||||
},
|
||||
|
||||
_initCanvas: function () {
|
||||
this._container = L.DomUtil.create('div', 'leaflet-image-layer');
|
||||
|
||||
this._canvas = L.DomUtil.create('canvas', '');
|
||||
this._ctx = this._canvas.getContext('2d');
|
||||
|
||||
this._vert_gradientFill=this._ctx.createLinearGradient(0,0,0,10);
|
||||
this._vert_gradientFill.addColorStop(0,"rgba(255, 255, 255, 1)");
|
||||
this._vert_gradientFill.addColorStop(1,"rgba(255, 255, 255, 0)");
|
||||
|
||||
this._hor_gradientFill=this._ctx.createLinearGradient(this._map.getSize().x-10,0,this._map.getSize().x,0);
|
||||
this._hor_gradientFill.addColorStop(0,"rgba(255, 255, 255, 0)");
|
||||
this._hor_gradientFill.addColorStop(1,"rgba(255, 255, 255, 1)");
|
||||
|
||||
this._updateOpacity();
|
||||
|
||||
this._container.appendChild(this._canvas);
|
||||
|
||||
L.extend(this._canvas, {
|
||||
onselectstart: L.Util.falseFn,
|
||||
onmousemove: L.Util.falseFn,
|
||||
onload: L.bind(this._onCanvasLoad, this)
|
||||
});
|
||||
},
|
||||
|
||||
_reset: function () {
|
||||
var container = this._container,
|
||||
canvas = this._canvas,
|
||||
size = this._map.getSize(),
|
||||
lt = this._map.containerPointToLayerPoint([0, 0]);
|
||||
|
||||
L.DomUtil.setPosition(container, lt);
|
||||
|
||||
container.style.width = size.x + 'px';
|
||||
container.style.height = size.y + 'px';
|
||||
|
||||
canvas.width = size.x;
|
||||
canvas.height = size.y;
|
||||
canvas.style.width = size.x + 'px';
|
||||
canvas.style.height = size.y + 'px';
|
||||
|
||||
this._ctx.fillStyle= this._vert_gradientFill;
|
||||
this._ctx.fillRect(0,0,size.x,10);
|
||||
this._ctx.fillStyle= this._hor_gradientFill;
|
||||
this._ctx.fillRect(size.x-10,0,size.x,size.y);
|
||||
|
||||
this._ctx.beginPath();
|
||||
this._ctx.moveTo(0,0);
|
||||
this._ctx.lineTo(size.x,0);
|
||||
this._ctx.lineTo(size.x,size.y);
|
||||
this._ctx.stroke();
|
||||
|
||||
this._calcInterval();
|
||||
this._draw();
|
||||
},
|
||||
|
||||
_onCanvasLoad: function () {
|
||||
this.fire('load');
|
||||
},
|
||||
|
||||
_updateOpacity: function () {
|
||||
L.DomUtil.setOpacity(this._canvas, this.options.opacity);
|
||||
},
|
||||
|
||||
_calcInterval: function() {
|
||||
var zoom = this._map.getZoom();
|
||||
for (var idx in this.options.zoomInterval) {
|
||||
var dict = this.options.zoomInterval[idx];
|
||||
if (dict.start <= zoom) {
|
||||
if (dict.end && dict.end >= zoom) {
|
||||
this._dd = dict.interval;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this._currZoom = zoom;
|
||||
},
|
||||
|
||||
_draw: function() {
|
||||
this._ctx.strokeStyle = this.options.color;
|
||||
this._create_lat_ticks();
|
||||
this._create_lon_ticks();
|
||||
|
||||
this._ctx.fillStyle = this.options.color;
|
||||
this._ctx.font = this.options.font;
|
||||
var units = ' m', dd = this._dd;
|
||||
if (this._dd >= 1000) { units = ' km'; dd = this._dd/1000; }
|
||||
this._ctx.textAlign="right";
|
||||
this._ctx.textBaseline="middle";
|
||||
this._ctx.fillText(dd + units, this._map.getSize().x-12, this._map.getSize().y/2);
|
||||
this._ctx.textAlign="center";
|
||||
this._ctx.textBaseline="top";
|
||||
this._ctx.fillText(dd + units, this._map.getSize().x/2, 12);
|
||||
},
|
||||
|
||||
_create_lat_ticks: function() {
|
||||
var phi_s = this._map.containerPointToLatLng(L.point(0,this._map.getSize().y/2)).lat;
|
||||
var phi_d = this._map.containerPointToLatLng(L.point(0,this._map.getSize().y)).lat;
|
||||
var phi_g = this._map.containerPointToLatLng(L.point(0,0)).lat;
|
||||
var d_s = this._merLength(phi_s*Math.PI/180.0);
|
||||
var d_g = this._merLength(phi_g*Math.PI/180.0);
|
||||
var d_d = this._merLength(phi_d*Math.PI/180.0);
|
||||
|
||||
// draw major ticks
|
||||
for (i = d_s+this._dd/2; i < d_g; i = i + this._dd) {
|
||||
var phi = this._invmerLength(i);
|
||||
if ((phi < this._LIMIT_PHI) && (phi > -this._LIMIT_PHI)) this._draw_lat_tick(phi,10,this.options.weight*1.5);
|
||||
}
|
||||
for (i = d_s-this._dd/2; i > d_d; i = i - this._dd) {
|
||||
var phi = this._invmerLength(i);
|
||||
if ((phi > -this._LIMIT_PHI) && (phi < this._LIMIT_PHI)) this._draw_lat_tick(phi,10,this.options.weight*1.5);
|
||||
}
|
||||
|
||||
// draw minor ticks
|
||||
for (i = d_s; i < d_g; i = i + this._dd/10.0) {
|
||||
var phi = this._invmerLength(i);
|
||||
if ((phi < this._LIMIT_PHI) && (phi > -this._LIMIT_PHI)) this._draw_lat_tick(phi,4,this.options.weight);
|
||||
}
|
||||
for (i = d_s-this._dd/10; i > d_d; i = i - this._dd/10) {
|
||||
var phi = this._invmerLength(i);
|
||||
if ((phi > -this._LIMIT_PHI) && (phi < this._LIMIT_PHI)) this._draw_lat_tick(phi,4,this.options.weight);
|
||||
}
|
||||
},
|
||||
|
||||
_create_lon_ticks: function() {
|
||||
var cen_p = this._map.containerPointToLatLng(L.point(this._map.getSize().x/2,0));
|
||||
var left_p = this._map.containerPointToLatLng(L.point(0,0));
|
||||
var right_p = this._map.containerPointToLatLng(L.point(this._map.getSize().x,0));
|
||||
var sinPhi = Math.sin(cen_p.lat*Math.PI/180.0);
|
||||
var N = this._a/Math.sqrt(1.0-this._e2*sinPhi*sinPhi);
|
||||
var dl = this._dd/(N*Math.cos(cen_p.lat*Math.PI/180.0))*180.0/Math.PI;
|
||||
|
||||
// draw major ticks
|
||||
for (i = cen_p.lng+dl/2; i < right_p.lng; i = i + dl) this._draw_lon_tick(i,10,this.options.weight*1.5);
|
||||
for (i = cen_p.lng-dl/2; i > left_p.lng; i = i - dl) this._draw_lon_tick(i,10,this.options.weight*1.5);
|
||||
|
||||
// draw minor ticks
|
||||
for (i = cen_p.lng; i < right_p.lng; i = i + dl/10) this._draw_lon_tick(i,4,this.options.weight);
|
||||
for (i = cen_p.lng-dl/10; i > left_p.lng; i = i - dl/10) this._draw_lon_tick(i,4,this.options.weight);
|
||||
},
|
||||
|
||||
_latLngToCanvasPoint: function (latlng) {
|
||||
var projectedPoint = this._map.project(L.latLng(latlng));
|
||||
projectedPoint._subtract(this._map.getPixelOrigin());
|
||||
return L.point(projectedPoint).add(this._map._getMapPanePos());
|
||||
},
|
||||
|
||||
_draw_lat_tick: function (phi, size, weight) {
|
||||
var y = this._latLngToCanvasPoint(L.latLng(phi*180.0/Math.PI, 0.0)).y;
|
||||
this._ctx.lineWidth = weight;
|
||||
this._ctx.beginPath();
|
||||
this._ctx.moveTo(this._map.getSize().x, y);
|
||||
this._ctx.lineTo(this._map.getSize().x-size, y);
|
||||
this._ctx.stroke();
|
||||
},
|
||||
|
||||
_draw_lon_tick: function(lam, size, weight) {
|
||||
var x = this._latLngToCanvasPoint(L.latLng(0.0, lam)).x;
|
||||
this._ctx.lineWidth = weight;
|
||||
this._ctx.beginPath();
|
||||
this._ctx.moveTo(x, 0);
|
||||
this._ctx.lineTo(x, size);
|
||||
this._ctx.stroke();
|
||||
},
|
||||
|
||||
_merLength: function(phi) {
|
||||
var cos2phi = Math.cos(2.0*phi);
|
||||
return this._A*(phi+Math.sin(2.0*phi)*(this._c1+(this._c2+(this._c3+(this._c4+this._c5*cos2phi)*cos2phi)*cos2phi)*cos2phi));
|
||||
},
|
||||
|
||||
_invmerLength: function(s) {
|
||||
var psi = s/this._A;
|
||||
var cos2psi = Math.cos(2.0*psi);
|
||||
return psi+Math.sin(2.0*psi)*(this._ic1+(this._ic2+(this._ic3+(this._ic4+this._ic5*cos2psi)*cos2psi)*cos2psi)*cos2psi);
|
||||
}
|
||||
});
|
||||
|
||||
L.edgeScaleBar = function (options) {
|
||||
return new L.EdgeScaleBar(options);
|
||||
};
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
<script src="{{url_for('static', filename='jquery-3.3.1.min.js')}}"></script>
|
||||
<script src="{{url_for('static', filename='leaflet/leaflet.js')}}"></script>
|
||||
<script src="{{url_for('static', filename='leaflet-edgescalebar/leaflet.edgescalebar.js')}}"></script>
|
||||
<script src="{{url_for('static', filename='leaflet-measure/leaflet-measure.js')}}"></script>
|
||||
<script src="{{url_for('static', filename='leaflet-locatecontrol/L.Control.Locate.min.js')}}"></script>
|
||||
<script src="{{url_for('static', filename='leaflet-bing-layer.min.js')}}"></script>
|
||||
|
@ -88,6 +89,8 @@
|
|||
|
||||
L.control.locate().addTo(map);
|
||||
|
||||
L.edgeScaleBar().addTo(map);
|
||||
|
||||
var baseLayers = {
|
||||
"Topographic": esri_Map,
|
||||
"OpenStreetMap": osm_Map,
|
||||
|
|
Loading…
Add table
Reference in a new issue