ngageoint / geopackage-mapcache-android

GeoPackage MapCache Android App
https://ngageoint.github.io/geopackage-mapcache-android
MIT License
40 stars 16 forks source link

Exception error "Failed to create projection for authority: EPSG, code: 3857" in the Android logcat for Pseudo Mercator projection #49

Closed eclectice closed 2 years ago

eclectice commented 2 years ago

Please fill out as much known and relevant information as possible.

Version Information:

Expected Results:

Observed Results:

Output:

2022-04-11 09:24:08.348 19309-19942/? W/ProjectionFactory: Failed to create projection for authority: EPSG, code: 3857, definition: 
                    PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984"
                    ,SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]]
                    ,AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG",
                    "8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]]
                    ,AUTHORITY["EPSG","9122"]]AUTHORITY["EPSG","4326"]],PROJECTION[
                    "Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER[
                    "scale_factor",1],PARAMETER["false_easting",0],PARAMETER[
                    "false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS[
                    "X",EAST],AXIS["Y",NORTH]

    mil.nga.crs.CRSException: Unexpected end of text, null token
        at mil.nga.crs.wkt.TextReader.readExpectedToken(TextReader.java:318)
        at mil.nga.crs.wkt.CRSReader.readRightDelimiter(CRSReader.java:1159)
        at mil.nga.crs.wkt.CRSReader.readProjectedCompat(CRSReader.java:4097)
        at mil.nga.crs.wkt.CRSReader.readProjectedCompat(CRSReader.java:3996)
        at mil.nga.crs.wkt.CRSReader.read(CRSReader.java:812)
        at mil.nga.crs.wkt.CRSReader.read(CRSReader.java:117)
        at mil.nga.crs.wkt.CRSReader.read(CRSReader.java:99)
        at mil.nga.proj.ProjectionFactory.fromDefinition(ProjectionFactory.java:988)
        at mil.nga.proj.ProjectionFactory.getProjection(ProjectionFactory.java:650)
        at mil.nga.proj.ProjectionFactory.getProjection(ProjectionFactory.java:582)
        at mil.nga.proj.ProjectionFactory.getProjection(ProjectionFactory.java:536)
        at mil.nga.proj.ProjectionFactory.getProjection(ProjectionFactory.java:454)
        at mil.nga.geopackage.srs.SpatialReferenceSystem.getProjection(SpatialReferenceSystem.java:355)
        at mil.nga.geopackage.tiles.matrixset.TileMatrixSet.getProjection(TileMatrixSet.java:268)
        at mil.nga.geopackage.tiles.user.TileDao.<init>(TileDao.java:92)
        at mil.nga.geopackage.GeoPackageImpl.getTileDao(GeoPackageImpl.java:262)
        at mil.nga.geopackage.GeoPackageImpl.getTileDao(GeoPackageImpl.java:336)
        at mil.nga.mapcache.repository.GeoPackageRepository.regenerateTableList(GeoPackageRepository.java:457)
        at mil.nga.mapcache.viewmodel.GeoPackageViewModel.regenerateGeoPackageTableList(GeoPackageViewModel.java:458)
        at mil.nga.mapcache.viewmodel.GeoPackageViewModel.importGeoPackage(GeoPackageViewModel.java:630)
        at mil.nga.mapcache.load.ImportTask$ImportGeoTask.doInBackground(ImportTask.java:456)
        at mil.nga.mapcache.load.ImportTask$ImportGeoTask.doInBackground(ImportTask.java:321)
        at android.os.AsyncTask$2.call(AsyncTask.java:304)
        at java.util.concurrent.FutureTask.run(FutureTask.java:237)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
        at java.lang.Thread.run(Thread.java:762)
2022-04-11 09:24:08.352 19309-19942/? W/ProjectionFactory: Failed to create projection for authority: EPSG, code: 3857, definition: 
                    PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984"
                    ,SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]]
                    ,AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG",
                    "8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]]
                    ,AUTHORITY["EPSG","9122"]]AUTHORITY["EPSG","4326"]],PROJECTION[
                    "Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER[
                    "scale_factor",1],PARAMETER["false_easting",0],PARAMETER[
                    "false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS[
                    "X",EAST],AXIS["Y",NORTH]

    mil.nga.crs.CRSException: Unexpected end of text, null token
        at mil.nga.crs.wkt.TextReader.readExpectedToken(TextReader.java:318)
        at mil.nga.crs.wkt.CRSReader.readRightDelimiter(CRSReader.java:1159)
        at mil.nga.crs.wkt.CRSReader.readProjectedCompat(CRSReader.java:4097)
        at mil.nga.crs.wkt.CRSReader.readProjectedCompat(CRSReader.java:3996)
        at mil.nga.crs.wkt.CRSReader.read(CRSReader.java:812)
        at mil.nga.crs.wkt.CRSReader.read(CRSReader.java:117)
        at mil.nga.crs.wkt.CRSReader.read(CRSReader.java:99)
        at mil.nga.proj.ProjectionFactory.fromDefinitionParams(ProjectionFactory.java:1030)
        at mil.nga.proj.ProjectionFactory.getProjection(ProjectionFactory.java:654)
        at mil.nga.proj.ProjectionFactory.getProjection(ProjectionFactory.java:582)
        at mil.nga.proj.ProjectionFactory.getProjection(ProjectionFactory.java:536)
        at mil.nga.proj.ProjectionFactory.getProjection(ProjectionFactory.java:454)
        at mil.nga.geopackage.srs.SpatialReferenceSystem.getProjection(SpatialReferenceSystem.java:355)
        at mil.nga.geopackage.tiles.matrixset.TileMatrixSet.getProjection(TileMatrixSet.java:268)
        at mil.nga.geopackage.tiles.user.TileDao.<init>(TileDao.java:92)
        at mil.nga.geopackage.GeoPackageImpl.getTileDao(GeoPackageImpl.java:262)
        at mil.nga.geopackage.GeoPackageImpl.getTileDao(GeoPackageImpl.java:336)
        at mil.nga.mapcache.repository.GeoPackageRepository.regenerateTableList(GeoPackageRepository.java:457)
        at mil.nga.mapcache.viewmodel.GeoPackageViewModel.regenerateGeoPackageTableList(GeoPackageViewModel.java:458)
        at mil.nga.mapcache.viewmodel.GeoPackageViewModel.importGeoPackage(GeoPackageViewModel.java:630)
        at mil.nga.mapcache.load.ImportTask$ImportGeoTask.doInBackground(ImportTask.java:456)
        at mil.nga.mapcache.load.ImportTask$ImportGeoTask.doInBackground(ImportTask.java:321)
        at android.os.AsyncTask$2.call(AsyncTask.java:304)
        at java.util.concurrent.FutureTask.run(FutureTask.java:237)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
        at java.lang.Thread.run(Thread.java:762)

Steps to Reproduce:

  1. Load any raster Geopackage file with Pseudo Mercator projection

Test Files:

image

image

Additional Information:

bosborn commented 2 years ago

The Coordinate Reference System Well-Known Text in your GeoPackage(gpkg_spatial_ref_sys definition) does not appear to be valid. There is fallback logic for failed and/or invalid definitions. In your case, the error is logged and the projection is successfully retrieved through an authority/code lookup. Projections are cached to reduce re-parsing matching authority, code, and definitions.

Invalid:

PROJCS["WGS 84 / Pseudo-Mercator",
    GEOGCS["WGS 84",
        DATUM["WGS_1984",
            SPHEROID["WGS 84",6378137,298.257223563,
                AUTHORITY["EPSG","7030"]],
            AUTHORITY["EPSG","6326"]],
        PRIMEM["Greenwich",0,
            AUTHORITY["EPSG","8901"]],
        UNIT["degree",0.0174532925199433,
            AUTHORITY["EPSG","9122"]],
        AUTHORITY["EPSG","9122"]]
    AUTHORITY["EPSG","4326"]],
PROJECTION["Mercator_1SP"],
PARAMETER["central_meridian",0],
PARAMETER["scale_factor",1],
PARAMETER["false_easting",0],
PARAMETER["false_northing",0],
UNIT["metre",1,
    AUTHORITY["EPSG","9001"]],
AXIS["X",EAST],
AXIS["Y",NORTH]

Valid (by fixing the provided invalid, other valid versions exist):

PROJCS["WGS 84 / Pseudo-Mercator",
    GEOGCS["WGS 84",
        DATUM["WGS_1984",
            SPHEROID["WGS 84",6378137,298.257223563,
                AUTHORITY["EPSG","7030"]],
            AUTHORITY["EPSG","6326"]],
        PRIMEM["Greenwich",0,
            AUTHORITY["EPSG","8901"]],
        UNIT["degree",0.0174532925199433,
            AUTHORITY["EPSG","9122"]],
        AUTHORITY["EPSG","4326"]],
    PROJECTION["Mercator_1SP"],
    PARAMETER["central_meridian",0],
    PARAMETER["scale_factor",1],
    PARAMETER["false_easting",0],
    PARAMETER["false_northing",0],
    UNIT["metre",1,
        AUTHORITY["EPSG","9001"]],
    AXIS["X",EAST],
    AXIS["Y",NORTH]]

If you have control over the GeoPackage creation, update the definition. If you are provided the GeoPackage, I suggest contacting/informing the producer.

eclectice commented 2 years ago

You are correct. It no longer displays the exception when I altered the definition of my Geopackage file in DB Browser for the SQLite tool for EPSG:3857 entry as follows:


PROJCS["WGS 84 / Pseudo-Mercator",
    GEOGCS["WGS 84",DATUM["WGS_1984",
    SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],
    AUTHORITY["EPSG","6326"]],
    PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],
    UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],
    AUTHORITY["EPSG","4326"]], 
    PROJECTION["Mercator_1SP"],
    PARAMETER["central_meridian",0],
    PARAMETER["scale_factor",1],
    PARAMETER["false_easting",0],
    PARAMETER["false_northing",0],
    UNIT["metre",1,AUTHORITY["EPSG","9001"]],
    AXIS["X",EAST],
    AXIS["Y",NORTH]
]

image

The other Geopackage tables The gpkg_content shows the coordinates in EPGS:3857: ![image](https://user-images.githubusercontent.com/1452656/162863980-a21a2ac4-fbd8-487e-a617-2dd80ef23aba.png) The gpkg_tile_matrix_set shows the coordinates in EPGS:3857: ![image](https://user-images.githubusercontent.com/1452656/162872290-41c1be9b-9da8-4e1d-9542-b14f9b69ff46.png) The gpkg_tile_matrix shows the coordinates in EPGS:3857: ![image](https://user-images.githubusercontent.com/1452656/162872769-2a36f0dd-29d2-4e4e-a505-8b2882941456.png)

The map with the CORRECT Coordinate Reference System Well-Known Text for EPSG:3857, however, has been relocated lower; it is intended to be inside the red bounding box provided by its tile data at the max zoom, as shown in the screenshot below:

The screenshot with the **CORRECT** Coordinate Reference System Well-Known Text for EPSG:3857 ![device-2022-04-12-113822](https://user-images.githubusercontent.com/1452656/162875659-07da7d22-0cec-4416-94bb-127ca58eb41a.png)

The red bounding box registered coordinates at the max zoom level (in this case 19) as follows:

2022-04-12 11:20:59.200 12547-12547/? I/OsmDroid: Geopackage database(0:5312): tile(0:tiles) zoom(0:19), CRS (3857) => (4326), bounding box from (N=max_y=181232.194064, E=max_x=12310642.027497, S=min_y=150963.130863, W=min_x=12280525.838353) => bounding box to (N=max_y=1.638782, E=max_x=110.588379, S=min_y=1.365134, W=min_x=110.317841)

The logcat output above comes from this code snippet:

  TileDao tileDao = open.getTileDao(tileTables.get(k));
  final long MIN_ZOOM = tileDao.getMinZoom();
  final long MAX_ZOOM = tileDao.getMaxZoom();

  if (tileDao.getProjection().getCode().equals(String.valueOf(ProjectionConstants.EPSG_WEB_MERCATOR))) {

      mil.nga.geopackage.BoundingBox boundingBox3 = tileDao.getBoundingBox(MAX_ZOOM);
      double northOri = boundingBox3.getMaxLatitude(); //MaxY
      double eastOri = boundingBox3.getMaxLongitude(); //MaxX
      double southOri = boundingBox3.getMinLatitude(); //MinY
      double westOri = boundingBox3.getMinLongitude(); //MinX

      mil.nga.geopackage.BoundingBox boundingBox = null;
      if (northOri > tileSystem.getMaxLatitude() || southOri < tileSystem.getMinLatitude()) {
          ProjectionTransform transform = tileDao.getProjection().getTransformation(ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM);
          mil.nga.geopackage.BoundingBox boundingBox2 = new mil.nga.geopackage.BoundingBox(westOri, southOri, eastOri, northOri);
          boundingBox = boundingBox2.transform(transform);
      } else {
          boundingBox = boundingBox3;
      }

      double north = Math.min(tileSystem.getMaxLatitude(), boundingBox.getMaxLatitude()); //MaxY
      double east = boundingBox.getMaxLongitude(); //MaxX
      double south = Math.max(tileSystem.getMinLatitude(), boundingBox.getMinLatitude()); //MinY
      double west = boundingBox.getMinLongitude(); //MinX
      org.osmdroid.util.BoundingBox bounds = new org.osmdroid.util.BoundingBox(north, east, south, west);

      Log.i(IMapView.LOGTAG, String.format("Geopackage database(%d:%s): tile(%d:%s) zoom(%d:%d), CRS (%s) => (%s), bounding box from (N=max_y=%f, E=max_x=%f, S=min_y=%f, W=min_x=%f) => bounding box to (N=max_y=%f, E=max_x=%f, S=min_y=%f, W=min_x=%f)",
              i, databases.get(i), k, tileTables.get(k),
              (int) MIN_ZOOM, (int) MAX_ZOOM,
              tileDao.getProjection().getCode(), String.valueOf(ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM),
              northOri, eastOri, southOri, westOri,
              north, east, south, west));

      //must project in WGS84 in OsmDroid
      srcs.add(new SafGeopackageRasterTileSource(databases.get(i), tileTables.get(k), (int) MIN_ZOOM, (int) MAX_ZOOM, bounds));

  }

If I import the Geopackage file with the INCORRECT Coordinate Reference System Well-Known Text for EPSG:3857, I got this screenshot in which the tiles are within the red bounding box:

The screenshot with the **INCORRECT** Coordinate Reference System Well-Known Text for EPSG:3857 ![device-2022-04-12-120039](https://user-images.githubusercontent.com/1452656/162878006-547f5808-c47b-4108-bd25-55f66e3a17cb.png)

The red bounding box registered coordinates at the max zoom level (in this case 19) as follows:

2022-04-12 11:54:22.510 30810-30810/? I/OsmDroid: Geopackage database(0:5312.corrupt): tile(0:tiles) zoom(0:19), CRS (3857) => (4326), bounding box from (N=max_y=181232.194064, E=max_x=12310642.027497, S=min_y=150963.130863, W=min_x=12280525.838353) => bounding box to (N=max_y=1.627817, E=max_x=110.588379, S=min_y=1.355998, W=min_x=110.317841)

There is a difference shown in the North and South component of the red bounding box for the CORRECT and INCORRECT Coordinate Reference System Well-Known Text for EPSG:3857.

Why is that?

bosborn commented 2 years ago

EPSG 3857 is a special case that relies on the equator radius instead of the known ellipsoid. Add AUTHORITY["EPSG","3857"] at the end of the CRS WKT to fix the projections.

PROJCS["WGS 84 / Pseudo-Mercator",
    GEOGCS["WGS 84",
        DATUM["WGS_1984",
            SPHEROID["WGS 84",6378137,298.257223563,
                AUTHORITY["EPSG","7030"]],
            AUTHORITY["EPSG","6326"]],
        PRIMEM["Greenwich",0,
            AUTHORITY["EPSG","8901"]],
        UNIT["degree",0.0174532925199433,
            AUTHORITY["EPSG","9122"]],
        AUTHORITY["EPSG","4326"]],
    PROJECTION["Mercator_1SP"],
    PARAMETER["central_meridian",0],
    PARAMETER["scale_factor",1],
    PARAMETER["false_easting",0],
    PARAMETER["false_northing",0],
    UNIT["metre",1,
        AUTHORITY["EPSG","9001"]],
    AXIS["X",EAST],
    AXIS["Y",NORTH],
    AUTHORITY["EPSG","3857"]]

Project Output for coordinate [12310642.027497, 181232.194064]:

w/o AUTHORITY["EPSG","3857"] $ proj From Projection: PROJCS["WGS 84 / Pseudo-Mercator", GEOGCS["WGS 84", DATUM["WGS_1984", SPHEROID["WGS 84",6378137,298.257223563, AUTHORITY["EPSG","7030"]], AUTHORITY["EPSG","6326"]], PRIMEM["Greenwich",0, AUTHORITY["EPSG","8901"]], UNIT["degree",0.0174532925199433, AUTHORITY["EPSG","9122"]], AUTHORITY["EPSG","4326"]], PROJECTION["Mercator_1SP"], PARAMETER["central_meridian",0], PARAMETER["scale_factor",1], PARAMETER["false_easting",0], PARAMETER["false_northing",0], UNIT["metre",1, AUTHORITY["EPSG","9001"]], AXIS["X",EAST], AXIS["Y",NORTH]] To Projection: 4326 PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH]] -> EPSG:4326 Enter 'Inverse' to invert transformation Coordinate(s): 12310642.027497,181232.194064 [12310642.027497, 181232.194064] -> [110.58837890624689, **1.6387821648748968**]
w/ AUTHORITY["EPSG","3857"] $ proj From Projection: PROJCS["WGS 84 / Pseudo-Mercator", GEOGCS["WGS 84", DATUM["WGS_1984", SPHEROID["WGS 84",6378137,298.257223563, AUTHORITY["EPSG","7030"]], AUTHORITY["EPSG","6326"]], PRIMEM["Greenwich",0, AUTHORITY["EPSG","8901"]], UNIT["degree",0.0174532925199433, AUTHORITY["EPSG","9122"]], AUTHORITY["EPSG","4326"]], PROJECTION["Mercator_1SP"], PARAMETER["central_meridian",0], PARAMETER["scale_factor",1], PARAMETER["false_easting",0], PARAMETER["false_northing",0], UNIT["metre",1, AUTHORITY["EPSG","9001"]], AXIS["X",EAST], AXIS["Y",NORTH]**, AUTHORITY["EPSG","3857"]**] To Projection: 4326 EPSG:3857 -> EPSG:4326 Enter 'Inverse' to invert transformation Coordinate(s): 12310642.027497,181232.194064 [12310642.027497, 181232.194064] -> [110.58837890624689, **1.6278174666801366**]
eclectice commented 2 years ago

Wow, it's so simple once I learn about it. Thank you; it now works as intended, as shown below. I can finally rest in peace.

device-2022-04-13-091751