Skip to content

Instantly share code, notes, and snippets.

@bamnet
Created July 22, 2019 03:57
Show Gist options
  • Save bamnet/1ca7efd437e78f4f368af2525f9b6bb1 to your computer and use it in GitHub Desktop.
Save bamnet/1ca7efd437e78f4f368af2525f9b6bb1 to your computer and use it in GitHub Desktop.
export class DistanceMatrixService extends google.maps.DistanceMatrixService {
rateLimiter = new RateLimiter(2, 'second');
static readonly maxElements = 100;
async getDistanceMatrixAsync(req: google.maps.DistanceMatrixRequest) {
// If the matrix is too big, we have to split up the requests into smaller chunks.
if (req.origins!.length * req.destinations!.length > DistanceMatrixService.maxElements) {
return this.shardedMatrix(req);
}
await this.waitForToken();
return new Promise(
(
resolve: (result: google.maps.DistanceMatrixResponse) => void,
reject: (status: google.maps.DistanceMatrixStatus) => void
) => {
super.getDistanceMatrix(req, (results, status) => {
if (status == google.maps.DistanceMatrixStatus.OK) {
resolve(results);
} else {
reject(status);
}
});
});
}
private async shardedMatrix(originalReq: google.maps.DistanceMatrixRequest) {
// TODO(???): Handle the scenario where we should shard on origins instead of destinations.
const origins = originalReq.origins!.length;
const destinations = originalReq.destinations!.length;
const requests = Math.ceil(origins * destinations / DistanceMatrixService.maxElements);
const interval = Math.floor(DistanceMatrixService.maxElements / origins);
const result: google.maps.DistanceMatrixResponse = {
originAddresses: new Array(origins),
destinationAddresses: new Array(destinations),
rows: [],
};
for (let i = 0; i < requests; i++) {
const lowerBound = i * interval;
const upperBound = Math.min(((i + 1) * interval), destinations);
const req = Object.assign({}, originalReq);
req.destinations = req.destinations!.slice(lowerBound, upperBound);
const subset = await this.getDistanceMatrixAsync(req);
if (subset) {
result.originAddresses = subset.originAddresses;
subset.destinationAddresses.forEach((a, j) => {
result.destinationAddresses[lowerBound + j] = a;
});
subset.rows.forEach((row, r) => {
row.elements.forEach((e, j) => {
if (!result.rows[r]) {
result.rows[r] = { elements: [] };
}
result.rows[r].elements[lowerBound + j] = e;
});
});
}
await this.sleep(10000);
}
return result;
}
private async waitForToken() {
return new Promise((resolve) => {
this.rateLimiter.removeTokens(100, () => resolve());
});
}
async sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment