From 21372d7ad4d18625d59d2ea85c522fc99e8cf06d Mon Sep 17 00:00:00 2001 From: Grigory Dobrov Date: Fri, 1 Jul 2022 16:43:04 +0300 Subject: [PATCH 1/3] support for contour_meters Isochrone API param mapbox/playground#193 --- docs/services.md | 5 +- services/__tests__/isochrone.test.js | 70 +++++++++++++++++++++++++++- services/isochrone.js | 40 ++++++++++++---- 3 files changed, 103 insertions(+), 12 deletions(-) diff --git a/docs/services.md b/docs/services.md index fb63abb8..3ada0211 100644 --- a/docs/services.md +++ b/docs/services.md @@ -2134,8 +2134,9 @@ Given a location and a routing profile, retrieve up to four isochrone contours - `config` **[Object][200]** - `config.profile` **(`"driving"` \| `"walking"` \| `"cycling"`)** A Mapbox Directions routing profile ID. (optional, default `"driving"`) - `config.coordinates` **[Coordinates][232]** A {longitude,latitude} coordinate pair around which to center the isochrone lines. - - `config.minutes` **[Array][210]<[number][205]>** The times in minutes to use for each isochrone contour. You can specify up to four contours. Times must be in increasing order. The maximum time that can be specified is 60 minutes. - - `config.colors` **[Array][210]<[string][201]>?** The colors to use for each isochrone contour, specified as hex values without a leading # (for example, ff0000 for red). If this parameter is used, there must be the same number of colors as there are entries in contours_minutes. If no colors are specified, the Isochrone API will assign a default rainbow color scheme to the output. + - `config.minutes` **[Array][210]<[number][205]>?** The times in minutes to use for each isochrone contour. You can specify up to four contours. Times must be in increasing order. The maximum time that can be specified is 60 minutes. Setting minutes and meters in the same time is an error. + - `config.meters` **[Array][210]<[number][205]>?** The distances in meters to use for each isochrone contour. You can specify up to four contours. Distances must be in increasing order. The maximum distance that can be specified is 100000 meters. Setting minutes and meters in the same time is an error. + - `config.colors` **[Array][210]<[string][201]>?** The colors to use for each isochrone contour, specified as hex values without a leading # (for example, ff0000 for red). If this parameter is used, there must be the same number of colors as there are entries in contours_minutes or contours_meters. If no colors are specified, the Isochrone API will assign a default rainbow color scheme to the output. - `config.polygons` **[boolean][202]?** Specify whether to return the contours as GeoJSON polygons (true) or linestrings (false, default). When polygons=true, any contour that forms a ring is returned as a polygon. - `config.denoise` **[number][205]?** A floating point value from 0.0 to 1.0 that can be used to remove smaller contours. The default is 1.0. A value of 1.0 will only return the largest contour for a given time value. A value of 0.5 drops any contours that are less than half the area of the largest contour in the set of contours for that same time value. - `config.generalize` **[number][205]?** A positive floating point value in meters used as the tolerance for Douglas-Peucker generalization. There is no upper bound. If no value is specified in the request, the Isochrone API will choose the most optimized generalization to use for the request. Note that the generalization of contours can lead to self-intersections, as well as intersections of adjacent contours. diff --git a/services/__tests__/isochrone.test.js b/services/__tests__/isochrone.test.js index 6657e669..20166d55 100644 --- a/services/__tests__/isochrone.test.js +++ b/services/__tests__/isochrone.test.js @@ -51,7 +51,7 @@ describe('getContours', () => { }); }); - test('with all config options', () => { + test('with all config options (minutes)', () => { isochrone.getContours({ coordinates: [-118.22258, 33.99038], minutes: [5, 10, 15, 20], @@ -79,6 +79,34 @@ describe('getContours', () => { }); }); + test('with all config options (meters)', () => { + isochrone.getContours({ + coordinates: [-118.22258, 33.99038], + meters: [5000, 10000, 15000, 20000], + profile: 'walking', + polygons: true, + colors: ['6706ce', '04e813', '4286f4', '555555'], + denoise: 0, + generalize: 0 + }); + + expect(tu.requestConfig(isochrone)).toEqual({ + path: '/isochrone/v1/mapbox/:profile/:coordinates', + method: 'GET', + params: { + coordinates: '-118.22258,33.99038', + profile: 'walking' + }, + query: { + contours_meters: '5000,10000,15000,20000', + polygons: 'true', + contours_colors: '6706ce,04e813,4286f4,555555', + denoise: 0, + generalize: 0 + } + }); + }); + test('hex colors with # are removed', () => { isochrone.getContours({ coordinates: [-118.22258, 33.99038], @@ -109,6 +137,15 @@ describe('getContours', () => { ).toThrow('minutes must contain between 1 and 4 contour values'); }); + test('errors if more than 4 contours_meters requested', () => { + expect(() => + isochrone.getContours({ + coordinates: [-118.22258, 33.99038], + meters: [5000, 10000, 15000, 20000, 30000] + }) + ).toThrow('meters must contain between 1 and 4 contour values'); + }); + test('errors if minute value is greater than 60', () => { expect(() => isochrone.getContours({ @@ -118,6 +155,15 @@ describe('getContours', () => { ).toThrow('minutes must be less than 60'); }); + test('errors if meter value is greater than 100000', () => { + expect(() => + isochrone.getContours({ + coordinates: [-118.22258, 33.99038], + meters: [10000, 50000, 100000, 100001] + }) + ).toThrow('meters must be less than 100000'); + }); + test('errors if generalize is less than 0', () => { expect(() => isochrone.getContours({ @@ -138,4 +184,26 @@ describe('getContours', () => { }) ).toThrow('colors should have the same number of entries as minutes'); }); + + test('colors should have the same number of entries as meters', () => { + expect(() => + isochrone.getContours({ + coordinates: [-118.22258, 33.99038], + meters: [5000, 10000, 15000, 20000], + profile: 'walking', + colors: ['6706ce', '04e813'] + }) + ).toThrow('colors should have the same number of entries as meters'); + }); + + test('either meters or minutes should be specified', () => { + expect(() => + isochrone.getContours({ + coordinates: [-118.22258, 33.99038], + minutes: [5, 10, 15, 20], + meters: [5000, 10000, 15000, 20000], + profile: 'walking' + }) + ).toThrow("minutes and meters can't be specified at the same time"); + }); }); diff --git a/services/isochrone.js b/services/isochrone.js index 882225d2..c6dacd8c 100644 --- a/services/isochrone.js +++ b/services/isochrone.js @@ -18,8 +18,9 @@ var Isochrone = {}; * @param {Object} config * @param {'driving'|'walking'|'cycling'} [config.profile="driving"] - A Mapbox Directions routing profile ID. * @param {Coordinates} config.coordinates - A {longitude,latitude} coordinate pair around which to center the isochrone lines. - * @param {Array} config.minutes - The times in minutes to use for each isochrone contour. You can specify up to four contours. Times must be in increasing order. The maximum time that can be specified is 60 minutes. - * @param {Array} [config.colors] - The colors to use for each isochrone contour, specified as hex values without a leading # (for example, ff0000 for red). If this parameter is used, there must be the same number of colors as there are entries in contours_minutes. If no colors are specified, the Isochrone API will assign a default rainbow color scheme to the output. + * @param {Array} [config.minutes] - The times in minutes to use for each isochrone contour. You can specify up to four contours. Times must be in increasing order. The maximum time that can be specified is 60 minutes. Setting minutes and meters in the same time is an error. + * @param {Array} [config.meters] - The distances in meters to use for each isochrone contour. You can specify up to four contours. Distances must be in increasing order. The maximum distance that can be specified is 100000 meters. Setting minutes and meters in the same time is an error. + * @param {Array} [config.colors] - The colors to use for each isochrone contour, specified as hex values without a leading # (for example, ff0000 for red). If this parameter is used, there must be the same number of colors as there are entries in contours_minutes or contours_meters. If no colors are specified, the Isochrone API will assign a default rainbow color scheme to the output. * @param {boolean} [config.polygons] - Specify whether to return the contours as GeoJSON polygons (true) or linestrings (false, default). When polygons=true, any contour that forms a ring is returned as a polygon. * @param {number} [config.denoise] - A floating point value from 0.0 to 1.0 that can be used to remove smaller contours. The default is 1.0. A value of 1.0 will only return the largest contour for a given time value. A value of 0.5 drops any contours that are less than half the area of the largest contour in the set of contours for that same time value. * @param {number} [config.generalize] - A positive floating point value in meters used as the tolerance for Douglas-Peucker generalization. There is no upper bound. If no value is specified in the request, the Isochrone API will choose the most optimized generalization to use for the request. Note that the generalization of contours can lead to self-intersections, as well as intersections of adjacent contours. @@ -31,6 +32,7 @@ Isochrone.getContours = function(config) { profile: v.oneOf('driving', 'walking', 'cycling'), coordinates: v.coordinates, minutes: v.arrayOf(v.number), + meters: v.arrayOf(v.number), colors: v.arrayOf(v.string), polygons: v.boolean, denoise: v.number, @@ -39,21 +41,31 @@ Isochrone.getContours = function(config) { config.profile = config.profile || 'driving'; - var minutesCount = config.minutes.length; + if (config.minutes !== undefined && config.meters !== undefined) { + throw new Error("minutes and meters can't be specified at the same time"); + } + var contours = config.minutes ? config.minutes : config.meters; + var contours_name = config.minutes ? 'minutes' : 'meters'; + var contoursCount = contours.length; - if (minutesCount < 1 || minutesCount > 4) { - throw new Error('minutes must contain between 1 and 4 contour values'); + if (contoursCount < 1 || contoursCount > 4) { + throw new Error( + contours_name + ' must contain between 1 and 4 contour values' + ); } if ( config.colors !== undefined && - config.minutes !== undefined && - config.colors.length !== config.minutes.length + contours !== undefined && + config.colors.length !== contoursCount ) { - throw new Error('colors should have the same number of entries as minutes'); + throw new Error( + 'colors should have the same number of entries as ' + contours_name + ); } if ( + config.minutes !== undefined && !config.minutes.every(function(minute) { return minute <= 60; }) @@ -61,6 +73,15 @@ Isochrone.getContours = function(config) { throw new Error('minutes must be less than 60'); } + if ( + config.meters !== undefined && + !config.meters.every(function(meter) { + return meter <= 100000; + }) + ) { + throw new Error('meters must be less than 100000'); + } + if (config.generalize && config.generalize < 0) { throw new Error('generalize tolerance must be a positive number'); } @@ -74,7 +95,8 @@ Isochrone.getContours = function(config) { } var query = stringifyBooleans({ - contours_minutes: config.minutes.join(','), + contours_minutes: config.minutes ? config.minutes.join(',') : null, + contours_meters: config.meters ? config.meters.join(',') : null, contours_colors: config.colors ? config.colors.join(',') : null, polygons: config.polygons, denoise: config.denoise, From 203a28107f616e5c519fc88f3aae11a0206948b0 Mon Sep 17 00:00:00 2001 From: Grigory Dobrov Date: Mon, 4 Jul 2022 14:13:25 +0300 Subject: [PATCH 2/3] moved max_meters to constant --- services/isochrone.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/isochrone.js b/services/isochrone.js index c6dacd8c..23237eee 100644 --- a/services/isochrone.js +++ b/services/isochrone.js @@ -73,13 +73,14 @@ Isochrone.getContours = function(config) { throw new Error('minutes must be less than 60'); } + var MAX_METERS = 100000; if ( config.meters !== undefined && !config.meters.every(function(meter) { - return meter <= 100000; + return meter <= MAX_METERS; }) ) { - throw new Error('meters must be less than 100000'); + throw new Error('meters must be less than ' + MAX_METERS); } if (config.generalize && config.generalize < 0) { From 3d568ad6e25fcb959436bfc2ad6e8142040b9d7b Mon Sep 17 00:00:00 2001 From: Grigory Dobrov Date: Wed, 6 Jul 2022 11:23:18 +0300 Subject: [PATCH 3/3] added changelog entry --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51aed80f..6ca9cd34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - **Revert:** add `driving-traffic` profile to Isochrone service. +## 0.13.4 + +**Add:** add `contours_meters` Isochrone API parameter + ## 0.13.3 **Add:** add `ip` as valid value for `proximity` parameter in the geocoding service