25
loading...
This website collects cookies to deliver better user experience
<div>
element with id="map"
- this will occupy the entire window and hold our rendered vector tile map. We also have a <div class="map-overlay">
element, which contains some helper text, our simple form and submit button, and a table where we will visualise the metadata associated with a particular address.js/main.js
, we have the code required to:config.apikey
to an OS Data Hub API key copied from a project with the OS Places API added. (Take care to set your project to "Developer Mode" until you push it to production!)// API Key in config object
var config = {
apikey: "<API KEY HERE>"
};
// Endpoints
const endpoints = {
places: 'https://api.os.uk/search/places/v1',
vectorTile: 'https://api.os.uk/maps/vector/v1/vts'
}
map
object with the maplibregl.Map
constructor, passing in an object literal with our configuration parameters. We'll define the id of the container
div (map
, from the HTML), minZoom
(i.e. how far out the user can zoom), the style
endpoint with key, the zoom
and center
of the viewport and a transformation to apply to the URL, which adds the srs
so we fetch tiles from the right Spatial Reference System. We also add navigation controls.// Initialise the map object.
var map = new maplibregl.Map({
container: 'map',
minZoom: 6,
// maxZoom: 18,
style: endpoints.vectorTile + '/resources/styles?key=' + config.apikey,
center: [-2.968, 54.425],
zoom: 13,
transformRequest: url => {
return {
url: url + '&srs=3857'
}
}
});
// Add navigation control (excluding compass button) to the map.
map.addControl(new maplibregl.NavigationControl());
map.on('style.load', function () {...
callback function - otherwise the program will try to duplicate a layer that doesn't exist yet.map.on("style.load", function () {
// Duplicate 'OS/TopographicArea_1/Building/1' layer to extrude the buildings
// in 3D using the Building Height Attribute (RelHMax) value.
map.addLayer({
"id": "OS/TopographicArea_1/Building/1_3D",
"type": "fill-extrusion",
"source": "esri",
"source-layer": "TopographicArea_1",
"filter": [
"==",
"_symbol",
33
],
"minzoom": 16,
"layout": {},
"paint": {
"fill-extrusion-color": "#DCD7C6",
"fill-extrusion-opacity": 0.5,
"fill-extrusion-height": [
"interpolate",
["linear"],
["zoom"],
16,
0,
16.05,
["get", "RelHMax"]
]
}
});
// Here we add the highlighted layer, with all buildings filtered out.
// We'll set the filter to our searched buildings when we actually
// call the OS Places API and have a TOID to highlight.
map.addLayer({
"id": "OS/TopographicArea_1/Building/1_3D-highlighted",
"type": "fill-extrusion",
"source": "esri",
"source-layer": "TopographicArea_1",
"filter": ["in", "TOID", ""],
"minzoom": 16,
"layout": {},
"paint": {
"fill-extrusion-color": "#FF1F5B",
"fill-extrusion-opacity": 1,
"fill-extrusion-height": [
"interpolate",
["linear"],
["zoom"],
16,
0,
16.05,
["get", "RelHMax"]
],
}
});
});
submit
event listener to the <form>
element - this means each time "Submit" is pressed and a submit
event fires, lookUpAddress
will be called.var form = document.getElementById("the-form");
form.addEventListener('submit', lookUpAddress);
lookUpAddress();
? Great question.'address-text'
element - i.e. the text input form field.fetchAddressFromPlaces(placesResponse);
, which will call the OS Places API and return the parsed JSON response.fetchAddressFromPlaces()
address
string retrieved from the form input field - with this we construct a URL with query
, our desired reference system (output_srs
), a maxresults
limit of 1 and the API key
as parameters.fetch
API to send an HTTP request, and parse the response with the .json()
method. The function simply returns the parsed JSON exactly as received from the API endpoint.async function fetchAddressFromPlaces(address) {
let url = endpoints.places + `/find?query=${encodeURIComponent(address)}&maxresults=1&output_srs=EPSG:4326&key=${config.apikey}`;
let res = await fetch(url);
let json = await res.json()
return json;
}
NOTE: Here we're simply fetching the first item the OS Places API matches with the query string. In production it might require a bit more user input or other analysis to make sure you actually do have the right address.
innerText
of the respective HTML elements.function updateInfoBox(placesResponse) {
let addressString, UPRN, TOID, longitude, latitude;
addressString = placesResponse.results[0].DPA.ADDRESS;
UPRN = placesResponse.results[0].DPA.UPRN;
TOID = placesResponse.results[0].DPA.TOPOGRAPHY_LAYER_TOID;
longitude = placesResponse.results[0].DPA.LNG;
latitude = placesResponse.results[0].DPA.LAT;
document.getElementById('address').innerText = addressString;
document.getElementById('uprn').innerText = UPRN;
document.getElementById('toid').innerText = TOID;
document.getElementById('longitude').innerHTML = longitude;
document.getElementById('latitude').innerHTML = latitude;
}
flyToCoords(coords)
rotateTo
animation so the camera flies around the building when the fly-to finishes:async function flyToCoords(coords) {
// @TIM TODO does this need to be set each function call, or just once?
map.once('moveend', function () {
map.rotateTo(0.0, { duration: 7000 });
});
map.flyTo({
center: coords,
zoom: 17.5,
pitch: 75,
bearing: 180
});
}
highlightTOID(toidArray)
#FF1F5B
- a vivid pink.function highlightTOID(toid) {
let filter = ["in", "TOID", toid];
map.setFilter("OS/TopographicArea_1/Building/1_3D-highlighted", filter);
}
25