ngageoint / geopackage-ios

GeoPackage iOS Library
http://ngageoint.github.io/geopackage-ios
MIT License
50 stars 19 forks source link

TileFeature Misaligned #41

Closed kttary closed 6 years ago

kttary commented 6 years ago

I'm trying to insert the zoom level 0 of osm (attached) to a tiletable with following values:

tileMatrixSet.minX = -180 tileMatrixSet.minY = -85 tileMatrixSet.maxX = 180 tileMatrixSet.maxY = 85

Unfortunately the result look misaligned. What is the proper value for minX, minY, maxX and maxY? tile

screen shot 2018-07-28 at 09 00 04
kttary commented 6 years ago

instead of latlong, i tried web mercator. Both did not give me correct result. Here is my snippet:

let srs2: GPKGSpatialReferenceSystem = srsDao.query(forOrganization: PROJ_AUTHORITY_EPSG, andCoordsysId: 3857)
let trans: SFPProjectionTransform = SFPProjectionTransform(fromEpsg: 4326, andToEpsg: 3857)
let myEnvelope = SFGeometryEnvelope(minX: -180, andMinY: -85, andMaxX: 0, andMaxY: 85)
let envelope2 = trans.transform(with: myEnvelope)

geoPackage.createTileMatrixSetTable()
geoPackage.createTileMatrixTable()

contents = GPKGContents()
contents.tableName = "MYTILE"
contents.setContentsDataType(GPKG_CDT_TILES)
contents.identifier = "MYTILE"
contents.theDescription = "MY FIRST TILE"
contents.lastChange = Date()
contents.minX = envelope2?.minX
contents.minY = envelope2?.minY
contents.maxX = envelope2?.maxX
contents.maxY = envelope2?.maxY
contents.setSrs(srs2)

let tileColumns = GPKGTileTable.createRequiredColumns()
let tileTable: GPKGTileTable = GPKGTileTable(table: "MYTILE", andColumns: tileColumns)
geoPackage.createTileTable(tileTable)

contentsDao.create(contents);

let tileMatrixSetDao: GPKGTileMatrixSetDao = geoPackage.getTileMatrixSetDao()
let tileMatrixSet: GPKGTileMatrixSet = GPKGTileMatrixSet()
tileMatrixSet.setContents(contents)
tileMatrixSet.setSrs(srs2)
tileMatrixSet.minX = envelope2?.minX
tileMatrixSet.minY = envelope2?.minY
tileMatrixSet.maxX = envelope2?.maxX
tileMatrixSet.maxY = envelope2?.maxY
tileMatrixSetDao.create(tileMatrixSet)

let tileMatrixDao: GPKGTileMatrixDao = geoPackage.getTileMatrixDao()

let fm = FileManager.default
let myPath: String = Bundle.main.path(forResource:"tile", ofType: "png")!
let tilePathData: Data? = fm.contents(atPath: myPath)
let image: UIImage = GPKGImageConverter.toImage(tilePathData)

let tileWidth : Int = Int(image.size.width);
let tileHeight : Int = Int(image.size.height);

var matrixWidthAndHeight = 2;
var pixelXSize: Double = (tileMatrixSet.maxX.doubleValue - tileMatrixSet.minX.doubleValue) / (Double)(matrixWidthAndHeight * tileWidth)
var pixelYSize: Double = (tileMatrixSet.maxY.doubleValue - tileMatrixSet.minY.doubleValue) / (Double)(matrixWidthAndHeight * tileHeight)

let tileData: Data = GPKGImageConverter.toData(image, andFormat: GPKGCompressFormats.fromName("png"))

            for zoom in 0..<2 {
                let tileMatrix: GPKGTileMatrix = GPKGTileMatrix()
                tileMatrix.setContents(contents)
                tileMatrix.zoomLevel = zoom as NSNumber
                tileMatrix.matrixWidth = matrixWidthAndHeight as NSNumber
                tileMatrix.matrixHeight = matrixWidthAndHeight as NSNumber
                tileMatrix.tileWidth = tileWidth as NSNumber
                tileMatrix.tileHeight = tileHeight as NSNumber
                tileMatrix.pixelXSize = NSDecimalNumber(value: pixelXSize)
                tileMatrix.pixelYSize = NSDecimalNumber(value: pixelYSize)
                tileMatrixDao.create(tileMatrix)

                matrixWidthAndHeight = matrixWidthAndHeight*2
                pixelXSize = pixelXSize / 2.0
                pixelYSize = pixelYSize / 2.0;

                let myDao = geoPackage.getTileDao(withTableName: tileMatrix.tableName)

                for column in 0..<tileMatrix.matrixHeight.int32Value {
                    for row in 0..<tileMatrix.matrixHeight.int32Value{
                        let newRow: GPKGTileRow = myDao!.newRow()
                        newRow.setZoomLevel(tileMatrix.zoomLevel as! Int32)
                        newRow.setTileColumn(column)
                        newRow.setTileRow(row)
                        newRow.setTileData(tileData)

                        myDao?.create(newRow)
                    }
                }
            }
kttary commented 6 years ago

today i try some changing, and the image aligned corectly on QGIS. However emulator can't show the tile. it logs some error as follow: 2018-07-31 14:56:54.489399+0700 MyGeopackage[1863:69373] [VKDefault] Tile 28.20.5 (128) in current unloaded state for 0.00 seconds - Raster Overlays Above Labels - Failed to decode (terminal) (0.00 sec) 2018-07-31 14:56:54.489563+0700 MyGeopackage[1863:69373] [VKDefault] Tile 24.20.5 (128) in current unloaded state for 0.00 seconds - Raster Overlays Above Labels - Failed to decode (terminal) (0.00 sec) 2018-07-31 14:56:54.489678+0700 MyGeopackage[1863:69373] [VKDefault] Tile 25.20.5 (128) in current unloaded state for 0.00 seconds - Raster Overlays Above Labels - Failed to decode (terminal) (0.00 sec) 2018-07-31 14:56:54.489829+0700 MyGeopackage[1863:69373] [VKDefault] Tile 26.20.5 (128) in current unloaded state for 0.00 seconds - Raster Overlays Above Labels - Failed to decode (terminal) (0.00 sec) 2018-07-31 14:56:54.489973+0700 MyGeopackage[1863:69373] [VKDefault] Tile 27.20.5 (128) in current unloaded state for 0.00 seconds - Raster Overlays Above Labels - Failed to decode (terminal) (0.00 sec) 2018-07-31 14:56:54.490122+0700 MyGeopackage[1863:69373] [VKDefault] Tile 28.18.5 (128) in current unloaded state for 0.00 seconds - Raster Overlays Above Labels - Failed to decode (terminal) (0.00 sec) 2018-07-31 14:56:54.490284+0700 MyGeopackage[1863:69373] [VKDefault] Tile 28.19.5 (128) in current unloaded state for 0.00 seconds - Raster Overlays Above Labels - Failed to decode (terminal) (0.00 sec) 2018-07-31 14:56:54.490438+0700 MyGeopackage[1863:69373] [VKDefault] Tile 26.18.5 (128) in current unloaded state for 0.00 seconds - Raster Overlays Above Labels - Failed to decode (terminal) (0.00 sec)

Pls some advice

screen shot 2018-07-30 at 21 47 06 screen shot 2018-07-30 at 21 49 40
bosborn commented 6 years ago

Here is your code slightly modified and with tiles downloaded for convenience. On a phone simulator and based on zoom levels, tiles may not show up till about zoom 3.

    func makeTiles(geoPackage: GPKGGeoPackage){

        let srsDao: GPKGSpatialReferenceSystemDao = geoPackage.getSpatialReferenceSystemDao()
        let srs2: GPKGSpatialReferenceSystem = srsDao.query(forOrganization: PROJ_AUTHORITY_EPSG, andCoordsysId: 3857)
        let trans: SFPProjectionTransform = SFPProjectionTransform(fromEpsg: 4326, andToEpsg: 3857)
        let myEnvelope = SFGeometryEnvelope(minXDouble: -180, andMinYDouble: PROJ_WEB_MERCATOR_MIN_LAT_RANGE, andMaxXDouble: 180, andMaxYDouble: PROJ_WEB_MERCATOR_MAX_LAT_RANGE)
        let envelope2 = trans.transform(with: myEnvelope)

        geoPackage.createTileMatrixSetTable()
        geoPackage.createTileMatrixTable()

        let contents: GPKGContents  = GPKGContents()
        contents.tableName = "MYTILE"
        contents.setContentsDataType(GPKG_CDT_TILES)
        contents.identifier = "MYTILE"
        contents.theDescription = "MY FIRST TILE"
        contents.lastChange = Date()
        contents.minX = envelope2?.minX
        contents.minY = envelope2?.minY
        contents.maxX = envelope2?.maxX
        contents.maxY = envelope2?.maxY
        contents.setSrs(srs2)

        let tileColumns = GPKGTileTable.createRequiredColumns()
        let tileTable: GPKGTileTable = GPKGTileTable(table: "MYTILE", andColumns: tileColumns)
        geoPackage.createTileTable(tileTable)

        let contentsDao: GPKGContentsDao = geoPackage.getContentsDao()
        contentsDao.create(contents)

        let tileMatrixSetDao: GPKGTileMatrixSetDao = geoPackage.getTileMatrixSetDao()
        let tileMatrixSet: GPKGTileMatrixSet = GPKGTileMatrixSet()
        tileMatrixSet.setContents(contents)
        tileMatrixSet.setSrs(srs2)
        tileMatrixSet.minX = envelope2?.minX
        tileMatrixSet.minY = envelope2?.minY
        tileMatrixSet.maxX = envelope2?.maxX
        tileMatrixSet.maxY = envelope2?.maxY
        tileMatrixSetDao.create(tileMatrixSet)

        let tileMatrixDao: GPKGTileMatrixDao = geoPackage.getTileMatrixDao()

        let data: Data = downloadTile(z: 0, x: 0, y: 0)
        let image: UIImage = GPKGImageConverter.toImage(data)
        let tileWidth : Int = Int(image.size.width)
        let tileHeight : Int = Int(image.size.height)

        var matrixWidthAndHeight = 1
        var pixelXSize: Double = (tileMatrixSet.maxX.doubleValue - tileMatrixSet.minX.doubleValue) / (Double)(matrixWidthAndHeight * tileWidth)
        var pixelYSize: Double = (tileMatrixSet.maxY.doubleValue - tileMatrixSet.minY.doubleValue) / (Double)(matrixWidthAndHeight * tileHeight)

        for zoom in 0..<4 {
            let tileMatrix: GPKGTileMatrix = GPKGTileMatrix()
            tileMatrix.setContents(contents)
            tileMatrix.zoomLevel = zoom as NSNumber
            tileMatrix.matrixWidth = matrixWidthAndHeight as NSNumber
            tileMatrix.matrixHeight = matrixWidthAndHeight as NSNumber
            tileMatrix.tileWidth = tileWidth as NSNumber
            tileMatrix.tileHeight = tileHeight as NSNumber
            tileMatrix.pixelXSize = NSDecimalNumber(value: pixelXSize)
            tileMatrix.pixelYSize = NSDecimalNumber(value: pixelYSize)
            tileMatrixDao.create(tileMatrix)

            matrixWidthAndHeight = matrixWidthAndHeight*2
            pixelXSize = pixelXSize / 2.0
            pixelYSize = pixelYSize / 2.0

            let myDao = geoPackage.getTileDao(withTableName: tileMatrix.tableName)

            for column in 0..<tileMatrix.matrixHeight.int32Value {
                for row in 0..<tileMatrix.matrixHeight.int32Value{
                    let newRow: GPKGTileRow = myDao!.newRow()
                    newRow.setZoomLevel(tileMatrix.zoomLevel as! Int32)
                    newRow.setTileColumn(column)
                    newRow.setTileRow(row)
                    let tileData: Data = downloadTile(z: zoom, x: column, y: row)
                    newRow.setTileData(tileData)

                    myDao?.create(newRow)
                }
            }
        }

        let tileDao: GPKGTileDao = geoPackage.getTileDao(withTableName: contents.tableName)
        let tileOverlay: MKTileOverlay = GPKGOverlayFactory.tileOverlay(with: tileDao)
        tileOverlay.canReplaceMapContent = false

        DispatchQueue.main.async { [unowned self] in
            self.mapView.add(tileOverlay)
        }

    }

    func downloadTile(z: Int, x: Int32, y: Int32) -> Data {
        var tileData: Data? = nil
        let url: String = String(format: "https://osm.geointservices.io/osm_tiles/%d/%d/%d.png", z, x, y)
        do {
            tileData = try Data.init(contentsOf: URL(string: url)!)
        } catch {
        }
        return tileData!;
    }

simulator screen shot - iphone 8 plus - 2018-08-01 at 09 50 22

kttary commented 6 years ago

Thank you for reply. When i tried to download for certain area of interest for example area between 152.921111, -27.604734 to 153.108080, -27.372853 from zoom level 4 to 5, the tiles look misaligned. What variable should i adjust and how to get those values? Thank you in advance

bosborn commented 6 years ago

You'll need to make sure the tile matrix set bounds are correct and work for all tile matrix zoom levels. The tile matrix dimensions, tile size dimensions, and pixel sizes must all be correct with the zoom level and the tile matrix set.

This site is very useful for determining the required bounds if you are using XYZ tiles.

This method will give you an encompassing tile grid from a web mercator bounding box and this one will do the same for a wgs84 bounding box.

This method will give you the web mercator bounding box of a single XYZ tile and this one will give you one for a range of tiles.

kttary commented 6 years ago

Cannot fully got your clue.. Im looping through encopasing tilegrid but no luck

let srs2: GPKGSpatialReferenceSystem = srsDao.query(forOrganization: PROJ_AUTHORITY_EPSG, andCoordsysId: 3857)
            let trans: SFPProjectionTransform = SFPProjectionTransform(fromEpsg: 4326, andToEpsg: 3857)
            let myEnvelope = SFGeometryEnvelope(minXDouble: 0, andMinYDouble: PROJ_WEB_MERCATOR_MIN_LAT_RANGE, andMaxXDouble: 180, andMaxYDouble: 0)
            let envelope2 = trans.transform(with: myEnvelope)

            let totalTileGrid: GPKGTileGrid = GPKGTileBoundingBoxUtils.getTileGrid(withWebMercatorBoundingBox: GPKGBoundingBox(minLongitudeDouble: envelope2!.minX as! Double, andMinLatitudeDouble: envelope2!.minY as! Double, andMaxLongitudeDouble: envelope2!.maxX as! Double, andMaxLatitudeDouble: envelope2!.maxY as! Double), andZoom: 1)
            let totalBoundingBox: GPKGBoundingBox = GPKGTileBoundingBoxUtils.getWebMercatorBoundingBox(with: totalTileGrid, andZoom: 1)

            geoPackage.createTileMatrixSetTable()
            geoPackage.createTileMatrixTable()

            contents = GPKGContents()
            contents.tableName = "MYTILE"
            contents.setContentsDataType(GPKG_CDT_TILES)
            contents.identifier = "MYTILE"
            contents.theDescription = "MY FIRST TILE"
            contents.lastChange = Date()
            contents.minX = totalBoundingBox.minLongitude
            contents.minY = totalBoundingBox.minLatitude
            contents.maxX = totalBoundingBox.maxLongitude
            contents.maxY = totalBoundingBox.maxLatitude
            contents.setSrs(srs2)

            let tileColumns = GPKGTileTable.createRequiredColumns()
            let tileTable: GPKGTileTable = GPKGTileTable(table: "MYTILE", andColumns: tileColumns)
            geoPackage.createTileTable(tileTable)

            contentsDao.create(contents);

            let tileMatrixSetDao: GPKGTileMatrixSetDao = geoPackage.getTileMatrixSetDao()
            let tileMatrixSet: GPKGTileMatrixSet = GPKGTileMatrixSet()
            tileMatrixSet.setContents(contents)
            tileMatrixSet.setSrs(srs2)
            tileMatrixSet.minX = contents.minX
            tileMatrixSet.minY = contents.minY
            tileMatrixSet.maxX = contents.maxX
            tileMatrixSet.maxY = contents.maxY
            tileMatrixSetDao.create(tileMatrixSet)

            let tileMatrixDao: GPKGTileMatrixDao = geoPackage.getTileMatrixDao()

            var tileGrid : GPKGTileGrid = totalTileGrid
            let myDao = geoPackage.getTileDao(withTableName: "MYTILE")

            for zoom:Int in 1..<4 {
                let tileData: Data = downloadTile(z: zoom, x: tileGrid.minX, y: tileGrid.minY)
                let image: UIImage = GPKGImageConverter.toImage(tileData)
                let tileWidth : Int = Int(image.size.width)
                let tileHeight : Int = Int(image.size.height)
                for x in tileGrid.minX..<tileGrid.maxX+1 {
                    for y in tileGrid.minY..<tileGrid.maxY+1 {
                        let tileData: Data = downloadTile(z: zoom, x: x, y: y)

                        let newRow: GPKGTileRow = myDao!.newRow()
                        newRow.setZoomLevel(Int32(zoom))
                        newRow.setTileColumn(x-tileGrid.minX)
                        newRow.setTileRow(y-tileGrid.minY)
                        newRow.setTileData(tileData)

                        myDao?.create(newRow)
                    }
                }

                let matrixWidth = tileGrid.maxX - tileGrid.minX + 1
                let matrixHeight = tileGrid.maxY - tileGrid.minY + 1;
                let pixelXSize = (tileMatrixSet.maxX.doubleValue - tileMatrixSet.minX.doubleValue) / Double(matrixWidth * Int32(tileWidth))
                let pixelYSize = (tileMatrixSet.maxY.doubleValue - tileMatrixSet.minY.doubleValue) / Double(matrixHeight * Int32(tileHeight))

                let tileMatrix: GPKGTileMatrix = GPKGTileMatrix()
                tileMatrix.setContents(contents)
                tileMatrix.zoomLevel = zoom as NSNumber
                tileMatrix.matrixWidth = matrixWidth as NSNumber
                tileMatrix.matrixHeight = matrixHeight as NSNumber
                tileMatrix.tileWidth = tileWidth as NSNumber
                tileMatrix.tileHeight = tileHeight as NSNumber
                tileMatrix.pixelXSize = NSDecimalNumber(value: pixelXSize)
                tileMatrix.pixelYSize = NSDecimalNumber(value: pixelYSize)
                tileMatrixDao.create(tileMatrix)

                tileGrid = GPKGTileBoundingBoxUtils.tileGrid(tileGrid, zoomIncrease: 1)
            }
kttary commented 6 years ago

got it. Thank you