Skip to content

Instantly share code, notes, and snippets.

@rgdonohue
Last active March 13, 2023 23:22
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rgdonohue/0598de175f591803fa97d65d8330f40d to your computer and use it in GitHub Desktop.
Save rgdonohue/0598de175f591803fa97d65d8330f40d to your computer and use it in GitHub Desktop.
Locate and find nearest in Leaflet

This example queries data from CARTO, uses the geolocation API to get the user's position, and finds the nearest three features.

Note: geolocation appears blocked within bl.ocks.org.

(function() {
var zoomLevel = 4,
mapCenter = [38, -101];
var options = {
center: mapCenter,
zoom: zoomLevel
};
var map = L.map('map', options);
L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> &copy; <a href="http://cartodb.com/attributions">CartoDB</a>',
subdomains: 'abcd',
maxZoom: 19
}).addTo(map);
var sqlQuery = 'SELECT * FROM stations';
var stations,
$body = $('body'),
$locate = $('#locate'),
$findNearest = $('#find-nearest'),
$status = $('#status');
$.getJSON('https://rgdonohue.carto.com/api/v2/sql?format=GeoJSON&q=' + sqlQuery, function(data) {
//$('#loader').fadeOut();
$body.addClass('loaded');
stations = L.geoJson(data, {
pointToLayer : function(feature, latlng) {
return L.circleMarker(latlng, {
stroke : false,
fillColor : 'orange',
fillOpacity : 1,
radius: 2
});
}
}).addTo(map);
$locate.fadeIn().on('click', function(e) {
$status.html('finding your location');
if (!navigator.geolocation){
alert("<p>Sorry, your browser does not support Geolocation</p>");
return;
}
$body.removeClass('loaded');
navigator.geolocation.getCurrentPosition(success, error);
$locate.fadeOut();
});
});
function success(position) {
$body.addClass('loaded');
var currentPos = [position.coords.latitude,position.coords.longitude];
map.setView(currentPos, zoomLevel);
var myLocation = L.marker(currentPos)
.addTo(map)
.bindTooltip("you are here")
.openTooltip();
$findNearest.fadeIn()
.on('click', function(e) {
$findNearest.fadeOut();
$status.html('finding your nearest locations')
queryFeatures(currentPos, 5);
myLocation.unbindTooltip();
});
};
function error() {
alert("Unable to retrieve your location");
};
function queryFeatures(currentPos, numResults) {
var distances = [];
stations.eachLayer(function(l) {
var distance = L.latLng(currentPos).distanceTo(l.getLatLng())/1000;
distances.push(distance);
});
distances.sort(function(a, b) {
return a - b;
});
var stationsLayer = L.featureGroup();
stations.eachLayer(function(l) {
var distance = L.latLng(currentPos).distanceTo(l.getLatLng())/1000;
if(distance < distances[numResults]) {
l.bindTooltip(distance.toLocaleString() + ' km from current location.');
L.polyline([currentPos, l.getLatLng()], {
color : 'orange',
weight : 2,
opacity: 1,
dashArray : "5, 10"
}).addTo(stationsLayer);
}
});
map.flyToBounds(stationsLayer.getBounds(), {duration : 3, easeLinearity: .1 });
map.on('zoomend', function() {
map.addLayer(stationsLayer);
})
}
})();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Find Nearest Points in Leaflet</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.1/dist/leaflet.css" />
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id='map'></div>
<div id='ui'>
<button id='locate'>
Find my location
</button>
<button id='find-nearest'>
Find Nearest Stations
</button>
</div>
<div id="loader-wrapper">
<div id="loader"></div>
<div class="loader-section section-left"></div>
<div class="loader-section section-right"></div>
<p id='status'>loading vehicle recharging station data</p>
</div>
<script src="https://code.jquery.com/jquery-3.1.0.min.js" integrity="sha256-cCueBR6CsyA4/9szpPfrX3s49M9vUU5BgtiJj06wt/s=" crossorigin="anonymous"></script>
<script src="https://unpkg.com/leaflet@1.0.1/dist/leaflet.js"></script>
<script src="app.js"></script>
</body>
</html>
body {
color: #888;
font-family: "Proxima Nova", sans-serif;
}
#loader-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
}
#status {
position: absolute;
text-align: center;
width: 100%;
margin: 100px auto;
color: #3d3d3d;
font-size: 1.6em;
z-index: 4000;
}
#loader {
display: block;
position: relative;
left: 50%;
top: 50%;
width: 100px;
height: 100px;
margin: -75px 0 0 -75px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #4682b4;
-webkit-animation: spin 2s linear infinite; /* Chrome, Opera 15+, Safari 5+ */
animation: spin 2s linear infinite; /* Chrome, Firefox 16+, IE 10+, Opera */
z-index: 1001;
}
#loader:before {
content: "";
position: absolute;
top: 5px;
left: 5px;
right: 5px;
bottom: 5px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #82b446;
-webkit-animation: spin 3s linear infinite; /* Chrome, Opera 15+, Safari 5+ */
animation: spin 3s linear infinite; /* Chrome, Firefox 16+, IE 10+, Opera */
}
#loader:after {
content: "";
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #b446b4;
-webkit-animation: spin 1.5s linear infinite; /* Chrome, Opera 15+, Safari 5+ */
animation: spin 1.5s linear infinite; /* Chrome, Firefox 16+, IE 10+, Opera */
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg); /* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(0deg); /* IE 9 */
transform: rotate(0deg); /* Firefox 16+, IE 10+, Opera */
}
100% {
-webkit-transform: rotate(360deg); /* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(360deg); /* IE 9 */
transform: rotate(360deg); /* Firefox 16+, IE 10+, Opera */
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg); /* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(0deg); /* IE 9 */
transform: rotate(0deg); /* Firefox 16+, IE 10+, Opera */
}
100% {
-webkit-transform: rotate(360deg); /* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: rotate(360deg); /* IE 9 */
transform: rotate(360deg); /* Firefox 16+, IE 10+, Opera */
}
}
#loader-wrapper .loader-section {
position: fixed;
top: 0;
width: 50%;
height: 100%;
background: rgba(256,256,256,.8);
z-index: 1000;
-webkit-transform: translateX(0); /* Chrome, Opera 15+, Safari 3.1+ */
-ms-transform: translateX(0); /* IE 9 */
transform: translateX(0); /* Firefox 16+, IE 10+, Opera */
}
#loader-wrapper .loader-section.section-left {
left: 0;
}
#loader-wrapper .loader-section.section-right {
right: 0;
}
.loaded #loader, .loaded #loader-wrapper {
opacity: 0;
display: none;
-webkit-transition: all 0.3s ease-out;
transition: all 0.3s ease-out;
}
#map {
position: absolute;
width: 100%;
left: 0;
right: 0;
top: 0;
bottom: 0;
border: 1px solid #777;
box-shadow: 2px 3px 3px 0px rgba(196,192,196,1);
}
button {
position: absolute;
top: 15px;
left: 50px;
padding: .5em 1em;
background-color: #fff;
font-size: 1.2em;
border:0;
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
border-radius: 4px;
color: black;
z-index: 1100;
}
button:hover {
cursor: pointer;
}
#locate {
display: none;
}
#find-nearest {
display: none;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment