From d6ee07c3a5cb146d2065d2a67074058867ef2fdd Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Thu, 18 Jun 2026 18:13:24 -0600 Subject: [PATCH 1/5] refactor: Remove deprecation warning --- src/traces/choropleth/defaults.js | 10 ---------- src/traces/scattergeo/attributes.js | 6 ------ src/traces/scattergeo/defaults.js | 10 ---------- src/types/generated/schema.d.ts | 4 ++-- test/plot-schema.json | 4 ++-- 5 files changed, 4 insertions(+), 30 deletions(-) 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/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", From 734ce036ad1680b7e309e929b088ec2d3f4f2449 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Mon, 22 Jun 2026 12:28:59 -0600 Subject: [PATCH 2/5] feat: Replace country-regex with country-iso-search --- package-lock.json | 14 +++++++----- package.json | 2 +- src/lib/custom_country_codes.ts | 39 +++++++++++++++++++++++++++++++++ src/lib/geo_location_utils.js | 15 +++++-------- 4 files changed, 54 insertions(+), 16 deletions(-) create mode 100644 src/lib/custom_country_codes.ts 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..5006a2162f1 --- /dev/null +++ b/src/lib/custom_country_codes.ts @@ -0,0 +1,39 @@ +import type { CountryRecord } 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; From 1555cb50aac424513a9647d00d543ee039abe6ff Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Mon, 22 Jun 2026 12:47:48 -0600 Subject: [PATCH 3/5] Add regression tests --- test/jasmine/tests/geo_test.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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() { From 0a8f7f6d4c943d59621b33de3fae1b3861340630 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Mon, 22 Jun 2026 13:09:37 -0600 Subject: [PATCH 4/5] Add draftlog --- draftlogs/7856_add.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 draftlogs/7856_add.md 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)] From 48cc7b8bb7bb45abca685e49d9d2c3704c4163ea Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Mon, 22 Jun 2026 16:53:32 -0600 Subject: [PATCH 5/5] Add description for COUNTRIES_X --- src/lib/custom_country_codes.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/custom_country_codes.ts b/src/lib/custom_country_codes.ts index 5006a2162f1..aa3985eda34 100644 --- a/src/lib/custom_country_codes.ts +++ b/src/lib/custom_country_codes.ts @@ -1,5 +1,11 @@ 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',