// The API Key provided is restricted to JSFiddle website
// Get your own API Key on https://myprojects.geoapify.com
const myAPIKey = "6dc7fb95a3b246cfa0f3bcef5ce9ed9a";
// Create a Leaflet map
const map = L.map('address-map').setView([50, 10], 5);
// Retina displays require different mat tiles quality
const isRetina = L.Browser.retina;
const baseUrl = "https://maps.geoapify.com/v1/tile/osm-bright/{z}/{x}/{y}.png?apiKey={apiKey}";
const retinaUrl = "https://maps.geoapify.com/v1/tile/osm-bright/{z}/{x}/{y}@2x.png?apiKey={apiKey}";
// add Geoapify attribution
map.attributionControl.setPrefix('Powered by <a href="https://www.geoapify.com/" target="_blank">Geoapify</a>')
// Add map tiles layer. Set 20 as the maximal zoom and provide map data attribution.
L.tileLayer(isRetina ? retinaUrl : baseUrl, {
attribution: '<a href="https://openmaptiles.org/" target="_blank">© OpenMapTiles</a> <a href="https://www.openstreetmap.org/copyright" target="_blank">© OpenStreetMap</a> contributors',
apiKey: myAPIKey,
maxZoom: 20,
id: 'osm-bright',
}).addTo(map);
// move zoom controls to bottom right
map.zoomControl.remove();
L.control.zoom({
position: 'bottomright'
}).addTo(map);
const autocompleteInput = new autocomplete.GeocoderAutocomplete(
document.getElementById("autocomplete"),
myAPIKey, {
lang: 'en',
allowNonVerifiedHouseNumber: true,
allowNonVerifiedStreet: true,
skipDetails: true
});
let selectedLocation;
let locationNeedToBeClarified;
let suggestedLocation;
let locationMarker;
autocompleteInput.on('select', (location) => {
selectLocation(location);
});
map.on('click', (event) => {
if (!selectedLocation || locationNeedToBeClarified) {
getAddressByLatLon(event.latlng.lat, event.latlng.lng).then(location => {
if (selectedLocation) {
if (locationMarker) {
locationMarker.setLatLng(event.latlng);
}
suggestedLocation = location;
updateAddressInfo();
} else {
if (location) {
autocompleteInput.setValue(location.properties.formatted);
selectLocation(location);
}
}
});
}
});
function selectLocation(location) {
cleanUp();
// check selected location here
selectedLocation = location;
if (location) {
locationNeedToBeClarified = (location.properties.nonVerifiedParts && location.properties.nonVerifiedParts.length) || !location.properties.housenumber || location.properties.distance > 0;
}
console.log(locationNeedToBeClarified);
zoomMap();
showLocationMarker();
updateAddressInfo();
}
function cleanUp() {
if (locationMarker) {
locationMarker.remove();
locationMarker = null;
}
locationNeedToBeClarified = false;
suggestedLocation = false;
}
function zoomMap(location) {
if (selectedLocation) {
let zoom = selectedLocation.properties.street ? 17 : 12;
zoom = Math.max(map.getZoom(), zoom);
map.flyTo([selectedLocation.properties.lat, selectedLocation.properties.lon], zoom, {
duration: 0.1
})
}
}
// add a marker with icon generated by Geoapify Marker Icon API
// https://apidocs.geoapify.com/playground/icon/
const markerWarningIcon = L.icon({
iconUrl: `https://api.geoapify.com/v1/icon/?type=awesome&color=%23ffd14e&size=large&icon=question&noWhiteCircle&scaleFactor=2&apiKey=${myAPIKey}`,
iconSize: [38, 56], // size of the icon
iconAnchor: [18, 51], // point of the icon which will correspond to marker's location
popupAnchor: [0, -57] // point from which the popup should open relative to the iconAnchor
});
const markerOkIcon = L.icon({
iconUrl: `https://api.geoapify.com/v1/icon/?type=awesome&color=%2330e970&size=large&icon=check&noWhiteCircle&scaleFactor=2&apiKey=${myAPIKey}`,
iconSize: [38, 56], // size of the icon
iconAnchor: [18, 51], // point of the icon which will correspond to marker's location
popupAnchor: [0, -57] // point from which the popup should open relative to the iconAnchor
});
function showLocationMarker() {
if (!selectedLocation) return;
locationMarker = L.marker([selectedLocation.properties.lat, selectedLocation.properties.lon], {
icon: locationNeedToBeClarified ? markerWarningIcon : markerOkIcon,
draggable: locationNeedToBeClarified
}).addTo(map);
if (locationNeedToBeClarified) {
// You can also use a tap event with a timeout for touch devices - https://stackoverflow.com/a/56427428
locationMarker.on('dragend', (event) => {
getAddressByLatLon(locationMarker.getLatLng().lat, locationMarker.getLatLng().lng).then(address => {
suggestedLocation = address;
updateAddressInfo();
});
})
}
}
function updateAddressInfo() {
const infoRow = document.getElementById("info");
const warningInfoRow = document.getElementById("warning-info");
const acceptLocationRow = document.getElementById("accept-location");
const acceptAddressRow = document.getElementById("accept-address");
const newAddress = document.getElementById("new-address");
let toHide = [infoRow, warningInfoRow, acceptLocationRow, acceptAddressRow];
let toShow = [];
if (!location) {
toHide = [warningInfoRow, acceptLocationRow, acceptAddressRow];
toShow = [infoRow];
} else if (location && !locationNeedToBeClarified) {
toHide = [infoRow, warningInfoRow, acceptLocationRow, acceptAddressRow];
} else if (location && locationNeedToBeClarified && !suggestedLocation) {
toHide = [infoRow, acceptLocationRow, acceptAddressRow];
toShow = [warningInfoRow];
} else if (location && locationNeedToBeClarified && suggestedLocation && suggestedLocation.properties.distance === 0) {
toHide = [infoRow, acceptLocationRow];
toShow = [warningInfoRow, acceptAddressRow];
newAddress.textContent = suggestedLocation.properties.formatted;
} else if (location && locationNeedToBeClarified && suggestedLocation && suggestedLocation.properties.distance > 0) {
toHide = [infoRow, acceptAddressRow];
toShow = [warningInfoRow, acceptLocationRow];
}
toHide.forEach(element => element.classList.add("hidden"));
toShow.forEach(element => element.classList.remove("hidden"));
}
function getAddressByLatLon(lat, lon) {
return fetch(`https://api.geoapify.com/v1/geocode/reverse?lat=${lat}&lon=${lon}&apiKey=${myAPIKey}`).then(result => result.json()).then(result => {
if (result && result.features && result.features.length) {
return result.features[0];
}
return null;
});
}
function acceptLocation() {
selectedLocation.properties.original_lon = selectedLocation.properties.lon;
selectedLocation.properties.lon = locationMarker.getLatLng().lng;
selectedLocation.properties.original_lat = selectedLocation.properties.lat;
selectedLocation.properties.lat = locationMarker.getLatLng().lng;
locationNeedToBeClarified = false;
suggestedLocation = null;
locationMarker.setIcon(markerOkIcon);
locationMarker.dragging.disable();
updateAddressInfo();
}
function acceptAddress() {
autocompleteInput.setValue(suggestedLocation.properties.formatted);
selectedLocation = suggestedLocation;
locationNeedToBeClarified = false;
suggestedLocation = null;
locationMarker.setIcon(markerOkIcon);
locationMarker.dragging.disable();
updateAddressInfo();
}
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.css">
<script type="text/javascript" src="https://unpkg.com/@geoapify/[email protected]/dist/index.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://unpkg.com/@geoapify/[email protected]/styles/minimal.css">
<div id="autocomplete" class="autocomplete-container"></div>
<div id="address-map" class="map-container"></div>
<div id="messages" class="messages-container">
<div id="info" class="message-row">Please enter your address in the input field below or click on the map to get the address.</div>
<div id="warning-info" class="message-row warning hidden">The house number or street is missing. Drag the marker to the correct location to clarify.</div>
<div id="accept-location" class="message-row hidden">Would you like to accept the address's new location? <button onclick="acceptLocation()">Yes</button></div>
<div id="accept-address" class="message-row hidden">You are located at the following address: <span id="new-address"></span> <br/> Do you want to accept this address? <button onclick="acceptAddress()">Yes</button></div>
</div>