diff --git a/draftlogs/7856_add.md b/draftlogs/7856_add.md new file mode 100644 index 00000000000..cbf444e3986 --- /dev/null +++ b/draftlogs/7856_add.md @@ -0,0 +1 @@ +- Replace `country-regex` with `country-iso-search` to search for country names in choropleth, scattergeo traces [[#7856](https://github.com/plotly/plotly.js/pull/7856)] diff --git a/package-lock.json b/package-lock.json index 1bcc20791d3..674a406a1e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "color": "^5.0.0", "color-normalize": "1.5.0", "color-rgba": "3.0.0", - "country-regex": "^1.1.0", + "country-iso-search": "^0.1.1", "d3-force": "^1.2.1", "d3-format": "^1.4.5", "d3-geo": "^1.12.1", @@ -2634,10 +2634,14 @@ "node": ">= 0.10" } }, - "node_modules/country-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/country-regex/-/country-regex-1.1.0.tgz", - "integrity": "sha1-UcMz3N8Sknt+XuucEKyBEqYSCJY=" + "node_modules/country-iso-search": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/country-iso-search/-/country-iso-search-0.1.1.tgz", + "integrity": "sha512-JJxdoVmgZW3F4DdQ4Jq3NuVfqsk6XoHq8PE8QXNIv1BVwAKyJum2dahC7hsW+v60xwH7pm7g8BwCJ05aUabApg==", + "license": "MIT", + "engines": { + "node": ">=20" + } }, "node_modules/create-require": { "version": "1.1.1", diff --git a/package.json b/package.json index a62cdd26db4..232ecaab025 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "color": "^5.0.0", "color-normalize": "1.5.0", "color-rgba": "3.0.0", - "country-regex": "^1.1.0", + "country-iso-search": "^0.1.1", "d3-force": "^1.2.1", "d3-format": "^1.4.5", "d3-geo": "^1.12.1", diff --git a/src/lib/custom_country_codes.ts b/src/lib/custom_country_codes.ts new file mode 100644 index 00000000000..aa3985eda34 --- /dev/null +++ b/src/lib/custom_country_codes.ts @@ -0,0 +1,45 @@ +import type { CountryRecord } from 'country-iso-search'; + +/** + * Plotly-specific country records for disputed and unrecognized territories + * that are not part of ISO 3166-1. Each entry uses an ISO3-like code in the + * `X` range (reserved by ISO 3166-1 for user-assigned codes) so these regions + * can be looked up alongside standard countries from `country-iso-search`. + */ +export const COUNTRIES_X: ReadonlyArray = [ + { + iso3: 'XAC', + iso2: '', + m49: '', + name: 'Aksai Chin', + aliases: [] + }, + { + iso3: 'XAP', + iso2: '', + m49: '', + name: 'Arunachal Pradesh', + aliases: [] + }, + { + iso3: 'XBT', + iso2: '', + m49: '', + name: 'Bir Tawil', + aliases: [] + }, + { + iso3: 'XHT', + iso2: '', + m49: '', + name: 'Halaib Triangle', + aliases: [] + }, + { + iso3: 'XJK', + iso2: '', + m49: '', + name: 'Jammu and Kashmir', + aliases: [] + } +]; diff --git a/src/lib/geo_location_utils.js b/src/lib/geo_location_utils.js index 55338602fd8..2e2b6a792a8 100644 --- a/src/lib/geo_location_utils.js +++ b/src/lib/geo_location_utils.js @@ -1,7 +1,7 @@ 'use strict'; var d3 = require('@plotly/d3'); -var countryRegex = require('country-regex'); +const { COUNTRIES, createLookup } = require('country-iso-search'); var { area: turfArea } = require('@turf/area'); var { centroid: turfCentroid } = require('@turf/centroid'); var { bbox: turfBbox } = require('@turf/bbox'); @@ -12,9 +12,9 @@ var isPlainObject = require('./is_plain_object'); var nestedProperty = require('./nested_property'); var polygon = require('./polygon'); const { usaLocationAbbreviations, usaLocationList } = require('./usa_location_names'); +const { COUNTRIES_X } = require('./custom_country_codes'); -// make list of all country iso3 ids from at runtime -var countryIds = Object.keys(countryRegex); +const { lookupAlpha3 } = createLookup([...COUNTRIES, ...COUNTRIES_X]); var locationmodeToIdFinder = { 'ISO-3': identity, @@ -23,13 +23,8 @@ var locationmodeToIdFinder = { }; function countryNameToISO3(countryName) { - for (var i = 0; i < countryIds.length; i++) { - var iso3 = countryIds[i]; - var regex = new RegExp(countryRegex[iso3]); - - if (regex.test(countryName.trim().toLowerCase())) return iso3; - } - + const iso3 = lookupAlpha3(countryName); + if (iso3) return iso3; loggers.log('Unrecognized country name: ' + countryName + '.'); return false; diff --git a/src/traces/choropleth/defaults.js b/src/traces/choropleth/defaults.js index 795167eb3ca..7b1e5c4dff1 100644 --- a/src/traces/choropleth/defaults.js +++ b/src/traces/choropleth/defaults.js @@ -4,12 +4,6 @@ var Lib = require('../../lib'); var colorscaleDefaults = require('../../components/colorscale/defaults'); var attributes = require('./attributes'); -const locationmodeBreakingChangeWarning = [ - 'The library used by the *country names* `locationmode` option is changing in the next major version.', - 'Some country names in existing plots may not work in the new version.', - 'To ensure consistent behavior, consider setting `locationmode` to *ISO-3*.' -].join(' '); - module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { function coerce(attr, dflt) { return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); @@ -34,10 +28,6 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout var locationMode = coerce('locationmode', locationmodeDflt); - if (locationMode === 'country names') { - Lib.warn(locationmodeBreakingChangeWarning); - } - if (locationMode === 'geojson-id') { coerce('featureidkey'); } diff --git a/src/traces/scattergeo/attributes.js b/src/traces/scattergeo/attributes.js index 9059261f671..1aeddeb5474 100644 --- a/src/traces/scattergeo/attributes.js +++ b/src/traces/scattergeo/attributes.js @@ -14,11 +14,6 @@ var scatterMarkerAttrs = scatterAttrs.marker; var scatterLineAttrs = scatterAttrs.line; var scatterMarkerLineAttrs = scatterMarkerAttrs.line; -const breakingChangeWarning = [ - 'The library used by the *country names* `locationmode` option is changing in an upcoming version.', - 'Country names in existing plots may not work in the new version.' -].join(' '); - module.exports = overrideAll( { lon: { @@ -43,7 +38,6 @@ module.exports = overrideAll( values: ['ISO-3', 'USA-states', 'country names', 'geojson-id'], dflt: 'ISO-3', description: [ - breakingChangeWarning, 'Determines the set of locations used to match entries in `locations`', 'to regions on the map.', 'Values *ISO-3*, *USA-states*, *country names* correspond to features on', diff --git a/src/traces/scattergeo/defaults.js b/src/traces/scattergeo/defaults.js index 88e20a17ff7..32629884b8a 100644 --- a/src/traces/scattergeo/defaults.js +++ b/src/traces/scattergeo/defaults.js @@ -10,12 +10,6 @@ var handleFillColorDefaults = require('../scatter/fillcolor_defaults'); var attributes = require('./attributes'); -const locationmodeBreakingChangeWarning = [ - 'The library used by the *country names* `locationmode` option is changing in the next major version.', - 'Some country names in existing plots may not work in the new version.', - 'To ensure consistent behavior, consider setting `locationmode` to *ISO-3*.' -].join(' '); - module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { function coerce(attr, dflt) { return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); @@ -33,10 +27,6 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout var locationMode = coerce('locationmode', locationmodeDflt); - if (locationMode === 'country names') { - Lib.warn(locationmodeBreakingChangeWarning); - } - if (locationMode === 'geojson-id') { coerce('featureidkey'); } diff --git a/src/types/generated/schema.d.ts b/src/types/generated/schema.d.ts index f927fc11032..b3c5da5ff2e 100644 --- a/src/types/generated/schema.d.ts +++ b/src/types/generated/schema.d.ts @@ -2346,7 +2346,7 @@ export interface ChoroplethData { */ legendwidth?: number; /** - * The library used by the *country names* `locationmode` option is changing in an upcoming version. Country names in existing plots may not work in the new version. Determines the set of locations used to match entries in `locations` to regions on the map. Values *ISO-3*, *USA-states*, *country names* correspond to features on the base map and value *geojson-id* corresponds to features from a custom GeoJSON linked to the `geojson` attribute. *USA-states* accepts both two-letter abbreviations (e.g. *CA*) and full state names (e.g. *California*). + * Determines the set of locations used to match entries in `locations` to regions on the map. Values *ISO-3*, *USA-states*, *country names* correspond to features on the base map and value *geojson-id* corresponds to features from a custom GeoJSON linked to the `geojson` attribute. *USA-states* accepts both two-letter abbreviations (e.g. *CA*) and full state names (e.g. *California*). * @default 'ISO-3' */ locationmode?: 'ISO-3' | 'USA-states' | 'country names' | 'geojson-id'; @@ -8240,7 +8240,7 @@ export interface ScattergeoData { width?: number; }; /** - * The library used by the *country names* `locationmode` option is changing in an upcoming version. Country names in existing plots may not work in the new version. Determines the set of locations used to match entries in `locations` to regions on the map. Values *ISO-3*, *USA-states*, *country names* correspond to features on the base map and value *geojson-id* corresponds to features from a custom GeoJSON linked to the `geojson` attribute. *USA-states* accepts both two-letter abbreviations (e.g. *CA*) and full state names (e.g. *California*). + * Determines the set of locations used to match entries in `locations` to regions on the map. Values *ISO-3*, *USA-states*, *country names* correspond to features on the base map and value *geojson-id* corresponds to features from a custom GeoJSON linked to the `geojson` attribute. *USA-states* accepts both two-letter abbreviations (e.g. *CA*) and full state names (e.g. *California*). * @default 'ISO-3' */ locationmode?: 'ISO-3' | 'USA-states' | 'country names' | 'geojson-id'; diff --git a/test/jasmine/tests/geo_test.js b/test/jasmine/tests/geo_test.js index 403e30a55a2..d75451d1923 100644 --- a/test/jasmine/tests/geo_test.js +++ b/test/jasmine/tests/geo_test.js @@ -886,6 +886,18 @@ describe('geojson / topojson utils', function() { expect(out).toEqual(false); }); + + it('with *country names* locationmode and an ISO 3166-1 short-name suffix', () => { + const out = _locationToFeature(topojson, 'Korea, Republic of', 'country names'); + + expect(out.id).toEqual('KOR'); + }); + + it('with *country names* locationmode and a custom country code', () => { + const out = _locationToFeature(topojson, 'Aksai Chin', 'country names'); + + expect(out.id).toEqual('XAC'); + }); }); describe('should distinguish between US and US Virgin Island', function() { diff --git a/test/plot-schema.json b/test/plot-schema.json index 160a59285ca..b18bd8e6062 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -24133,7 +24133,7 @@ "valType": "number" }, "locationmode": { - "description": "The library used by the *country names* `locationmode` option is changing in an upcoming version. Country names in existing plots may not work in the new version. Determines the set of locations used to match entries in `locations` to regions on the map. Values *ISO-3*, *USA-states*, *country names* correspond to features on the base map and value *geojson-id* corresponds to features from a custom GeoJSON linked to the `geojson` attribute. *USA-states* accepts both two-letter abbreviations (e.g. *CA*) and full state names (e.g. *California*).", + "description": "Determines the set of locations used to match entries in `locations` to regions on the map. Values *ISO-3*, *USA-states*, *country names* correspond to features on the base map and value *geojson-id* corresponds to features from a custom GeoJSON linked to the `geojson` attribute. *USA-states* accepts both two-letter abbreviations (e.g. *CA*) and full state names (e.g. *California*).", "dflt": "ISO-3", "editType": "calc", "valType": "enumerated", @@ -61010,7 +61010,7 @@ } }, "locationmode": { - "description": "The library used by the *country names* `locationmode` option is changing in an upcoming version. Country names in existing plots may not work in the new version. Determines the set of locations used to match entries in `locations` to regions on the map. Values *ISO-3*, *USA-states*, *country names* correspond to features on the base map and value *geojson-id* corresponds to features from a custom GeoJSON linked to the `geojson` attribute. *USA-states* accepts both two-letter abbreviations (e.g. *CA*) and full state names (e.g. *California*).", + "description": "Determines the set of locations used to match entries in `locations` to regions on the map. Values *ISO-3*, *USA-states*, *country names* correspond to features on the base map and value *geojson-id* corresponds to features from a custom GeoJSON linked to the `geojson` attribute. *USA-states* accepts both two-letter abbreviations (e.g. *CA*) and full state names (e.g. *California*).", "dflt": "ISO-3", "editType": "calc", "valType": "enumerated",