patternjavascriptMinor
Plotting JSON locations using Google Road API with Javascript Promise chaining
Viewed 0 times
locationsgooglewithplottingjavascriptchainingpromiseusingjsonroad
Problem
I wrote this code for an personal application, just to help me visualize some data related to driving. I have one JSON on my machine, this JSON contains a set of locations (lat, lngs) and every location has a category (integer number).
After I fetch this file I want to use Google's Road API, to snap the points on the road. Unfortunately it only allows 100 points per request. And depending on the "track" I can have usually 400+ points.
At this point, this application is only maintained by me. But I used this as an excuse to re-learn Javascript (including the new stuff only new browsers have).
I used Promises and I was used to the callback hell of before and after reading so much good stuff about Promises I got really frustrated seeing this huge chaining I made.
It's working, can't complain about that.
But, looks messy.
``
}
return Promise.all(requests)
.then(responses => Promise.all(responses.map(response => response.json())))
.then(function(jsons){
After I fetch this file I want to use Google's Road API, to snap the points on the road. Unfortunately it only allows 100 points per request. And depending on the "track" I can have usually 400+ points.
At this point, this application is only maintained by me. But I used this as an excuse to re-learn Javascript (including the new stuff only new browsers have).
I used Promises and I was used to the callback hell of before and after reading so much good stuff about Promises I got really frustrated seeing this huge chaining I made.
It's working, can't complain about that.
But, looks messy.
``
document.addEventListener('DOMContentLoaded', function(evt){
'use strict';
const apiKey = '-----------';
let rawMap = new google.maps.Map(document.getElementById('rawMap'), {
center: {lat: 0, lng: 0},
zoom: 10
});
let snappedMap = new google.maps.Map(document.getElementById('snappedMap'), {
center: {lat: 0, lng: 0},
zoom: 10
});
const colors = [
'#FF0000',
'#00FF00',
'#0000FF',
'#FFFF00',
'#00FFFF',
'#FF00FF',
'#000000',
];
fetch('output.json').then(response => response.json()).then(function(points){
for (let i = 0 ; i bounds.extend(point));
rawMap.fitBounds(bounds)
snappedMap.fitBounds(bounds);
let requests = [];
for (let i = 0; i ${point['lat']},${point['lng']}).join('|'));
requests.push(fetch(https://roads.googleapis.com/v1/snapToRoads?${params}`));}
return Promise.all(requests)
.then(responses => Promise.all(responses.map(response => response.json())))
.then(function(jsons){
Solution
- Divide processing in simple functions, then chain them in linear fashion.
- Use literal
.propertyor['property']consistently, don't mix them as it's the same in JS
- Concatenate arrays in one operation via
Array.prototype.concat.apply([], arrayOfArrays)instead ofreducethat creates intermediate arrays (it's slow and memory-hogging). However, if the number of elements may exceed 32k (the max number of arguments in some browsers) it's safer toconcatin chunks of 10k by using a loop and.slice.
// your init partlet points;const getJSON = (response) => response.json();const drawPoints = (json) => {
// will be used later for snapping to roads
points = json;
for (let i = 0; i bounds.extend(point));
rawMap.fitBounds(bounds);
snappedMap.fitBounds(bounds);
};const requestSnapToRoads = () => {
let requests = [];
for (let i = 0; i `${point.lat},${point.lng}`)
.join('|'));
requests.push(fetch(`https://roads.googleapis.com/v1/snapToRoads?${params}`));
}
return Promise.all(requests).then(responses => Promise.all(responses.map(getJSON)));
};const applySnapToRoads = (jsons) => {
let lastIndex = -1;
let allSnappedPoints = jsons.map((json, i) => {
for (let snappedPoint of json.snappedPoints) {
if ('originalIndex' in snappedPoint) {
lastIndex = i * 100 + snappedPoint.originalIndex;
}
snappedPoint.cat = points[lastIndex].cat;
}
return json.snappedPoints;
});
return [].concat.apply([], allSnappedPoints);
};const drawSnappedPoints = (snappedPoints) => {
const getPointAt = (index) => ({
lat: snappedPoints[index].location.latitude,
lng: snappedPoints[index].location.longitude
});
for (let i = 0; i < snappedPoints.length - 1; i++) {
let path = new google.maps.Polyline({
path: [getPointAt(i), getPointAt(i + 1)],
strokeWeight: 5,
strokeColor: colors[snappedPoints[i].cat]
});
path.setMap(snappedMap);
}
};// chain them!
fetch('output.json')
.then(getJSON)
.then(drawPoints)
.then(requestSnapToRoads)
.then(applySnapToRoads)
.then(drawSnappedPoints)
.catch(console.log);Code Snippets
// your init partlet points;const getJSON = (response) => response.json();const drawPoints = (json) => {
// will be used later for snapping to roads
points = json;
for (let i = 0; i < points.length - 1; ++i) {
let path = new google.maps.Polyline({
path: [points[i], points[i + 1]],
strokeColor: colors[points[i].cat],
strokeWeight: 5
});
path.setMap(rawMap);
}
// fit map bounds to the points
let bounds = new google.maps.LatLngBounds();
points.forEach(point => bounds.extend(point));
rawMap.fitBounds(bounds);
snappedMap.fitBounds(bounds);
};const requestSnapToRoads = () => {
let requests = [];
for (let i = 0; i < points.length; i += 100) {
let params = new URLSearchParams();
params.append('interpolate', 'true');
params.append('key', apiKey)
params.append('path', points
.slice(i, i + 100)
.map(point => `${point.lat},${point.lng}`)
.join('|'));
requests.push(fetch(`https://roads.googleapis.com/v1/snapToRoads?${params}`));
}
return Promise.all(requests).then(responses => Promise.all(responses.map(getJSON)));
};Context
StackExchange Code Review Q#145616, answer score: 2
Revisions (0)
No revisions yet.