Using Map Attributes API (formerly Fleet Telematics API) to extract map data with JavaScript
Extracting map data using Map Attributes API (formerly Fleet Telematics API) with JavaScript code snippets
Since the Fleet Telematics functionality in the HERE JS API 3.1.x library has been deprecated (code snippet of the previous usage), please use simple JavaScript implementations for extracting map data using the Map Attributes API instead.
See please JavaScript code snippets (retrieve speed limits layers) below:
Search in bounding box<br /> const retrieveSpeedLimitsInBbox = () => { const fcs = [1, 2, 3, 4, 5]; const layers = ["SPEED_LIMITS", "SPEED_LIMITS_VAR", "SPEED_LIMITS_COND", "TRUCK_SPEED_LIMITS"]; var allResp = []; var layerIds = []; layers.forEach((layer) => { fcs.forEach((fc) => { layerIds.push([layer, "_FC", fc].join("")); }); }); const bbox = map.getViewModel().getLookAtData().bounds.getBoundingBox(); const bboxPrm = encodeURIComponent(`bbox:${bbox.getLeft()},${bbox.getBottom()},${bbox.getRight()},${bbox.getTop()}`); let cntReqs = layerIds.length; layerIds.forEach(layerId => { let url = `https://smap.hereapi.com/v8/maps/attributes?layers=${layerId}∈=${bboxPrm}&apikey=${apikey}&meta=1` fetch(url) .then(r => { if (!r.ok) { throw new Error('Response was not ok'); } return r.json(); }) .then(d => { parseData(d, (--cntReqs == 0)); }) .catch(error => console.error('There was a problem with your fetch operation:', error)); }); function parseData(r, isLast){ allResp = [...allResp, r]; if (isLast) { parsed = allResp.reduce((accum, currV) => { let layerId = currV.meta[0].layerId; currV.geometries.forEach(geom => { let row = geom.attributes; accum[row.LINK_ID] = accum[row.LINK_ID] || {}; //below analyse attributes from 'row' object accordingly 'layerId' and //save them to 'accum[row.LINK_ID]' and //at the same time calculating a speed limit accordingly previouse saved attributes in accum[row.LINK_ID] //like: if(layerId.startsWith("SPEED_LIMITS_FC")){ accum[row.LINK_ID].FROM_REF_SPEED_LIMIT = row.FROM_REF_SPEED_LIMIT; accum[row.LINK_ID].TO_REF_SPEED_LIMIT = row.TO_REF_SPEED_LIMIT; }else if(layerId.startsWith("SPEED_LIMITS_VAR_FC")){ accum[row.LINK_ID].DIRECTION = row.DIRECTION; accum[row.LINK_ID].DATE_TIMES = row.DATE_TIMES; }else if(layerId.startsWith("SPEED_LIMITS_COND_FC")){ accum[row.LINK_ID].DATE_TIMES = row.DATE_TIMES; accum[row.LINK_ID].SPEED_LIMIT = row.SPEED_LIMIT; }else if(layerId.startsWith("TRUCK_SPEED_LIMITS_FC")){ accum[row.LINK_ID].TRUCK_FROM_REF_SPEED_LIMIT = row.FROM_REF_SPEED_LIMIT; accum[row.LINK_ID].TRUCK_TO_REF_SPEED_LIMIT = row.TO_REF_SPEED_LIMIT; accum[row.LINK_ID].TRAILER = row.TRAILER; } }); return accum; }, {}); console.log("parsed:", parsed); } } };<br />
Serach in tiles<br /> var tileIds = {}; const retrieveSpeedLimitsInTile = () => { const lookAt = map.getViewModel().getLookAtData(); const bbox = lookAt.bounds.getBoundingBox(); if(Math.floor(lookAt.zoom) != 15){//don't request map attributes if cuurent zoom level of map is not 15 return; } const pointTopLeft = {lat: bbox.getTop(), lng: bbox.getLeft()}; const pointBottomRight = {lat: bbox.getBottom(), lng: bbox.getRight()}; const fcs = [1, 2, 3, 4, 5]; const layers = ["SPEED_LIMITS", "SPEED_LIMITS_VAR", "SPEED_LIMITS_COND", "TRUCK_SPEED_LIMITS"]; var allResp = []; var layerIds = []; var arrGridTidsAllFcs = []; fcs.forEach((fc) => { let level = fc + 8; let tileXYTopLeft = getTileXY(pointTopLeft, level); let tileXYBottomRight = getTileXY(pointBottomRight, level); let arrGridTileIds = calcGridTileIds(tileXYTopLeft, tileXYBottomRight, level); arrGridTidsAllFcs = [...arrGridTidsAllFcs, ...arrGridTileIds]; arrGridTileIds.forEach((tileId) => { tileIds[tileId] = { "fc": fc }; tileIds[tileId]["layers"] = layers.map((layer) => [layer, "_FC", fc].join("")); }); }); const arrGridTileIdsBy64 = splitArrayEqually(arrGridTidsAllFcs, Math.floor( 64 / layers.length)); const baseUrl = `https://smap.hereapi.com/v8/maps/attributes?&apikey=${apikey}&meta=1`; const inTile = "in=" + encodeURIComponent("tile:"); let cntReqs = arrGridTileIdsBy64.length; arrGridTileIdsBy64.forEach(item64 => { let urlTiles = []; let urllayerIds = []; item64.forEach(tilId => { let tiles = Array.from({ length: tileIds[tilId].layers.length }, () => tilId); urlTiles = [...urlTiles, ...tiles]; urllayerIds = [...urllayerIds, ...tileIds[tilId].layers]; }); let url = [`${baseUrl}`, `layers=${urllayerIds.join(",")}`, `${inTile}${urlTiles.join(",")}`].join("&"); fetch(url) .then(r => { if (!r.ok) { throw new Error('Response was not ok'); } return r.json(); }) .then(d => { parseData(d, (--cntReqs == 0)); }) .catch(error => console.error('There was a problem with your fetch operation:', error)); }); function parseData(r, isLast){ allResp = [...allResp, ...r.Tiles]; var parsed; if (isLast) { parsed = allResp.reduce((accum, currV) => { let layerId = currV.Meta.layerName; currV.Rows.forEach(row => { accum[row.LINK_ID] = accum[row.LINK_ID] || {}; //below analyse attributes from 'row' object accordingly 'layerId' and //save them to 'accum[row.LINK_ID]' and //at the same time calculating a speed limit accordingly previouse saved attributes in accum[row.LINK_ID] //like: if(layerId.startsWith("SPEED_LIMITS_FC")){ accum[row.LINK_ID].FROM_REF_SPEED_LIMIT = row.FROM_REF_SPEED_LIMIT; accum[row.LINK_ID].TO_REF_SPEED_LIMIT = row.TO_REF_SPEED_LIMIT; }else if(layerId.startsWith("SPEED_LIMITS_VAR_FC")){ accum[row.LINK_ID].DIRECTION = row.DIRECTION; accum[row.LINK_ID].DATE_TIMES = row.DATE_TIMES; }else if(layerId.startsWith("SPEED_LIMITS_COND_FC")){ accum[row.LINK_ID].DATE_TIMES = row.DATE_TIMES; accum[row.LINK_ID].SPEED_LIMIT = row.SPEED_LIMIT; }else if(layerId.startsWith("TRUCK_SPEED_LIMITS_FC")){ accum[row.LINK_ID].TRUCK_FROM_REF_SPEED_LIMIT = row.FROM_REF_SPEED_LIMIT; accum[row.LINK_ID].TRUCK_TO_REF_SPEED_LIMIT = row.TO_REF_SPEED_LIMIT; accum[row.LINK_ID].TRAILER = row.TRAILER; } }); return accum; }, {}); console.log("parsed:", parsed); } } //calculte all grid of tileIds for given top left tileXY number and bottom right tileXY function calcGridTileIds(tileXYTopLeft, tileXYBottomRight, level) { const cntXs = tileXYBottomRight.tileX - tileXYTopLeft.tileX + 1; const cntYs = tileXYTopLeft.tileY - tileXYBottomRight.tileY + 1; const arrXs = Array.from({ length: cntXs }, (_, index) => index + tileXYTopLeft.tileX); const arrYs = Array.from({ length: cntYs }, (_, index) => index + tileXYBottomRight.tileY); let arrGridTileIds = []; arrXs.forEach(function(itemX, idx) { arrYs.forEach(function(itemY, idx) { let tileId = getTileId({ tileX: itemX, tileY: itemY }, level); if(!tileIds[tileId]){// we will not send request for already fetched tileId arrGridTileIds.push(tileId); } }); }); return arrGridTileIds; } function getTileXY(point, level){ var degSize = 180 / Math.pow(2, level), tileY = Math.floor((point.lat + 90) / degSize), tileX = Math.floor((point.lng + 180) / degSize); return {tileX: tileX, tileY: tileY}; } function getTileId(tileXY, level){ return (tileXY.tileY * 2 * Math.pow(2, level) + tileXY.tileX); } function splitArrayEqually(array, chunkSize) { const totalElements = array.length; const numOfChunks = Math.ceil(totalElements / chunkSize); const idealSize = Math.ceil(totalElements / numOfChunks); let result = []; for (let i = 0; i < numOfChunks; i++) { let start = i * idealSize; let end = start + idealSize; result.push(array.slice(start, Math.min(end, totalElements))); } return result; } };<br />