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-ignoreconst GeoPackageTileLayer = BaseTileLayer.createSubclass({properties: {geoPackage: null, // This will hold the opened GeoPackagetileDao: 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 rowconst tile = tileDao.queryForTile(col, row, level);console.log('COL: ', col, ' ROW: ', row, ' LEVEL: ', level);if (tile) {// Create an Image elementconst image = new Image();// Step 4: Draw the image onto the canvas when it loadsimage.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 loadedimage.onerror = function () {reject(new Error('Tile image could not be loaded'));};// Set the tile source as the raw tile data in a Blob formatconst 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 layerconst 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);}});}
constructor() {esriConfig.request.interceptors?.push({urls: [],before: (params: any) => {params.requestOptions.withCredentials = true;},});esriConfig.log.level = 'info';// Handle 2D <-> 3D transformationeffect(() => {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();}});}
Are you accounting for the fact that those tiles are (likely) 512x512, as opposed to the "standard" 256x256?
The tiles are 256x256.
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).
I just sent you a PM with the link. Thanks in advance!
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