Skip to content

Commit

Permalink
added ruler plugin to leaflet map
Browse files Browse the repository at this point in the history
  • Loading branch information
ASC95 committed Apr 3, 2024
1 parent 2f0fc27 commit b8aabb7
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 0 deletions.
3 changes: 3 additions & 0 deletions omf/geo.py
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,9 @@ def map_omd(omd_path, output_dir, open_browser=False):
currentUser=None, showFileMenu=json.dumps(False), isOnline=json.dumps(False), css=css, js=js)
output_dir = pathlib.Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
# - Copy PNGs
for filepath in (pathlib.Path(omf.omfDir).resolve(True) / 'static' / 'geoJsonMap' / 'v3').glob('**/*.png'):
shutil.copy2(filepath, output_dir)
with open(output_dir / 'geoJson_offline.html', 'w') as f:
f.write(rendered)
if open_browser:
Expand Down
41 changes: 41 additions & 0 deletions omf/static/geoJsonMap/v3/lib/leaflet-ruler.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.leaflet-ruler{
height: 35px;
width: 35px;
background-image: url("./protractor.png"); /* <div>Icons made by <a href="http://www.freepik.com" title="Freepik">Freepik</a> from <a href="http://www.flaticon.com" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></div> */
background-repeat: no-repeat;
background-position: center;
}
.leaflet-ruler:hover{
background-image: url("./protractor-colored.png"); /* <div>Icons made by <a href="http://www.freepik.com" title="Freepik">Freepik</a> from <a href="http://www.flaticon.com" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></div> */
}
.leaflet-ruler-clicked{
height: 35px;
width: 35px;
background-repeat: no-repeat;
background-position: center;
background-image: url("./protractor-colored.png");
border-color: chartreuse !important;
}
.leaflet-bar{
background-color: #ffffff;
}
.leaflet-control {
cursor: pointer;
}
.result-tooltip{
background-color: white;
border-width: medium;
border-color: #de0000;
font-size: smaller;
}
.moving-tooltip{
background-color: rgba(255, 255, 255, .7);
background-clip: padding-box;
opacity: 0.5;
border: dotted;
border-color: red;
font-size: smaller;
}
.plus-length{
padding-left: 45px;
}
180 changes: 180 additions & 0 deletions omf/static/geoJsonMap/v3/lib/leaflet-ruler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
(function(factory, window){
"use strict";
if (typeof define === 'function' && define.amd) {
define(['leaflet'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(require('leaflet'));
}
if (typeof window !== 'undefined' && window.L) {
window.L.Ruler = factory(L);
}
}(function (L) {
"use strict";
L.Control.Ruler = L.Control.extend({
options: {
position: 'topright',
events: {
onToggle: function (is_active) { }
},
circleMarker: {
color: 'red',
radius: 2
},
lineStyle: {
color: 'red',
dashArray: '1,6'
},
lengthUnit: {
display: 'km',
decimal: 2,
factor: null,
label: 'Distance:'
},
angleUnit: {
display: '&deg;',
decimal: 2,
factor: null,
label: 'Bearing:'
}
},
isActive: function () {
return this._choice;
},
onAdd: function(map) {
this._map = map;
this._container = L.DomUtil.create('div', 'leaflet-bar');
this._container.classList.add('leaflet-ruler');
L.DomEvent.disableClickPropagation(this._container);
L.DomEvent.on(this._container, 'click', this._toggleMeasure, this);
this._choice = false;
this._defaultCursor = this._map._container.style.cursor;
this._allLayers = L.layerGroup();
return this._container;
},
onRemove: function() {
L.DomEvent.off(this._container, 'click', this._toggleMeasure, this);
},
_toggleMeasure: function() {
this._choice = !this._choice;
this.options.events.onToggle(this._choice);
this._clickedLatLong = null;
this._clickedPoints = [];
this._totalLength = 0;
if (this._choice){
this._map.doubleClickZoom.disable();
L.DomEvent.on(this._map._container, 'keydown', this._escape, this);
L.DomEvent.on(this._map._container, 'dblclick', this._closePath, this);
this._container.classList.add("leaflet-ruler-clicked");
this._clickCount = 0;
this._tempLine = L.featureGroup().addTo(this._allLayers);
this._tempPoint = L.featureGroup().addTo(this._allLayers);
this._pointLayer = L.featureGroup().addTo(this._allLayers);
this._polylineLayer = L.featureGroup().addTo(this._allLayers);
this._allLayers.addTo(this._map);
this._map._container.style.cursor = 'crosshair';
this._map.on('click', this._clicked, this);
this._map.on('mousemove', this._moving, this);
}
else {
this._map.doubleClickZoom.enable();
L.DomEvent.off(this._map._container, 'keydown', this._escape, this);
L.DomEvent.off(this._map._container, 'dblclick', this._closePath, this);
this._container.classList.remove("leaflet-ruler-clicked");
this._map.removeLayer(this._allLayers);
this._allLayers = L.layerGroup();
this._map._container.style.cursor = this._defaultCursor;
this._map.off('click', this._clicked, this);
this._map.off('mousemove', this._moving, this);
}
},
_clicked: function(e) {
this._clickedLatLong = e.latlng;
this._clickedPoints.push(this._clickedLatLong);
L.circleMarker(this._clickedLatLong, this.options.circleMarker).addTo(this._pointLayer);
if(this._clickCount > 0 && !e.latlng.equals(this._clickedPoints[this._clickedPoints.length - 2])){
if (this._movingLatLong){
L.polyline([this._clickedPoints[this._clickCount-1], this._movingLatLong], this.options.lineStyle).addTo(this._polylineLayer);
}
var text;
this._totalLength += this._result.Distance;
if (this._clickCount > 1){
text = '<b>' + this.options.angleUnit.label + '</b>&nbsp;' + this._result.Bearing.toFixed(this.options.angleUnit.decimal) + '&nbsp;' + this.options.angleUnit.display + '<br><b>' + this.options.lengthUnit.label + '</b>&nbsp;' + this._totalLength.toFixed(this.options.lengthUnit.decimal) + '&nbsp;' + this.options.lengthUnit.display;
}
else {
text = '<b>' + this.options.angleUnit.label + '</b>&nbsp;' + this._result.Bearing.toFixed(this.options.angleUnit.decimal) + '&nbsp;' + this.options.angleUnit.display + '<br><b>' + this.options.lengthUnit.label + '</b>&nbsp;' + this._result.Distance.toFixed(this.options.lengthUnit.decimal) + '&nbsp;' + this.options.lengthUnit.display;
}
L.circleMarker(this._clickedLatLong, this.options.circleMarker).bindTooltip(text, {permanent: true, className: 'result-tooltip'}).addTo(this._pointLayer).openTooltip();
}
this._clickCount++;
},
_moving: function(e) {
if (this._clickedLatLong){
L.DomEvent.off(this._container, 'click', this._toggleMeasure, this);
this._movingLatLong = e.latlng;
if (this._tempLine){
this._map.removeLayer(this._tempLine);
this._map.removeLayer(this._tempPoint);
}
var text;
this._addedLength = 0;
this._tempLine = L.featureGroup();
this._tempPoint = L.featureGroup();
this._tempLine.addTo(this._map);
this._tempPoint.addTo(this._map);
this._calculateBearingAndDistance();
this._addedLength = this._result.Distance + this._totalLength;
L.polyline([this._clickedLatLong, this._movingLatLong], this.options.lineStyle).addTo(this._tempLine);
if (this._clickCount > 1){
text = '<b>' + this.options.angleUnit.label + '</b>&nbsp;' + this._result.Bearing.toFixed(this.options.angleUnit.decimal) + '&nbsp;' + this.options.angleUnit.display + '<br><b>' + this.options.lengthUnit.label + '</b>&nbsp;' + this._addedLength.toFixed(this.options.lengthUnit.decimal) + '&nbsp;' + this.options.lengthUnit.display + '<br><div class="plus-length">(+' + this._result.Distance.toFixed(this.options.lengthUnit.decimal) + ')</div>';
}
else {
text = '<b>' + this.options.angleUnit.label + '</b>&nbsp;' + this._result.Bearing.toFixed(this.options.angleUnit.decimal) + '&nbsp;' + this.options.angleUnit.display + '<br><b>' + this.options.lengthUnit.label + '</b>&nbsp;' + this._result.Distance.toFixed(this.options.lengthUnit.decimal) + '&nbsp;' + this.options.lengthUnit.display;
}
L.circleMarker(this._movingLatLong, this.options.circleMarker).bindTooltip(text, {sticky: true, offset: L.point(0, -40) ,className: 'moving-tooltip'}).addTo(this._tempPoint).openTooltip();
}
},
_escape: function(e) {
if (e.keyCode === 27){
if (this._clickCount > 0){
this._closePath();
}
else {
this._choice = true;
this._toggleMeasure();
}
}
},
_calculateBearingAndDistance: function() {
var f1 = this._clickedLatLong.lat, l1 = this._clickedLatLong.lng, f2 = this._movingLatLong.lat, l2 = this._movingLatLong.lng;
var toRadian = Math.PI / 180;
// haversine formula
// bearing
var y = Math.sin((l2-l1)*toRadian) * Math.cos(f2*toRadian);
var x = Math.cos(f1*toRadian)*Math.sin(f2*toRadian) - Math.sin(f1*toRadian)*Math.cos(f2*toRadian)*Math.cos((l2-l1)*toRadian);
var brng = Math.atan2(y, x)*((this.options.angleUnit.factor ? this.options.angleUnit.factor/2 : 180)/Math.PI);
brng += brng < 0 ? (this.options.angleUnit.factor ? this.options.angleUnit.factor : 360) : 0;
// distance
var R = this.options.lengthUnit.factor ? 6371 * this.options.lengthUnit.factor : 6371; // kilometres
var deltaF = (f2 - f1)*toRadian;
var deltaL = (l2 - l1)*toRadian;
var a = Math.sin(deltaF/2) * Math.sin(deltaF/2) + Math.cos(f1*toRadian) * Math.cos(f2*toRadian) * Math.sin(deltaL/2) * Math.sin(deltaL/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var distance = R * c;
this._result = {
Bearing: brng,
Distance: distance
};
},
_closePath: function() {
this._map.removeLayer(this._tempLine);
this._map.removeLayer(this._tempPoint);
if (this._clickCount <= 1) this._map.removeLayer(this._pointLayer);
this._choice = false;
L.DomEvent.on(this._container, 'click', this._toggleMeasure, this);
this._toggleMeasure();
}
});
L.control.ruler = function(options) {
return new L.Control.Ruler(options);
};
}, window));
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added omf/static/geoJsonMap/v3/lib/protractor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions omf/static/geoJsonMap/v3/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ function main() {
LeafletLayer.control.addTo(LeafletLayer.map);
addZoomToFitButon();
addClusterButton();
addRuler();
addGeocoding();
// - Disable the following annoying default Leaflet keyboard shortcuts:
// - TODO: do a better job and stop the event(s) from propagating in text inputs instead
Expand Down Expand Up @@ -383,6 +384,25 @@ function addGeocoding() {
LeafletLayer.map.addControl(search);
}

function addRuler() {
var options = {
position: 'topleft',
lengthUnit: {
display: 'm',
decimal: 3,
factor: 1000,
label: 'Distance (m):'
},
angleUnit: {
display: '&deg;',
decimal: 3,
factor: null,
label: 'Angle:'
}
};
L.control.ruler(options).addTo(LeafletLayer.map);
}

(function loadInterface() {
const modalInsert = document.createElement('div');
modalInsert.id = 'modalInsert';
Expand Down
3 changes: 3 additions & 0 deletions omf/templates/geoJson.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
<!-- Geocoding -->
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/geosearch.css"/>
<script src="https://unpkg.com/[email protected]/dist/geosearch.umd.js"></script>
<!-- Ruler -->
<link rel="stylesheet" href="/static/geoJsonMap/v3/lib/leaflet-ruler.css"/>
<script type="text/javascript" src="/static/geoJsonMap/v3/lib/leaflet-ruler.js"></script>
<!-- Use Graphology library for graph operations -->
<script src="/static/geoJsonMap/v3/lib/graphology-0.25.1.min.js"></script>
<!-- CSV parsing -->
Expand Down

0 comments on commit b8aabb7

Please sign in to comment.