Get POI information from MapTiler, OpenStreetMap (OSM), and Wikidata data by clicking on the map.
Click on any POI on the map to get the information.
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Offset the vanishing point using padding</title>
<script src="https://cdn.maptiler.com/maptiler-sdk-js/v3.0.1/maptiler-sdk.umd.min.js"></script>
<link href="https://cdn.maptiler.com/maptiler-sdk-js/v3.0.1/maptiler-sdk.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-md5/2.19.0/js/md5.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<style>
body {margin: 0; padding: 0;}
#map {position: absolute; top: 0; bottom: 0; width: 100%;}
.rounded-rect {
background: white;
border-radius: 10px;
box-shadow: 0 0 50px -25px black;
}
.flex-center {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
}
.flex-center.left {
left: 0px;
}
.sidebar-content-info {
position: absolute;
top: 0px;
font-size: 1rem;
padding: 16px;
box-sizing: border-box;
width: 100%;
word-break: break-word;
overflow-y: auto;
height: 100%;
}
.sidebar-content-info img {
width: 100%;
}
.sidebar-content-info h1 {
line-height: 1em;
}
.sidebar-content-info label {
margin-right: 8px;
color: #6B7C92;
font-weight: 600;
}
.sidebar-content-info .details-info {
font-size: 0.8em;
}
.sidebar-content {
position: absolute;
width: 95%;
height: 95%;
font-family: Arial, Helvetica, sans-serif;
font-size: 32px;
color: #AEB6C7;
}
.sidebar-toggle {
position: absolute;
width: 1.3em;
height: 1.3em;
overflow: visible;
display: flex;
justify-content: center;
align-items: center;
}
.sidebar-toggle.left {
right: -1.5em;
}
.sidebar.left .sidebar-toggle.left .icon {
background-image: url("data:image/svg+xml,%3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Ctitle%3Ekeyboard_arrow_left%3C/title%3E%3Cpath fill='currentColor' d='M15.422 16.594l-1.406 1.406-6-6 6-6 1.406 1.406-4.594 4.594z'%3E%3C/path%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: center;
}
.sidebar.left.collapsed .sidebar-toggle.left .icon {
background-image: url("data:image/svg+xml,%3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Ctitle%3Ekeyboard_arrow_right%3C/title%3E%3Cpath fill='currentColor' d='M8.578 16.594l4.594-4.594-4.594-4.594 1.406-1.406 6 6-6 6z'%3E%3C/path%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: center;
}
.icon {
display: inline-block;
width: 70%;
height: 70%;
background-size: cover;
}
.icon:hover {
filter: invert(71%) sepia(45%) saturate(7285%) hue-rotate(158deg) brightness(90%) contrast(92%);
}
.sidebar-toggle:hover {
color: #0aa1cf;
cursor: pointer;
}
.sidebar {
transition: transform 1s;
z-index: 1;
width: 300px;
height: 100%;
}
.left.collapsed {
transform: translateX(-295px);
}
.right.collapsed {
transform: translateX(295px);
}
</style>
</head>
<body>
<div id="map">
<div id="left" class="sidebar flex-center left collapsed">
<div class="sidebar-content rounded-rect flex-center">
<div class="sidebar-content-info">Left Sidebar</div>
<div
class="sidebar-toggle rounded-rect left"
>
<span class="icon"></span>
</div>
</div>
</div>
</div>
<script type="module">
import queryString from 'https://cdn.jsdelivr.net/npm/query-string@8.1.0/+esm'
maptilersdk.config.apiKey = 'YOUR_MAPTILER_API_KEY_HERE';
var center = [-73.986281, 40.74241];
var map = new maptilersdk.Map({
container: 'map',
zoom: 16,
center: center,
style: maptilersdk.MapStyle.STREETS
});
const marker = new maptilersdk.Marker();
function toggleSidebar(id) {
var elem = document.getElementById(id);
var classes = elem.className.split(' ');
var collapsed = classes.indexOf('collapsed') !== -1;
var padding = {};
if (collapsed) {
classes.splice(classes.indexOf('collapsed'), 1);
padding[id] = 300;
map.easeTo({
padding: padding,
duration: 1000
});
} else {
padding[id] = 0;
classes.push('collapsed');
map.easeTo({
padding: padding,
duration: 1000
});
}
elem.className = classes.join(' ');
}
function showSidebar(id) {
var elem = document.getElementById(id);
var classes = elem.className.split(' ');
var collapsed = classes.indexOf('collapsed') !== -1;
var padding = {};
if (collapsed) {
classes.splice(classes.indexOf('collapsed'), 1);
padding[id] = 300;
map.easeTo({
padding: padding,
duration: 1000
});
elem.className = classes.join(' ');
}
}
map.on('load', function () {
toggleSidebar('left');
map.on('click', async function(e) {
const features = map.queryRenderedFeatures(e.point, {
layers: ['Public', 'Sport', 'Tourism', 'Culture', 'Education', 'Shopping', 'Food',
'Transport', 'Park', 'Healthcare', 'Station']
});
if (features.length > 0) {
getInfoFromLngLat(e.lngLat, features[0]);
}
});
});
async function getInfoFromLngLat(lngLat, feature) {
marker.setLngLat(lngLat).addTo(map);
const osmInfo = await getOMSInfo(feature.id);
const wikidata = await getWikidata(osmInfo?.tags);
showPoiInfo(feature, osmInfo, wikidata);
}
async function getOMSInfo(id) {
const query = queryString.stringify({
data: `[out:json][timeout:25];
node(${id/10});
out tags;`
});
const response = await fetch(`https://overpass-api.de/api/interpreter?${query}`, {
redirect: 'follow',
headers: {
accept: 'application/json'
},
});
const info = await response.json();
return info?.elements[0] ? info.elements[0] : null;
}
function getWikidataId(tags) {
if (!tags) return null;
const regexWikidata = /(\w*:)?wikidata/;
const key = Object.keys(tags).find(key => key.match(regexWikidata));
return tags[key] ? tags[key] : null;
}
function getWikidataImageHash(name) {
const imageHash = md5(name);
return imageHash;
}
function getWikidataImagePath(image_name) {
const name = image_name.replace(/\s+/g, '_');
const hash = getWikidataImageHash(name);
return `https://upload.wikimedia.org/wikipedia/commons/${hash.substring(0,1)}/${hash.substring(0,2)}/${name}`;
}
async function getWikidata(tags) {
const id = getWikidataId(tags);
if (!id) return null;
const response = await fetch(`https://www.wikidata.org/wiki/Special:EntityData/${id}.json?flavor=simple`);
const info = await response.json();
let image;
if (info.entities[id].claims.P154) {
image = getWikidataImagePath(info.entities[id].claims.P154[0].mainsnak.datavalue.value);
}else if (info.entities[id].claims.P18) {
image = getWikidataImagePath(info.entities[id].claims.P18[0].mainsnak.datavalue.value);
}
return image ? {...info.entities[id], ...{image}} : info.entities[id];
}
function showPoiInfo(feature, osmInfo, wikidata) {
const textHtml = [];
if (wikidata?.image) {
textHtml.push(`<img src="${wikidata?.image}" />`);
}
textHtml.push(`<h1>${feature.properties.name}</h1>`);
if (feature.properties.class !== feature.properties.subclass) {
textHtml.push(`<h4>${feature.properties.class} (<small>${feature.properties.subclass}</small>)</h4>`);
} else {
textHtml.push(`<h4>${feature.properties.class}</h4>`);
}
if (osmInfo && osmInfo?.tags) {
const {opening_hours, "contact:website": contact_website, website, ...tags } = osmInfo.tags;
if (contact_website || website) {
let web = website ?? contact_website;
textHtml.push(`<div><a href="${web}">${web}</a></div>`);
}
if (opening_hours) {
textHtml.push(`<h3>Opening hours</h3>`);
osmInfo?.tags?.opening_hours.split(",").forEach(element => {
textHtml.push(`<div>${element.trim()}</div>`);
});
}
if (tags) {
textHtml.push(`<h3>Details</h3>`);
textHtml.push(`<div class="details-info">`);
Object.keys(tags).forEach(element => {
if (element.includes("email")) {
textHtml.push(`<div><label>${element}:</label><a href="mailto: ${tags[element]}">${tags[element]}</a></div>`);
} else if (element.includes("website")) {
textHtml.push(`<div><label>${element}:</label><a href="${tags[element]}">${tags[element]}</a></div>`);
} else if (element.includes("wikidata")) {
textHtml.push(`<div><label>${element}:</label><a href="https://www.wikidata.org/wiki/${tags[element]}">${tags[element]}</a></div>`);
} else if (element.includes("wikipedia")) {
const [lang, term] = tags[element].split(":");
textHtml.push(`<div><label>${element}:</label><a href="https://${lang}.wikipedia.org/wiki/${term}">${tags[element]}</a></div>`);
} else {
textHtml.push(`<div><label>${element}:</label>${tags[element]}</div>`);
}
});
textHtml.push(`</div>`);
}
}
document.querySelector(".sidebar-content-info").innerHTML = textHtml.join("");
showSidebar('left');
}
document.querySelector(".sidebar-toggle").addEventListener('click', function() {
toggleSidebar('left');
});
</script>
</body>
</html>
An extension of MapLibre GL JS