Select to view content in your preferred language

BaseTileLayer does not show up correctly in WGS84 projection.

565
5
10-15-2024 01:56 PM
imsolost
Emerging Contributor

I am attempting to display a downloaded basemap in my angular app. The map does appear, however spatial reference seems to be off, although I am manually setting the spatial reference anywhere I can. The system in use for the map is WGS84 (EPSG 4326). What happens as shown in the screenshot is that the y-axis extends well beyond what it should, and the x-axis completely cuts off any part of the map that does not fit inside the red box. The red box is drawn to outline the coordinates x = -180 to 180 and y = -90 to 90. It seems that because of this, fetchTile in BaseTileLayer.createSubclass is not grabbing the correct columns. Since it would seem that I am setting the spatical reference to the same spatial reference as my offline basemap I'm not sure why the map isn't being mapped to the proper coordinates. The basemap I am using is in .gpkg format. Any ideas on why it isn't showing up properly? Thanks for any help you can offer!

 

How I set my Spatial Reference:

export const SPATIAL_REFERENCE: SpatialReference = SpatialReference.WGS84;

The code for getting the tiles:

private updateBasemapToGeopackage(): void {
// @ts-ignore
const GeoPackageTileLayer = BaseTileLayer.createSubclass({
properties: {
geoPackage: null, // This will hold the opened GeoPackage

tileDao: null, // This will hold the Tile DAO (Tile Data Access Object)
      },

fetchTile: function (level: number, row: number, col: number) {
const tileDao: TileDao<TileRow> = this.tileDao;

const canvas = document.createElement('canvas');

const context = canvas.getContext('2d');

return new Promise((resolve, reject) => {
// Step 3: Fetch the tile from the GeoPackage for the given zoom level, column, and row
const tile = tileDao.queryForTile(col, row, level);
console.log('COL: ', col, ' ROW: ', row, ' LEVEL: ', level);
if (tile) {
// Create an Image element
const image = new Image();

// Step 4: Draw the image onto the canvas when it loads
image.onload = function () {
canvas.width = image.width;
canvas.height = image.height;
if (context) {
context.drawImage(image, 0, 0);
              }
resolve(canvas); // Resolve the canvas as the tile
            };

// Handle error if tile data cannot be loaded
image.onerror = function () {
reject(new Error('Tile image could not be loaded'));
            };

// Set the tile source as the raw tile data in a Blob format
const blob = new Blob([tile.tileData], { type: 'image/png' });
image.src = URL.createObjectURL(blob);
          } else {
// If no tile exists for this level/col/row, return null (empty tile)
resolve(null);
          }
        });
      },
    });

fetch('./test.gpkg')
      .then((response) => response.arrayBuffer())
      .then((data) => {
// return (window as any).GeoPackage.open(data);
return GeoPackageAPI.open(new Uint8Array(data));
// return GeoPackageAPI.open(data as Uint8Array);
      })
      .then((geoPackage: GeoPackage) => {
const tileTables = geoPackage.getTileTables();
console.log(tileTables);
// Use the first tile table (you can adapt this to load multiple if needed)
if (tileTables && tileTables.length) {
const tileDao = geoPackage.getTileDao(tileTables[0]);
// Step 6: Create an instance of the custom tile layer
const tileLayer = new GeoPackageTileLayer({
geoPackage: geoPackage,
tileDao: tileDao,
spatialReference: SPATIAL_REFERENCE,
tileInfo: TileInfo.create({
spatialReference: SPATIAL_REFERENCE,
            }),
          });

// Step 7: Add the tile layer to the map
// this.map.add(tileLayer);

const basemap = new Basemap({
baseLayers: [tileLayer],
title: 'Put the title here',
          });
this.map.basemap = basemap;
const line = new Polyline({
paths: [
              [
                [-180, -90],
                [-180, 90],
              ],
              [
                [-180, 90],
                [180, 90],
              ],
              [
                [180, 90],
                [180, -90],
              ],
              [
                [180, -90],
                [-180, -90],
              ],
            ],
spatialReference: SPATIAL_REFERENCE,
          });

const lineGraphic = new Graphic({
geometry: line,
symbol: symbolUtils.getPolylineSymbol(false, '#FF0000'),
          });
this.mapView.graphics.add(lineGraphic);
        }
      });
  }
How I'm setting up the MapView and such:
 
constructor() {
esriConfig.request.interceptors?.push({
urls: [],
before: (params: any) => {
params.requestOptions.withCredentials = true;
      },
    });
esriConfig.log.level = 'info';

// Handle 2D <-> 3D transformation
effect(() => {
const sharedProps = {
map: this.map,
container: 'viewDiv',
spatialReference: SPATIAL_REFERENCE,
popup: {
dockEnabled: false,
visibleElements: {
closeButton: false,
collapseButton: false,
          },
dockOptions: {
buttonEnabled: false,
breakpoint: true,
          },
        },
      };

if (this.isViewInitialized()) {
let savedGraphics: Graphic[] = [];
let currentMapViewExtent: Extent | null = null;
let currentSceneCamera: Viewpoint | null = null;
if (this.view) {
savedGraphics = this.view.graphics.toArray();
if (this.isMap2d()) {
currentSceneCamera = (this.view as SceneView).viewpoint.clone();
          } else {
currentMapViewExtent = this.view.extent;
          }
        }
if (this.isMap2d()) {
this.view = new MapView({
...sharedProps,
zoom: 10,
extent: {
xmin: -180000,
ymin: -90,
xmax: 180000,
ymax: 90,
spatialReference: SPATIAL_REFERENCE,
            },
constraints: {
snapToZoom: false,
rotationEnabled: false,
minZoom: 2,
            },
          });
this.view.constraints.geometry = this.view.extent.clone();

this.goToView(currentSceneCamera);
        } else {
this.view = new SceneView({
...sharedProps,
environment: {
lighting: {
type: 'virtual',
              },
            },
          });

this.goToView(currentMapViewExtent);
        }

this.updateAllGraphicDimensionChanges(savedGraphics);
this.handleViewSetup();
      }
    });
  }
How my Map looks:
imsolost_0-1729025696982.pngimsolost_1-1729025714845.png

 

 

0 Kudos
5 Replies
JoelBennett
MVP Regular Contributor

Are you accounting for the fact that those tiles are (likely) 512x512, as opposed to the "standard" 256x256?

0 Kudos
imsolost
Emerging Contributor

The tiles are 256x256.

0 Kudos
MatteoRizzi1
Esri Contributor

Hi @imsolost

it would be helpful to have a demo code to dig into your issue and better understand how you're setting up the app.

I know that sharing a sample with an Angular app can be challenging, but perhaps you could create a simplified version on StackBlitz (or another browser-based IDE) and then share the link here (feel free to send it through a private message directly to me).

0 Kudos
imsolost
Emerging Contributor

I just sent you a PM with the link. Thanks in advance!

0 Kudos
imsolost
Emerging Contributor

Here is a stackblitz example. Please be aware that the map is only shown for one zoom level due to the fact that stackblitz will not allow me to upload a file with multiple zoom levels because the file size is too big without a paid subscription. When it starts up you will only see half of the map (from roughly -180 to 0 longitude), however the entire longitude is present in the geopackage.

https://stackblitz.com/edit/stackblitz-starters-xaacuv?file=src%2Fapp%2Fapp.component.ts 

0 Kudos