Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix GeoPackage support implementation. #243

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.List;

import gov.nasa.worldwind.draw.BasicDrawableTerrain;
import gov.nasa.worldwind.geom.Location;
import gov.nasa.worldwind.geom.Range;
import gov.nasa.worldwind.geom.Sector;
import gov.nasa.worldwind.geom.Vec3;
Expand All @@ -32,7 +33,7 @@
public class BasicTessellator implements Tessellator, TileFactory {

// ~0.6 meter resolution
protected LevelSet levelSet = new LevelSet(new Sector().setFullSphere(), 90, 20, 32, 32);
protected LevelSet levelSet = new LevelSet(new Sector().setFullSphere(), new Location(-90, -180), new Location(90, 90), 20, 32, 32);

protected double detailControl = 80;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public BlueMarbleLandsatLayer(String serviceAddress) {

@Override
public Tile createTile(Sector sector, Level level, int row, int column) {
double radiansPerPixel = Math.toRadians(level.tileDelta) / level.tileHeight;
double radiansPerPixel = Math.toRadians(level.tileDelta.latitude) / level.tileHeight;
double metersPerPixel = radiansPerPixel * WorldWind.WGS84_SEMI_MAJOR_AXIS;

if (metersPerPixel < 2.0e3) { // switch to Landsat at 2km resolution
Expand Down
28 changes: 21 additions & 7 deletions worldwind/src/main/java/gov/nasa/worldwind/layer/LayerFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.util.SparseArray;

import java.io.BufferedInputStream;
import java.io.InputStream;
Expand All @@ -22,13 +23,15 @@
import java.util.concurrent.RejectedExecutionException;

import gov.nasa.worldwind.WorldWind;
import gov.nasa.worldwind.geom.Location;
import gov.nasa.worldwind.geom.Sector;
import gov.nasa.worldwind.ogc.WmsLayerConfig;
import gov.nasa.worldwind.ogc.WmsTileFactory;
import gov.nasa.worldwind.ogc.gpkg.GeoPackage;
import gov.nasa.worldwind.ogc.gpkg.GpkgContent;
import gov.nasa.worldwind.ogc.gpkg.GpkgSpatialReferenceSystem;
import gov.nasa.worldwind.ogc.gpkg.GpkgTileFactory;
import gov.nasa.worldwind.ogc.gpkg.GpkgTileMatrix;
import gov.nasa.worldwind.ogc.gpkg.GpkgTileMatrixSet;
import gov.nasa.worldwind.ogc.gpkg.GpkgTileUserMetrics;
import gov.nasa.worldwind.ogc.wms.WmsCapabilities;
Expand Down Expand Up @@ -258,6 +261,13 @@ protected void createFromGeoPackageAsync(String pathName, Layer layer, Callback
continue;
}

SparseArray<GpkgTileMatrix> tileMatrix = geoPackage.getTileMatrix(content.getTableName());
if (tileMatrix == null || tileMatrix.size() == 0) {
Logger.logMessage(Logger.WARN, "LayerFactory", "createFromGeoPackageAsync",
"Unsupported GeoPackage tile matrix");
continue;
}

GpkgTileUserMetrics tileMetrics = geoPackage.getTileUserMetrics(content.getTableName());
if (tileMetrics == null) {
Logger.logMessage(Logger.WARN, "LayerFactory", "createFromGeoPackageAsync",
Expand All @@ -266,14 +276,18 @@ protected void createFromGeoPackageAsync(String pathName, Layer layer, Callback
}

LevelSetConfig config = new LevelSetConfig();
config.sector.set(content.getMinY(), content.getMinX(),
content.getMaxY() - content.getMinY(), content.getMaxX() - content.getMinX());
config.firstLevelDelta = 180;
config.numLevels = tileMetrics.getMaxZoomLevel() + 1; // zero when there are no zoom levels, (0 = -1 + 1)
config.tileWidth = 256;
config.tileHeight = 256;
config.sector.set(tileMatrixSet.getMinY(), tileMatrixSet.getMinX(),
tileMatrixSet.getMaxY() - tileMatrixSet.getMinY(),
tileMatrixSet.getMaxX() - tileMatrixSet.getMinX());
config.tileOrigin.set(tileMatrixSet.getMinY(), tileMatrixSet.getMinX());
config.firstLevelDelta.set(
(tileMatrixSet.getMaxY() - tileMatrixSet.getMinY()) / tileMatrix.valueAt(0).getMatrixHeight(),
(tileMatrixSet.getMaxX() - tileMatrixSet.getMinX()) / tileMatrix.valueAt(0).getMatrixWidth()
);
config.numLevels = tileMatrix.keyAt(tileMatrix.size() - 1) - tileMatrix.keyAt(0) + 1;

TiledSurfaceImage surfaceImage = new TiledSurfaceImage();
surfaceImage.setDisplayName(content.getIdentifier());
surfaceImage.setLevelSet(new LevelSet(config));
surfaceImage.setTileFactory(new GpkgTileFactory(content));
gpkgRenderables.addRenderable(surfaceImage);
Expand Down Expand Up @@ -670,7 +684,7 @@ protected LevelSet createWmtsLevelSet(WmtsLayer wmtsLayer, CompatibleTileMatrixS
}
int imageSize = tileMatrixSet.getTileMatrices().get(0).getTileHeight();

return new LevelSet(boundingBox, 90.0, compatibleTileMatrixSet.tileMatrices.size(), imageSize, imageSize);
return new LevelSet(boundingBox, new Location(-90, -180), new Location(90, 90), compatibleTileMatrixSet.tileMatrices.size(), imageSize, imageSize);
}

protected String buildWmtsKvpTemplate(String kvpServiceAddress, String layer, String format, String styleIdentifier, String tileMatrixSet) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package gov.nasa.worldwind.layer.mercator;

import android.graphics.Bitmap;

import java.util.Collection;

import gov.nasa.worldwind.geom.Location;
import gov.nasa.worldwind.render.ImageTile;
import gov.nasa.worldwind.util.DownloadPostprocessor;
import gov.nasa.worldwind.util.Level;
import gov.nasa.worldwind.util.LevelSet;
import gov.nasa.worldwind.util.Logger;
import gov.nasa.worldwind.util.Tile;
import gov.nasa.worldwind.util.TileFactory;

class MercatorImageTile extends ImageTile implements DownloadPostprocessor<Bitmap> {

/**
* Constructs a tile with a specified sector, level, row and column.
*
* @param sector the sector spanned by the tile
* @param level the tile's level in a {@link LevelSet}
* @param row the tile's row within the specified level
* @param column the tile's column within the specified level
*/
MercatorImageTile(MercatorSector sector, Level level, int row, int column) {
super(sector, level, row, column);
}

/**
* Creates all Mercator tiles for a specified level within a {@link LevelSet}.
*
* @param level the level to create the tiles for
* @param tileFactory the tile factory to use for creating tiles.
* @param result an pre-allocated Collection in which to store the results
*/
static void assembleMercatorTilesForLevel(Level level, TileFactory tileFactory, Collection<Tile> result) {
if (level == null) {
throw new IllegalArgumentException(
Logger.logMessage(Logger.ERROR, "Tile", "assembleTilesForLevel", "missingLevel"));
}

if (tileFactory == null) {
throw new IllegalArgumentException(
Logger.logMessage(Logger.ERROR, "Tile", "assembleTilesForLevel", "missingTileFactory"));
}

if (result == null) {
throw new IllegalArgumentException(
Logger.logMessage(Logger.ERROR, "Tile", "assembleTilesForLevel", "missingResult"));
}

// NOTE LevelSet.sector is final Sector attribute and thus can not be cast to MercatorSector!
MercatorSector sector = MercatorSector.fromSector(level.parent.sector);
Location tileOrigin = level.parent.tileOrigin;
double dLat = level.tileDelta.latitude;
double dLon = level.tileDelta.longitude;

int firstRow = Tile.computeRow(dLat, sector.minLatitude(), tileOrigin.latitude);
int lastRow = Tile.computeLastRow(dLat, sector.maxLatitude(), tileOrigin.latitude);
int firstCol = Tile.computeColumn(dLon, sector.minLongitude(), tileOrigin.longitude);
int lastCol = Tile.computeLastColumn(dLon, sector.maxLongitude(), tileOrigin.longitude);

double dLatPercent = dLat / sector.deltaLatitude() * (sector.maxLatPercent() - sector.minLatPercent());
double firstRowPercent = MercatorSector.gudermannianInverse(tileOrigin.latitude) + firstRow * dLatPercent;
double firstColLon = tileOrigin.longitude + firstCol * dLon;

double d1 = firstRowPercent;
for (int row = firstRow; row <= lastRow; row++) {
double d2 = d1 + dLatPercent;
double t1 = firstColLon;
for (int col = firstCol; col <= lastCol; col++) {
double t2 = t1 + dLon;
result.add(tileFactory.createTile(new MercatorSector(d1, d2, t1, t2), level, row, col));
t1 = t2;
}
d1 = d2;
}
}

/**
* Returns the four children formed by subdividing this tile. This tile's sector is subdivided into four quadrants
* as follows: Southwest; Southeast; Northwest; Northeast. A new tile is then constructed for each quadrant and
* configured with the next level within this tile's LevelSet and its corresponding row and column within that
* level. This returns null if this tile's level is the last level within its {@link LevelSet}.
*
* @param tileFactory the tile factory to use to create the children
*
* @return an array containing the four child tiles, or null if this tile's level is the last level
*/
@Override
public Tile[] subdivide(TileFactory tileFactory) {
if (tileFactory == null) {
throw new IllegalArgumentException(
Logger.logMessage(Logger.ERROR, "Tile", "subdivide", "missingTileFactory"));
}

Level childLevel = this.level.nextLevel();
if (childLevel == null) {
return null;
}

MercatorSector sector = (MercatorSector) this.sector;

double d0 = sector.minLatPercent();
double d2 = sector.maxLatPercent();
double d1 = d0 + (d2 - d0) / 2.0;

double t0 = sector.minLongitude();
double t2 = sector.maxLongitude();
double t1 = 0.5 * (t0 + t2);

int northRow = 2 * this.row;
int southRow = northRow + 1;
int westCol = 2 * this.column;
int eastCol = westCol + 1;

Tile[] children = new Tile[4];
children[0] = tileFactory.createTile(new MercatorSector(d0, d1, t0, t1), childLevel, northRow, westCol);
children[1] = tileFactory.createTile(new MercatorSector(d0, d1, t1, t2), childLevel, northRow, eastCol);
children[2] = tileFactory.createTile(new MercatorSector(d1, d2, t0, t1), childLevel, southRow, westCol);
children[3] = tileFactory.createTile(new MercatorSector(d1, d2, t1, t2), childLevel, southRow, eastCol);

return children;
}

@Override
public Bitmap process(Bitmap resource) {
// Re-project mercator tile to equirectangular
int[] pixels = new int[resource.getWidth() * resource.getHeight()];
int[] result = new int[resource.getWidth() * resource.getHeight()];
resource.getPixels(pixels, 0, resource.getWidth(), 0, 0, resource.getWidth(), resource.getHeight());
double miny = ((MercatorSector) sector).minLatPercent();
double maxy = ((MercatorSector) sector).maxLatPercent();
for (int y = 0; y < resource.getHeight(); y++) {
double sy = 1.0 - y / (double) (resource.getHeight() - 1);
double lat = sy * (sector.maxLatitude() - sector.minLatitude()) + sector.minLatitude();
double dy = 1.0 - (MercatorSector.gudermannianInverse(lat) - miny) / (maxy - miny);
dy = Math.max(0.0, Math.min(1.0, dy));
int iy = (int) (dy * (resource.getHeight() - 1));
for (int x = 0; x < resource.getWidth(); x++) {
result[x + y * resource.getWidth()] = pixels[x + iy * resource.getWidth()];
}
}
return Bitmap.createBitmap(result, resource.getWidth(), resource.getHeight(), resource.getConfig());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package gov.nasa.worldwind.layer.mercator;

import gov.nasa.worldwind.geom.Sector;

public class MercatorSector extends Sector {

private final double minLatPercent, maxLatPercent;

public MercatorSector(double minLatPercent, double maxLatPercent, double minLongitude, double maxLongitude) {
this.minLatPercent = minLatPercent;
this.maxLatPercent = maxLatPercent;
this.minLatitude = gudermannian(minLatPercent);
this.maxLatitude = gudermannian(maxLatPercent);
this.minLongitude = minLongitude;
this.maxLongitude = maxLongitude;
}

public static MercatorSector fromDegrees(double minLatPercent, double maxLatPercent,
double minLongitude, double maxLongitude) {
return new MercatorSector(minLatPercent, maxLatPercent, minLongitude, maxLongitude);
}

static MercatorSector fromSector(Sector sector) {
return new MercatorSector(gudermannianInverse(sector.minLatitude()),
gudermannianInverse(sector.maxLatitude()),
sector.minLongitude(), sector.maxLongitude());
}

static double gudermannianInverse(double latitude) {
return Math.log(Math.tan(Math.PI / 4.0 + Math.toRadians(latitude) / 2.0)) / Math.PI;
}

private static double gudermannian(double percent) {
return Math.toDegrees(Math.atan(Math.sinh(percent * Math.PI)));
}

double minLatPercent() {
return minLatPercent;
}

double maxLatPercent()
{
return maxLatPercent;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package gov.nasa.worldwind.layer.mercator;

import gov.nasa.worldwind.WorldWind;
import gov.nasa.worldwind.geom.Location;
import gov.nasa.worldwind.geom.Sector;
import gov.nasa.worldwind.layer.RenderableLayer;
import gov.nasa.worldwind.render.ImageOptions;
import gov.nasa.worldwind.render.ImageSource;
import gov.nasa.worldwind.util.Level;
import gov.nasa.worldwind.util.LevelSet;
import gov.nasa.worldwind.util.Tile;
import gov.nasa.worldwind.util.TileFactory;

public abstract class MercatorTiledImageLayer extends RenderableLayer implements TileFactory {

private final int firstLevelOffset;

public MercatorTiledImageLayer(String name, int numLevels, int firstLevelOffset, int tileSize, boolean overlay) {
super(name);
this.setPickEnabled(false);
this.firstLevelOffset = firstLevelOffset;

MercatorSector sector = new MercatorSector(-1.0, 1.0, -180.0, 180.0);
Location tileOrigin = new Location(sector.minLatitude(), sector.minLongitude());
int n = 1 << firstLevelOffset;
Location firstLevelDelta = new Location(sector.deltaLatitude() / n, sector.deltaLongitude() / n);
MercatorTiledSurfaceImage surfaceImage = new MercatorTiledSurfaceImage();
surfaceImage.setLevelSet(new LevelSet(sector, tileOrigin, firstLevelDelta, numLevels - firstLevelOffset, tileSize, tileSize));
surfaceImage.setTileFactory(this);
if(!overlay) {
surfaceImage.setImageOptions(new ImageOptions(WorldWind.RGB_565)); // reduce memory usage by using a 16-bit configuration with no alpha
}
this.addRenderable(surfaceImage);
}

@Override
public Tile createTile(Sector sector, Level level, int row, int column) {
MercatorImageTile tile = new MercatorImageTile((MercatorSector) sector, level, row, column);
tile.setImageSource(ImageSource.fromUrl(getImageSourceUrl(column, (1 << (level.levelNumber + firstLevelOffset)) - 1 - row, level.levelNumber + firstLevelOffset), tile));
return tile;
}

protected abstract String getImageSourceUrl(int x, int y, int z);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package gov.nasa.worldwind.layer.mercator;

import gov.nasa.worldwind.shape.TiledSurfaceImage;
import gov.nasa.worldwind.util.Level;

public class MercatorTiledSurfaceImage extends TiledSurfaceImage {

@Override
protected void createTopLevelTiles() {
Level firstLevel = this.levelSet.firstLevel();
if (firstLevel != null) {
MercatorImageTile.assembleMercatorTilesForLevel(firstLevel, this.tileFactory, this.topLevelTiles);
}
}

}
Loading