Public
Edited
Jan 24, 2023
Insert cell
Insert cell
{
let container = html`<div style='height:600px;' />`;

// Give the container dimensions.
yield container;

// set the targomo client
const client = tgmCoreClient("westcentraleurope");

// Create the "map" object with the mapboxgl.Map constructor, referencing
// the container div
let map = new mapboxgl.Map({
container,
center: [13.3694, 52.5251],
zoom: 13,
minZoom: 11,
style: client.basemaps.getGLStyleURL("Basic"),
scrollZoom: false,
attributionControl: false
})
.addControl(new mapboxgl.NavigationControl())
.addControl(
new mapboxgl.AttributionControl({
compact: true,
customAttribution: attribution
})
);

// get bounding box info from the current map view
const getBbox = () => {
const { _sw: southWest, _ne: northEast } = map.getBounds();
return { southWest, northEast };
};

const getPlaces = async (bounds) => {
// get public transit assets from Targomo, based on the bbox
const places = await client.pois.boundingBox(bounds, {
group: ["public_transport_station", "bus_stop", "tram_stop"]
});

const placesGeoJSON = turf.featureCollection(
// iterate the returned POIs to categorize
places.map((p) => {
const { groupIds, tags } = p;
const group = groupIds.includes("bus_stop") // is listed as a bus stop
? "bus"
: groupIds.includes("tram_stop") // is listed as a bus stop
? "rail-light"
: tags.ferry === "yes" // is listed as a public transport station, of type ferry
? "ferry"
: tags.subway === "yes" // is listed as a public transport station, of type subway
? "rail-metro"
: "rail"; // is listed as a public transport station, of all remaining types
// build a point for each
return turf.point([p.lng, p.lat], {
tags,
group: group
});
})
);
return placesGeoJSON;
};

map.on("load", async () => {
// pre-load icons from targomo icon library for use in symbols
for (let icon of [
"tgm-bus",
"tgm-rail",
"tgm-rail-light",
"tgm-rail-metro",
"tgm-ferry",
"tgm-square"
]) {
map.loadImage(
`https://releases.targomo.com/tgm-icons/images/${icon}.png`,
(error, image) => {
if (error) throw error;
if (!map.hasImage(icon)) map.addImage(icon, image, { sdf: true });
}
);
}

// add source for the POIs
map.addSource("pois", {
type: "geojson",
data: turf.featureCollection([]),
cluster: true,
clusterMaxZoom: 16, // Max zoom to cluster points on
clusterRadius: 50 // Radius of each cluster when clustering points
});

// badge for non-clustered POIs
map.addLayer({
id: "single-badge",
type: "symbol",
source: "pois",
filter: ["all", ["!", ["has", "point_count"]]], // not clustered
layout: {
"icon-image": "tgm-square",
"icon-size": 0.25,
"icon-allow-overlap": true
},
paint: {
"icon-color": "#2871cc"
}
});

// icon for non-clustered POIs
map.addLayer({
id: "single",
type: "symbol",
source: "pois",
filter: ["all", ["!", ["has", "point_count"]]], // not clustered
layout: {
"icon-image": "tgm-{group}", // icon name matches the classification done in `getPlaces()`
"icon-size": 0.15,
"icon-allow-overlap": true
},
paint: {
"icon-color": "#fff"
}
});

// icon for clustered POIs
map.addLayer({
id: "multi",
type: "symbol",
source: "pois",
filter: ["has", "point_count"], // has a count, therefore clustered
layout: {
"text-field": ["get", "point_count"], // label with the cluter number
"text-size": 8,
"text-allow-overlap": true,
"text-font": ["Open Sans Bold"],
"icon-image": "tgm-square",
"icon-size": 0.25
},
paint: {
"icon-color": "#003f9a",
"text-color": "#fff"
}
});

const POIs = await getPlaces(getBbox());
// set the POI data
map.getSource("pois").setData(POIs);
});

map.on("moveend", async () => {
const POIs = await getPlaces(getBbox());
// set the POI data
map.getSource("pois").setData(POIs);
});
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more