Repository: iTwin/itwinjs-core
Pull request #8037: Optionally return world CRS
Author: login: ben-polinsky
Created: 2025-05-02T19:53:59Z | Merged: 2025-05-06T15:57:20Z

imodel-native: https://github.com/iTwin/imodel-native/pull/1103

There are a number of critical "world" CRS ("EPSG:4326" for example) which are not available from [getAvailableCoordinateReferenceSystems](https://github.com/iTwin/itwinjs-core/blob/06313ff2dc8671959008c377d92437e3bbe2f57a/core/backend/src/GeographicCRSServices.ts#L48). This is because [GeoServicesInterop::GetListOfGCS](https://github.com/iTwin/imodel-native/blob/644642cf7a86d9926c7c64aee51f4491045cca97/iModelJsNodeAddon/GeoServicesInterop.cpp#L75) filters out CRS without extents or those with extents spanning the globe.

This PR exposes an optional includeWorld argument to circumvent the above check.



Diff:
diff --git a/common/api/core-backend.api.md b/common/api/core-backend.api.md
index 978a4576e3a5..0b3c13e7b515 100644
--- a/common/api/core-backend.api.md
+++ b/common/api/core-backend.api.md
@@ -3218,6 +3218,7 @@ export function getAvailableCoordinateReferenceSystems(args: GetAvailableCoordin
 // @beta
 export interface GetAvailableCoordinateReferenceSystemsArgs {
     extent?: Range2dProps;
+    includeWorld?: boolean;
 }
 
 // @beta
diff --git a/common/changes/@itwin/core-backend/benpo-return-world-crs_2025-05-02-20-06.json b/common/changes/@itwin/core-backend/benpo-return-world-crs_2025-05-02-20-06.json
new file mode 100644
index 000000000000..87df06756a86
--- /dev/null
+++ b/common/changes/@itwin/core-backend/benpo-return-world-crs_2025-05-02-20-06.json
@@ -0,0 +1,10 @@
+{
+  "changes": [
+    {
+      "packageName": "@itwin/core-backend",
+      "comment": "optionally return world CRS from getAvailableCoordinateReferenceSystems",
+      "type": "none"
+    }
+  ],
+  "packageName": "@itwin/core-backend"
+}
\ No newline at end of file
diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml
index 0ae45be448db..3bbea4485ade 100644
--- a/common/config/rush/pnpm-lock.yaml
+++ b/common/config/rush/pnpm-lock.yaml
@@ -17,8 +17,8 @@ importers:
   ../../core/backend:
     dependencies:
       '@bentley/imodeljs-native':
-        specifier: 5.1.14
-        version: 5.1.14
+        specifier: 5.1.15
+        version: 5.1.15
       '@itwin/cloud-agnostic-core':
         specifier: ^2.2.4
         version: 2.2.4(inversify@6.0.1)(reflect-metadata@0.1.13)
@@ -4623,8 +4623,8 @@ packages:
   '@bentley/icons-generic@1.0.34':
     resolution: {integrity: sha512-IIs1wDcY2oZ8tJ3EZRw0U51M+0ZL3MvwoDYYmhUXaa9/UZqpFoOyLBGaxjirQteWXqTIMm3mFvmC+Nbn1ok4Iw==}
 
-  '@bentley/imodeljs-native@5.1.14':
-    resolution: {integrity: sha512-btuk5eMSl51Oz+1TJfu30/icJhCMQyz7hSpk0Sw94xNU38ZGXZZUhE67JBJtbDeH3N7z90G2edaWXJQkI1pm4Q==}
+  '@bentley/imodeljs-native@5.1.15':
+    resolution: {integrity: sha512-mbGw/8S4ahL78pamMVR4AVGPo594oSZEixVjXOoWfx1w08KMdxJLNuSiB8AlVsgLAWm/ckE1SujHEabweEDudA==}
 
   '@bentley/linear-referencing-schema@2.0.3':
     resolution: {integrity: sha512-2pFIEN4BS7alIDhGous6N2icKAS8ZhVBfoWB8WvSFaYnneGv5YwbbXl46qATDdPO5jUFezkW6uVxdpp1eOgUHQ==}
@@ -11014,7 +11014,7 @@ snapshots:
 
   '@bentley/icons-generic@1.0.34': {}
 
-  '@bentley/imodeljs-native@5.1.14': {}
+  '@bentley/imodeljs-native@5.1.15': {}
 
   '@bentley/linear-referencing-schema@2.0.3': {}
 
diff --git a/core/backend/package.json b/core/backend/package.json
index cffd33006d5a..ef64f2173772 100644
--- a/core/backend/package.json
+++ b/core/backend/package.json
@@ -106,7 +106,7 @@
     "webpack": "^5.97.1"
   },
   "dependencies": {
-    "@bentley/imodeljs-native": "5.1.14",
+    "@bentley/imodeljs-native": "5.1.15",
     "@itwin/cloud-agnostic-core": "^2.2.4",
     "@itwin/object-storage-azure": "^2.3.0",
     "@itwin/object-storage-core": "^2.3.0",
diff --git a/core/backend/src/GeographicCRSServices.ts b/core/backend/src/GeographicCRSServices.ts
index 0e15f4905464..d59ea231423b 100644
--- a/core/backend/src/GeographicCRSServices.ts
+++ b/core/backend/src/GeographicCRSServices.ts
@@ -9,7 +9,7 @@
 import { Range2dProps } from "@itwin/core-geometry";
 import { IModelNative } from "./internal/NativePlatform";
 
-/** Describes a geographic coordinate reference system produced by [[getAvailableCoordinateReferenceSystems]].
+/** Describes a coordinate reference system produced by [[getAvailableCoordinateReferenceSystems]].
  * @beta
  */
 export interface AvailableCoordinateReferenceSystemProps {
@@ -34,10 +34,14 @@ export interface AvailableCoordinateReferenceSystemProps {
  * @beta
  */
 export interface GetAvailableCoordinateReferenceSystemsArgs {
-  /** If provided, only return CRS's that contain the given extent. Minimum longitude and latitude correspond to extent.low.x and extent.low.y, respectively.
+  /** If provided, only return coordinate reference systems that contain the given extent. Minimum longitude and latitude correspond to extent.low.x and extent.low.y, respectively.
    * Maximum longitude and latitude correspond to extent.high.x and extent.high.y, respectively.
    */
   extent?: Range2dProps;
+  /** If true, returns additional coordinate reference systems with extents spanning the entire Earth's surface.
+   * @default false
+   */
+  includeWorld?: boolean
 }
 
 /** Get a list of Geographic Coordinate Reference Systems.
@@ -46,5 +50,5 @@ export interface GetAvailableCoordinateReferenceSystemsArgs {
  * @beta
  */
 export async function getAvailableCoordinateReferenceSystems(args: GetAvailableCoordinateReferenceSystemsArgs): Promise<AvailableCoordinateReferenceSystemProps[]> {
-  return IModelNative.platform.GeoServices.getListOfCRS(args.extent);
+  return IModelNative.platform.GeoServices.getListOfCRS(args.extent, args.includeWorld);
 }
diff --git a/core/backend/src/test/misc/GeoServices.test.ts b/core/backend/src/test/misc/GeoServices.test.ts
index 9eec9c688e53..ae9e36484cb5 100644
--- a/core/backend/src/test/misc/GeoServices.test.ts
+++ b/core/backend/src/test/misc/GeoServices.test.ts
@@ -809,10 +809,18 @@ describe("GeoServices", () => {
     const validationRange = 50;
     const validationRangeSmall = 10;
 
-    const validateCRSList = async (expectedCount: number, allowedRange: number, extent?: Range2dProps): Promise<void> => {
-      const listOfCRS = await getAvailableCoordinateReferenceSystems({ extent });
+    interface CRSValidationOptions {
+      expectedCount: number;
+      allowedRange: number;
+      extent?: Range2dProps;
+      includeWorld?: boolean;
+    }
+
+    const validateCRSList = async (options: CRSValidationOptions): Promise<void> => {
+      const { expectedCount, allowedRange, extent, includeWorld } = options;
+      const listOfCRS = await getAvailableCoordinateReferenceSystems({ extent, includeWorld });
 
-      // Check fields of returned CRS's
+      // Check fields of returned coordinate reference systems
       const extentRange: Range2d = Range2d.fromJSON(extent);
       for (const crs of listOfCRS) {
         // Validate extent
@@ -840,26 +848,33 @@ describe("GeoServices", () => {
     };
 
     it("should get all CRS", async () => {
-      await validateCRSList(11975, validationRange);
+      await validateCRSList({expectedCount: 12_332, allowedRange: validationRange, includeWorld: true});
     });
 
+    it("should get all CRS, excluding world crs", async () => {
+      await validateCRSList({expectedCount: 11_975, allowedRange: validationRange});
+    })
+
     it("should return CRS that are in the specified range (1)", async () => {
       const extent: Range2dProps = { low: { x: 60.1, y: 61.2 }, high: { x: 62.3, y: 63.4 } };
-      await validateCRSList(82, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 82, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 439, allowedRange: validationRangeSmall, extent, includeWorld: true});
     });
 
     it("should return CRS that are in the specified range (2)", async () => {
       const extent: Range2dProps = { low: { x: 0, y: 2 }, high: { x: 1, y: 3 } };
-      await validateCRSList(67, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 67, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 424, allowedRange: validationRangeSmall, extent, includeWorld: true});
     });
 
     it("should return CRS that are in the specified range (3)", async () => {
       const extent: Range2dProps = { low: { x: 0.3, y: 2.4 }, high: { x: 1.6, y: 3.77 } };
-      await validateCRSList(62, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 62, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 419, allowedRange: validationRangeSmall, extent, includeWorld: true});
     });
 
     it("should retrieve the whole list of CRS and validate the properties for a few selected CRS.", async () => {
-      const listOfCRS = await getAvailableCoordinateReferenceSystems({});
+      const listOfCRS = await getAvailableCoordinateReferenceSystems({includeWorld: true});
       let nbFound = 0;
       for (const crs of listOfCRS) {
         switch (crs.name) {
@@ -887,9 +902,17 @@ describe("GeoServices", () => {
             assert.equal(crs.deprecated, false);
             validateExtent(Range2d.fromJSON({ low: { x: -71, y: 43.5 }, high: { x: -66, y: 48 } }), crs.crsExtent);
             break;
+
+          // world CRS
+          case "EPSG:4326":
+            nbFound++;
+            assert.equal(crs.description, "WGS 84");
+            assert.equal(crs.deprecated, false);
+            validateExtent(Range2d.fromJSON({ low: { x: -180, y: -90 }, high: { x: 180, y: 90 } }), crs.crsExtent);
+            break;
         }
       }
-      assert.equal(nbFound, 4);
+      assert.equal(nbFound, 5);
     });
   });
 
diff --git a/test-apps/display-test-app/android/imodeljs-test-app/app/build.gradle b/test-apps/display-test-app/android/imodeljs-test-app/app/build.gradle
index 8f1c57e93004..7da7b538aa7d 100644
--- a/test-apps/display-test-app/android/imodeljs-test-app/app/build.gradle
+++ b/test-apps/display-test-app/android/imodeljs-test-app/app/build.gradle
@@ -41,7 +41,7 @@ dependencies {
     implementation 'com.google.android.material:material:1.7.0'
     implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
     implementation 'androidx.navigation:navigation-ui:2.5.3'
-    implementation 'com.github.itwin:mobile-native-android:5.1.14'
+    implementation 'com.github.itwin:mobile-native-android:5.1.15'
     implementation 'androidx.webkit:webkit:1.5.0'
 }
 
diff --git a/test-apps/display-test-app/ios/imodeljs-test-app/imodeljs-test-app.xcodeproj/project.pbxproj b/test-apps/display-test-app/ios/imodeljs-test-app/imodeljs-test-app.xcodeproj/project.pbxproj
index 3eb31a56778e..c2073b625327 100644
--- a/test-apps/display-test-app/ios/imodeljs-test-app/imodeljs-test-app.xcodeproj/project.pbxproj
+++ b/test-apps/display-test-app/ios/imodeljs-test-app/imodeljs-test-app.xcodeproj/project.pbxproj
@@ -455,7 +455,7 @@
 			repositoryURL = "https://github.com/iTwin/mobile-native-ios";
 			requirement = {
 				kind = exactVersion;
-				version = 5.1.14;
+				version = 5.1.15;
 			};
 		};
 /* End XCRemoteSwiftPackageReference section */
diff --git a/tools/internal/ios/core-test-runner/core-test-runner.xcodeproj/project.pbxproj b/tools/internal/ios/core-test-runner/core-test-runner.xcodeproj/project.pbxproj
index cb22810142c6..9928ca05a899 100644
--- a/tools/internal/ios/core-test-runner/core-test-runner.xcodeproj/project.pbxproj
+++ b/tools/internal/ios/core-test-runner/core-test-runner.xcodeproj/project.pbxproj
@@ -554,7 +554,7 @@
 			repositoryURL = "https://github.com/iTwin/mobile-native-ios";
 			requirement = {
 				kind = exactVersion;
-				version = 5.1.14;
+				version = 5.1.15;
 			};
 		};
 /* End XCRemoteSwiftPackageReference section */


Reviews:
user: login: bbastings
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T20:10:09Z
user: login: tcobbs-bentley
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-05T23:11:38Z
user: login: tcobbs-bentley
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-05T23:13:10Z
user: login: tcobbs-bentley
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-05T23:16:22Z
user: login: ben-polinsky
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T23:23:40Z
user: login: ben-polinsky
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T23:26:47Z
user: login: tcobbs-bentley
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-05T23:28:04Z
user: login: ben-polinsky
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T23:37:32Z
user: login: tcobbs-bentley
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-05T23:41:11Z
user: login: ben-polinsky
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T23:48:20Z
user: login: tcobbs-bentley
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-05T23:49:51Z
user: login: ben-polinsky
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T13:47:03Z
user: login: ben-polinsky
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T13:51:13Z
user: login: ben-polinsky
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T13:52:06Z
user: login: aruniverse
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-06T14:31:29Z
user: login: tcobbs-bentley
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-06T15:57:18Z

Review comments:
diff_hunk: @@ -38,6 +38,10 @@ export interface GetAvailableCoordinateReferenceSystemsArgs {
    * Maximum longitude and latitude correspond to extent.high.x and extent.high.y, respectively.
    */
   extent?: Range2dProps;
+  /** If true, returns additional CRS with extents spanning the entire Earth's surface.
path: core/backend/src/GeographicCRSServices.ts
user: login: tcobbs-bentley
body: I don't like using "CRS" in a way where it is supposed to be plural. The `extent` comment above uses "CRS's", which I also don't particularly like, but I feel that it is better than just "CRS". (Grammatically, I don't think "apostrophe s" is ever allowed to indicate plural, but with acronyms, it might be accepted.) Whatever is used should be consistent. I feel that the only real options are "CRS's" and "Coordinate Reference Systems" in both locations.
created_at: 2025-05-05T23:11:37Z
author_association: MEMBER
diff_hunk: @@ -809,23 +809,31 @@ describe("GeoServices", () => {
     const validationRange = 50;
     const validationRangeSmall = 10;
 
-    const validateCRSList = async (expectedCount: number, allowedRange: number, extent?: Range2dProps): Promise<void> => {
-      const listOfCRS = await getAvailableCoordinateReferenceSystems({ extent });
+    interface CRSValidationOptions {
+      expectedCount: number;
+      allowedRange: number;
+      extent?: Range2dProps;
+      includeWorld?: boolean;
+    }
+
+    const validateCRSList = async (options: CRSValidationOptions): Promise<void> => {
+      const { expectedCount, allowedRange, extent, includeWorld } = options;
+      const listOfCRS = await getAvailableCoordinateReferenceSystems({ extent, includeWorld });
 
       // Check fields of returned CRS's
       const extentRange: Range2d = Range2d.fromJSON(extent);
       for (const crs of listOfCRS) {
-        // Validate extent
-        if (extent !== undefined) {
-          const crsExtentRange: Range2d = Range2d.fromJSON(crs.crsExtent);
-          const intersects = extentRange.intersectsRange(crsExtentRange);
-          assert.isTrue(intersects);
-        }
+      // Validate extent
+      if (extent !== undefined) {
+        const crsExtentRange: Range2d = Range2d.fromJSON(crs.crsExtent);
+        const intersects = extentRange.intersectsRange(crsExtentRange);
+        assert.isTrue(intersects);
+      }
 
-        // These fields should always be present
-        assert.isTrue(crs.name !== undefined);
-        assert.isTrue(crs.description !== undefined);
-        assert.isTrue(crs.deprecated === true || crs.deprecated === false);
+      // These fields should always be present
+      assert.isTrue(crs.name !== undefined);
+      assert.isTrue(crs.description !== undefined);
+      assert.isTrue(crs.deprecated === true || crs.deprecated === false);
path: core/backend/src/test/misc/GeoServices.test.ts
user: login: tcobbs-bentley
body: I believe that this unindent was unintentional.
created_at: 2025-05-05T23:13:10Z
author_association: MEMBER
diff_hunk: @@ -840,26 +848,33 @@ describe("GeoServices", () => {
     };
 
     it("should get all CRS", async () => {
-      await validateCRSList(11975, validationRange);
+      await validateCRSList({expectedCount: 12_332, allowedRange: validationRange, includeWorld: true});
     });
 
+    it("should get all CRS, excluding world crs", async () => {
+      await validateCRSList({expectedCount: 11_975, allowedRange: validationRange});
+    })
+
     it("should return CRS that are in the specified range (1)", async () => {
       const extent: Range2dProps = { low: { x: 60.1, y: 61.2 }, high: { x: 62.3, y: 63.4 } };
-      await validateCRSList(82, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 82, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 439, allowedRange: validationRangeSmall, extent, includeWorld: true});
     });
 
     it("should return CRS that are in the specified range (2)", async () => {
       const extent: Range2dProps = { low: { x: 0, y: 2 }, high: { x: 1, y: 3 } };
-      await validateCRSList(67, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 67, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 424, allowedRange: validationRangeSmall, extent, includeWorld: true});
     });
 
     it("should return CRS that are in the specified range (3)", async () => {
       const extent: Range2dProps = { low: { x: 0.3, y: 2.4 }, high: { x: 1.6, y: 3.77 } };
-      await validateCRSList(62, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 62, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 422, allowedRange: validationRangeSmall, extent, includeWorld: true});
path: core/backend/src/test/misc/GeoServices.test.ts
user: login: tcobbs-bentley
body: Why do the tests at line 861 and 867 have 357 new systems, while this test has 360 new systems? Shouldn't the number of world systems be the same, no matter the allowed range?
created_at: 2025-05-05T23:16:22Z
author_association: MEMBER
diff_hunk: @@ -840,26 +848,33 @@ describe("GeoServices", () => {
     };
 
     it("should get all CRS", async () => {
-      await validateCRSList(11975, validationRange);
+      await validateCRSList({expectedCount: 12_332, allowedRange: validationRange, includeWorld: true});
     });
 
+    it("should get all CRS, excluding world crs", async () => {
+      await validateCRSList({expectedCount: 11_975, allowedRange: validationRange});
+    })
+
     it("should return CRS that are in the specified range (1)", async () => {
       const extent: Range2dProps = { low: { x: 60.1, y: 61.2 }, high: { x: 62.3, y: 63.4 } };
-      await validateCRSList(82, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 82, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 439, allowedRange: validationRangeSmall, extent, includeWorld: true});
     });
 
     it("should return CRS that are in the specified range (2)", async () => {
       const extent: Range2dProps = { low: { x: 0, y: 2 }, high: { x: 1, y: 3 } };
-      await validateCRSList(67, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 67, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 424, allowedRange: validationRangeSmall, extent, includeWorld: true});
     });
 
     it("should return CRS that are in the specified range (3)", async () => {
       const extent: Range2dProps = { low: { x: 0.3, y: 2.4 }, high: { x: 1.6, y: 3.77 } };
-      await validateCRSList(62, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 62, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 422, allowedRange: validationRangeSmall, extent, includeWorld: true});
path: core/backend/src/test/misc/GeoServices.test.ts
user: login: ben-polinsky
body: That's probably a mistake, checking...
created_at: 2025-05-05T23:23:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -38,6 +38,10 @@ export interface GetAvailableCoordinateReferenceSystemsArgs {
    * Maximum longitude and latitude correspond to extent.high.x and extent.high.y, respectively.
    */
   extent?: Range2dProps;
+  /** If true, returns additional CRS with extents spanning the entire Earth's surface.
path: core/backend/src/GeographicCRSServices.ts
user: login: ben-polinsky
body: > don't think "apostrophe s" is ever allowed to indicate plural, but with acronyms, it might be accepted.

Seems to be bending toward allowing in some cases, but I agree. Looking it up now, it seems CRSs might be the best idea, which I don't hate.
created_at: 2025-05-05T23:26:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -38,6 +38,10 @@ export interface GetAvailableCoordinateReferenceSystemsArgs {
    * Maximum longitude and latitude correspond to extent.high.x and extent.high.y, respectively.
    */
   extent?: Range2dProps;
+  /** If true, returns additional CRS with extents spanning the entire Earth's surface.
path: core/backend/src/GeographicCRSServices.ts
user: login: tcobbs-bentley
body: I'm ok with CRSs as long as you update the `extent` comment to be consistent.
created_at: 2025-05-05T23:28:04Z
author_association: MEMBER
diff_hunk: @@ -809,23 +809,31 @@ describe("GeoServices", () => {
     const validationRange = 50;
     const validationRangeSmall = 10;
 
-    const validateCRSList = async (expectedCount: number, allowedRange: number, extent?: Range2dProps): Promise<void> => {
-      const listOfCRS = await getAvailableCoordinateReferenceSystems({ extent });
+    interface CRSValidationOptions {
+      expectedCount: number;
+      allowedRange: number;
+      extent?: Range2dProps;
+      includeWorld?: boolean;
+    }
+
+    const validateCRSList = async (options: CRSValidationOptions): Promise<void> => {
+      const { expectedCount, allowedRange, extent, includeWorld } = options;
+      const listOfCRS = await getAvailableCoordinateReferenceSystems({ extent, includeWorld });
 
       // Check fields of returned CRS's
       const extentRange: Range2d = Range2d.fromJSON(extent);
       for (const crs of listOfCRS) {
-        // Validate extent
-        if (extent !== undefined) {
-          const crsExtentRange: Range2d = Range2d.fromJSON(crs.crsExtent);
-          const intersects = extentRange.intersectsRange(crsExtentRange);
-          assert.isTrue(intersects);
-        }
+      // Validate extent
+      if (extent !== undefined) {
+        const crsExtentRange: Range2d = Range2d.fromJSON(crs.crsExtent);
+        const intersects = extentRange.intersectsRange(crsExtentRange);
+        assert.isTrue(intersects);
+      }
 
-        // These fields should always be present
-        assert.isTrue(crs.name !== undefined);
-        assert.isTrue(crs.description !== undefined);
-        assert.isTrue(crs.deprecated === true || crs.deprecated === false);
+      // These fields should always be present
+      assert.isTrue(crs.name !== undefined);
+      assert.isTrue(crs.description !== undefined);
+      assert.isTrue(crs.deprecated === true || crs.deprecated === false);
path: core/backend/src/test/misc/GeoServices.test.ts
user: login: ben-polinsky
body: ~~It wasn't, the formatting is better here. But for the sake of a cleaner history,~~ I'll fix.
created_at: 2025-05-05T23:37:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -809,23 +809,31 @@ describe("GeoServices", () => {
     const validationRange = 50;
     const validationRangeSmall = 10;
 
-    const validateCRSList = async (expectedCount: number, allowedRange: number, extent?: Range2dProps): Promise<void> => {
-      const listOfCRS = await getAvailableCoordinateReferenceSystems({ extent });
+    interface CRSValidationOptions {
+      expectedCount: number;
+      allowedRange: number;
+      extent?: Range2dProps;
+      includeWorld?: boolean;
+    }
+
+    const validateCRSList = async (options: CRSValidationOptions): Promise<void> => {
+      const { expectedCount, allowedRange, extent, includeWorld } = options;
+      const listOfCRS = await getAvailableCoordinateReferenceSystems({ extent, includeWorld });
 
       // Check fields of returned CRS's
       const extentRange: Range2d = Range2d.fromJSON(extent);
       for (const crs of listOfCRS) {
-        // Validate extent
-        if (extent !== undefined) {
-          const crsExtentRange: Range2d = Range2d.fromJSON(crs.crsExtent);
-          const intersects = extentRange.intersectsRange(crsExtentRange);
-          assert.isTrue(intersects);
-        }
+      // Validate extent
+      if (extent !== undefined) {
+        const crsExtentRange: Range2d = Range2d.fromJSON(crs.crsExtent);
+        const intersects = extentRange.intersectsRange(crsExtentRange);
+        assert.isTrue(intersects);
+      }
 
-        // These fields should always be present
-        assert.isTrue(crs.name !== undefined);
-        assert.isTrue(crs.description !== undefined);
-        assert.isTrue(crs.deprecated === true || crs.deprecated === false);
+      // These fields should always be present
+      assert.isTrue(crs.name !== undefined);
+      assert.isTrue(crs.description !== undefined);
+      assert.isTrue(crs.deprecated === true || crs.deprecated === false);
path: core/backend/src/test/misc/GeoServices.test.ts
user: login: tcobbs-bentley
body: Lines 826 through 836 can't be at the same indent level as the `for` statement on line 825.
created_at: 2025-05-05T23:41:11Z
author_association: MEMBER
diff_hunk: @@ -840,26 +848,33 @@ describe("GeoServices", () => {
     };
 
     it("should get all CRS", async () => {
-      await validateCRSList(11975, validationRange);
+      await validateCRSList({expectedCount: 12_332, allowedRange: validationRange, includeWorld: true});
     });
 
+    it("should get all CRS, excluding world crs", async () => {
+      await validateCRSList({expectedCount: 11_975, allowedRange: validationRange});
+    })
+
     it("should return CRS that are in the specified range (1)", async () => {
       const extent: Range2dProps = { low: { x: 60.1, y: 61.2 }, high: { x: 62.3, y: 63.4 } };
-      await validateCRSList(82, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 82, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 439, allowedRange: validationRangeSmall, extent, includeWorld: true});
     });
 
     it("should return CRS that are in the specified range (2)", async () => {
       const extent: Range2dProps = { low: { x: 0, y: 2 }, high: { x: 1, y: 3 } };
-      await validateCRSList(67, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 67, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 424, allowedRange: validationRangeSmall, extent, includeWorld: true});
     });
 
     it("should return CRS that are in the specified range (3)", async () => {
       const extent: Range2dProps = { low: { x: 0.3, y: 2.4 }, high: { x: 1.6, y: 3.77 } };
-      await validateCRSList(62, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 62, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 422, allowedRange: validationRangeSmall, extent, includeWorld: true});
path: core/backend/src/test/misc/GeoServices.test.ts
user: login: ben-polinsky
body: The numbers are correct...
created_at: 2025-05-05T23:48:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -840,26 +848,33 @@ describe("GeoServices", () => {
     };
 
     it("should get all CRS", async () => {
-      await validateCRSList(11975, validationRange);
+      await validateCRSList({expectedCount: 12_332, allowedRange: validationRange, includeWorld: true});
     });
 
+    it("should get all CRS, excluding world crs", async () => {
+      await validateCRSList({expectedCount: 11_975, allowedRange: validationRange});
+    })
+
     it("should return CRS that are in the specified range (1)", async () => {
       const extent: Range2dProps = { low: { x: 60.1, y: 61.2 }, high: { x: 62.3, y: 63.4 } };
-      await validateCRSList(82, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 82, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 439, allowedRange: validationRangeSmall, extent, includeWorld: true});
     });
 
     it("should return CRS that are in the specified range (2)", async () => {
       const extent: Range2dProps = { low: { x: 0, y: 2 }, high: { x: 1, y: 3 } };
-      await validateCRSList(67, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 67, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 424, allowedRange: validationRangeSmall, extent, includeWorld: true});
     });
 
     it("should return CRS that are in the specified range (3)", async () => {
       const extent: Range2dProps = { low: { x: 0.3, y: 2.4 }, high: { x: 1.6, y: 3.77 } };
-      await validateCRSList(62, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 62, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 422, allowedRange: validationRangeSmall, extent, includeWorld: true});
path: core/backend/src/test/misc/GeoServices.test.ts
user: login: tcobbs-bentley
body: Any idea why this one has 360 extras while the other two only have 357? It might indicate that something is wrong somewhere, or there could be some other logical explanation.
created_at: 2025-05-05T23:49:51Z
author_association: MEMBER
diff_hunk: @@ -840,26 +848,33 @@ describe("GeoServices", () => {
     };
 
     it("should get all CRS", async () => {
-      await validateCRSList(11975, validationRange);
+      await validateCRSList({expectedCount: 12_332, allowedRange: validationRange, includeWorld: true});
     });
 
+    it("should get all CRS, excluding world crs", async () => {
+      await validateCRSList({expectedCount: 11_975, allowedRange: validationRange});
+    })
+
     it("should return CRS that are in the specified range (1)", async () => {
       const extent: Range2dProps = { low: { x: 60.1, y: 61.2 }, high: { x: 62.3, y: 63.4 } };
-      await validateCRSList(82, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 82, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 439, allowedRange: validationRangeSmall, extent, includeWorld: true});
     });
 
     it("should return CRS that are in the specified range (2)", async () => {
       const extent: Range2dProps = { low: { x: 0, y: 2 }, high: { x: 1, y: 3 } };
-      await validateCRSList(67, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 67, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 424, allowedRange: validationRangeSmall, extent, includeWorld: true});
     });
 
     it("should return CRS that are in the specified range (3)", async () => {
       const extent: Range2dProps = { low: { x: 0.3, y: 2.4 }, high: { x: 1.6, y: 3.77 } };
-      await validateCRSList(62, validationRangeSmall, extent);
+      await validateCRSList({expectedCount: 62, allowedRange: validationRangeSmall, extent});
+      await validateCRSList({expectedCount: 422, allowedRange: validationRangeSmall, extent, includeWorld: true});
path: core/backend/src/test/misc/GeoServices.test.ts
user: login: ben-polinsky
body: Ah good - I read the debugger wrong, it is 419: 
<img width="141" alt="image" src="https://github.com/user-attachments/assets/9f9dba24-b316-4fae-8918-46ac130bf7c3" />

Phew :)
created_at: 2025-05-06T13:47:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -809,23 +809,31 @@ describe("GeoServices", () => {
     const validationRange = 50;
     const validationRangeSmall = 10;
 
-    const validateCRSList = async (expectedCount: number, allowedRange: number, extent?: Range2dProps): Promise<void> => {
-      const listOfCRS = await getAvailableCoordinateReferenceSystems({ extent });
+    interface CRSValidationOptions {
+      expectedCount: number;
+      allowedRange: number;
+      extent?: Range2dProps;
+      includeWorld?: boolean;
+    }
+
+    const validateCRSList = async (options: CRSValidationOptions): Promise<void> => {
+      const { expectedCount, allowedRange, extent, includeWorld } = options;
+      const listOfCRS = await getAvailableCoordinateReferenceSystems({ extent, includeWorld });
 
       // Check fields of returned CRS's
       const extentRange: Range2d = Range2d.fromJSON(extent);
       for (const crs of listOfCRS) {
-        // Validate extent
-        if (extent !== undefined) {
-          const crsExtentRange: Range2d = Range2d.fromJSON(crs.crsExtent);
-          const intersects = extentRange.intersectsRange(crsExtentRange);
-          assert.isTrue(intersects);
-        }
+      // Validate extent
+      if (extent !== undefined) {
+        const crsExtentRange: Range2d = Range2d.fromJSON(crs.crsExtent);
+        const intersects = extentRange.intersectsRange(crsExtentRange);
+        assert.isTrue(intersects);
+      }
 
-        // These fields should always be present
-        assert.isTrue(crs.name !== undefined);
-        assert.isTrue(crs.description !== undefined);
-        assert.isTrue(crs.deprecated === true || crs.deprecated === false);
+      // These fields should always be present
+      assert.isTrue(crs.name !== undefined);
+      assert.isTrue(crs.description !== undefined);
+      assert.isTrue(crs.deprecated === true || crs.deprecated === false);
path: core/backend/src/test/misc/GeoServices.test.ts
user: login: ben-polinsky
body: Should be fine now
created_at: 2025-05-06T13:51:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -38,6 +38,10 @@ export interface GetAvailableCoordinateReferenceSystemsArgs {
    * Maximum longitude and latitude correspond to extent.high.x and extent.high.y, respectively.
    */
   extent?: Range2dProps;
+  /** If true, returns additional CRS with extents spanning the entire Earth's surface.
path: core/backend/src/GeographicCRSServices.ts
user: login: ben-polinsky
body: I've moved to coordinate reference systems in that file for consistency... we're all over the place across the repo.
created_at: 2025-05-06T13:52:05Z
author_association: CONTRIBUTOR

PR comments:
user: login: ben-polinsky
created_at: 2025-05-06T18:10:47Z
body: @mergifyio backport release/5.0.x
author_association: CONTRIBUTOR
user: login: mergify[bot]
created_at: 2025-05-06T18:11:07Z
body: > backport release/5.0.x

#### ✅ Backports have been created

<details>

* [#8052 Optionally return world CRS (backport #8037) [release/5.0.x]](https://github.com/iTwin/itwinjs-core/pull/8052) has been created for branch `release/5.0.x` but encountered conflicts

</details>


<!---
DO NOT EDIT
-*- Mergify Payload -*-
{"command": "backport release/5.0.x", "conclusion": "success", "action_is_running": true}
-*- Mergify Payload End -*-
-->
author_association: CONTRIBUTOR