v0.3, 16-10-2025 pupicon added

This commit is contained in:
2025-10-16 21:32:19 +01:00
parent fcba00eb3e
commit 95029dfe49
1097 changed files with 5244 additions and 78 deletions

210
www/index.sprites.html Normal file
View File

@@ -0,0 +1,210 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Pupmap by BoopLabs v2025-10-03_2300</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="style.css">
<link href="https://unpkg.com/maplibre-gl/dist/maplibre-gl.css" rel="stylesheet"/>
<script src="https://unpkg.com/maplibre-gl/dist/maplibre-gl.js"></script>
<script src="https://unpkg.com/pmtiles/dist/pmtiles.js"></script>
</head>
<body>
<input id="searchBox" placeholder="Search place..." />
<div id="map"></div>
<script>
const protocol = new pmtiles.Protocol();
maplibregl.addProtocol("pmtiles", protocol.tile);
const map = new maplibregl.Map({
container: 'map',
style: 'style.json',
center: [10, 60],
zoom: 4
});
map.on('load', () => {
// Roads
map.addSource('roads', { type: 'geojson', data: 'data/roads_europe_clean.geojson' });
map.addLayer({
id: 'roads-minor',
type: 'line',
source: 'roads',
filter: ['==', ['get', 'type'], 'minor'],
paint: { 'line-color': '#cccccc', 'line-width': 1, 'line-opacity': 0.5 }
});
map.addLayer({
id: 'roads-major',
type: 'line',
source: 'roads',
filter: ['==', ['get', 'type'], 'major'],
paint: { 'line-color': '#555555', 'line-width': 2, 'line-opacity': 0.7 }
});
// Cities
map.addSource('cities', { type: 'geojson', data: 'data/world_cities_europe_clean.geojson' });
map.addLayer({
id: 'city-points-small',
type: 'circle',
source: 'cities',
filter: ['<', ['get','POP_MAX'], 100000],
paint: { 'circle-radius': 3, 'circle-color': '#999999', 'circle-stroke-width': 1, 'circle-stroke-color': '#666666' }
});
map.addLayer({
id: 'city-points-medium',
type: 'circle',
source: 'cities',
filter: ['all', ['>=', ['get','POP_MAX'], 100000], ['<', ['get','POP_MAX'], 500000]],
paint: { 'circle-radius': 5, 'circle-color': '#777777', 'circle-stroke-width': 1, 'circle-stroke-color': '#555555' }
});
map.addLayer({
id: 'city-points-large',
type: 'circle',
source: 'cities',
filter: ['>=', ['get','POP_MAX'], 500000],
paint: { 'circle-radius': 7, 'circle-color': '#444444', 'circle-stroke-width': 1, 'circle-stroke-color': '#222222' }
});
// City labels
map.addLayer({
id: 'city-labels',
type: 'symbol',
source: 'cities',
layout: {
'text-field': ['get', 'NAME'],
'text-font': ['Noto Sans Regular'],
'text-size': ['interpolate', ['linear'], ['zoom'], 4, 10, 6, 12, 10, 16],
'text-offset': [0, 1.2],
'text-anchor': 'top'
},
paint: { 'text-color': '#222222', 'text-halo-color': '#ffffff', 'text-halo-width': 1 },
filter: ['>', ['get','POP_MAX'], ['interpolate', ['linear'], ['zoom'], 4, 2000000, 6, 1000000, 8, 500000, 10, 100000]]
});
// City popups
map.on('click', ['city-points-small','city-points-medium','city-points-large'], (e) => {
const coordinates = e.features[0].geometry.coordinates.slice();
const cityName = e.features[0].properties.NAME || "Unknown City";
const population = e.features[0].properties.POP_MAX || "n/a";
new maplibregl.Popup()
.setLngLat(coordinates)
.setHTML(`<strong>${cityName}</strong><br/>Population: ${population}`)
.addTo(map);
});
['city-points-small','city-points-medium','city-points-large'].forEach(layer => {
map.on('mouseenter', layer, () => { map.getCanvas().style.cursor = 'pointer'; });
map.on('mouseleave', layer, () => { map.getCanvas().style.cursor = ''; });
});
// POIs
map.addSource('pois', {
type: 'geojson',
data: 'data/pois.geojson',
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50
});
// Clusters
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'pois',
filter: ['has', 'point_count'],
paint: {
'circle-color': '#FF0000',
'circle-radius': ['step', ['get','point_count'], 15, 10, 20, 50, 25],
'circle-opacity': 0.7,
'circle-stroke-width': 1,
'circle-stroke-color': '#fff'
}
});
// Unclustered POIs with pup icon
map.loadImage('https://pups.boop.no/data/pupicon64.png', (error, image) => {
if (error) {
console.error("Failed to load pup icon", error);
return;
}
if (!map.hasImage('pup')) map.addImage('pup', image, { sdf: false });
map.addLayer({
id: 'unclustered-point',
type: 'symbol',
source: 'pois',
filter: ['!', ['has','point_count']],
layout: {
'icon-image': 'pup',
'icon-size': 1.0,
'icon-allow-overlap': true,
'icon-ignore-placement': true
}
});
});
map.loadImage('https://pups.boop.no/data/pup_icon_64x64.png', (error, image) => {
if (error) {
console.error("Failed to load pup icon", error);
return;
}
console.log("Image loaded:", image);
if (!map.hasImage('pup')) {
map.addImage('pup', image, { sdf: false });
console.log("Image added to map");
}
});
map.on('click', 'unclustered-point', (e) => {
const coordinates = e.features[0].geometry.coordinates.slice();
const popupContent = e.features[0].properties.popup || e.features[0].properties.names.join(', ');
new maplibregl.Popup()
.setLngLat(coordinates)
.setHTML(`<strong>${popupContent}</strong>`)
.addTo(map);
});
map.on('click', 'clusters', (e) => {
const features = map.queryRenderedFeatures(e.point, { layers: ['clusters'] });
const clusterId = features[0].properties.cluster_id;
map.getSource('pois').getClusterExpansionZoom(clusterId, (err, zoom) => {
if (err) return;
map.easeTo({ center: features[0].geometry.coordinates, zoom: zoom });
});
});
['clusters', 'unclustered-point'].forEach(layer => {
map.on('mouseenter', layer, () => { map.getCanvas().style.cursor = 'pointer'; });
map.on('mouseleave', layer, () => { map.getCanvas().style.cursor = ''; });
});
});
// Nominatim search
document.getElementById('searchBox').addEventListener('keypress', async function(e){
if(e.key==='Enter'){
const query = e.target.value;
const url = `http://yourhost:7070/search?q=${encodeURIComponent(query)}&format=json`;
try{
const res = await fetch(url);
const results = await res.json();
if(results.length>0){
const loc = results[0];
map.flyTo({ center: [loc.lon, loc.lat], zoom: 12 });
} else { alert("No results found"); }
} catch(err){ alert("Error connecting to search API"); console.error(err);}
}
});
</script>
</body>
</html>