Skip to content

Commit

Permalink
Split MVT to GeoJSON translation into separate files and add GeoEngin…
Browse files Browse the repository at this point in the history
…e Configuration

The GeoEngine configuration contains the grid size and zoom levels along with the
servers. This used to be hidden away in TileGridUtils.kt which wasn't the best
place for it.
  • Loading branch information
davecraig committed Nov 20, 2024
1 parent 7e8d053 commit 46e75ba
Show file tree
Hide file tree
Showing 12 changed files with 558 additions and 525 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ import com.google.android.play.core.review.ReviewManagerFactory
import com.google.android.play.core.review.model.ReviewErrorCode
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.scottishtecharmy.soundscape.geoengine.PROTOMAPS_SERVER_BASE
import org.scottishtecharmy.soundscape.geoengine.PROTOMAPS_SERVER_PATH
import org.scottishtecharmy.soundscape.screens.home.HomeRoutes
import org.scottishtecharmy.soundscape.screens.home.HomeScreen
import org.scottishtecharmy.soundscape.screens.home.Navigator
import org.scottishtecharmy.soundscape.services.SoundscapeService
import org.scottishtecharmy.soundscape.ui.theme.SoundscapeTheme
import org.scottishtecharmy.soundscape.geoengine.utils.TileGrid.Companion.PROTOMAPS_SERVER_PATH
import org.scottishtecharmy.soundscape.geoengine.utils.TileGrid.Companion.PROTOMAPS_SERVER_BASE
import org.scottishtecharmy.soundscape.utils.extractAssets
import java.io.File
import javax.inject.Inject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import io.realm.kotlin.RealmConfiguration
import org.scottishtecharmy.soundscape.database.local.model.Location
import org.scottishtecharmy.soundscape.database.local.model.RouteData
import org.scottishtecharmy.soundscape.database.local.model.RoutePoint
import org.scottishtecharmy.soundscape.geoengine.utils.TileGrid.Companion.SOUNDSCAPE_TILE_BACKEND
import org.scottishtecharmy.soundscape.geoengine.SOUNDSCAPE_TILE_BACKEND

object RealmConfiguration {
private var tileDataRealm: Realm? = null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.scottishtecharmy.soundscape.geoengine

/**
* This file contains the various configuration options for the GeoEngine.
*/

/**
* The zoom level and grid size are constant. When using soundscape-backend these will be
* 16 and 3, but if we switch to using protobuf tiles they will be 15 and 2.
*/
const val SOUNDSCAPE_TILE_BACKEND = false
val ZOOM_LEVEL = if(SOUNDSCAPE_TILE_BACKEND) 16 else 15
var GRID_SIZE = if(SOUNDSCAPE_TILE_BACKEND) 3 else 2

/**
* The default tile server is the one out in the cloud where the tile JSON is at:
* https://server/protomaps.json
*
* and the tiles are at
* https://server/protomaps/{z}/{x}/{y}.mvt
*/
const val PROTOMAPS_SERVER_BASE = "https://d1wzlzgah5gfol.cloudfront.net"
const val PROTOMAPS_SERVER_PATH = "protomaps"
const val PROTOMAPS_SUFFIX = "mvt"

/**
* It's also useful to be able to use tiles served up locally when testing. When I
* test locally I'm serving up the file like this:
*
* tileserver-gl-light --file europe.pmtiles -b 192.168.86.39
*
* With this configuration the tile JSON descriptor appears at:
* http://192.168.86.39:8080/data/v3.json
*
* and the tiles within it are at:
* http://192.168.86.39:8080/data/v3/{z}/{x}/{y}.pbf
*
*/
//const val PROTOMAPS_SERVER_BASE = "http://192.168.86.39:8080"
//const val PROTOMAPS_SERVER_PATH = "data/v3"
//const val PROTOMAPS_SUFFIX = "pbf"

Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ import org.scottishtecharmy.soundscape.network.TileClient
import org.scottishtecharmy.soundscape.geoengine.mvttranslation.InterpolatedPointsJoiner
import org.scottishtecharmy.soundscape.geoengine.utils.RelativeDirections
import org.scottishtecharmy.soundscape.geoengine.utils.TileGrid
import org.scottishtecharmy.soundscape.geoengine.utils.TileGrid.Companion.SOUNDSCAPE_TILE_BACKEND
import org.scottishtecharmy.soundscape.geoengine.utils.TileGrid.Companion.ZOOM_LEVEL
import org.scottishtecharmy.soundscape.geoengine.utils.TileGrid.Companion.getTileGrid
import org.scottishtecharmy.soundscape.geoengine.utils.checkIntersection
import org.scottishtecharmy.soundscape.geoengine.utils.cleanTileGeoJSON
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package org.scottishtecharmy.soundscape.geoengine.mvttranslation

import org.scottishtecharmy.soundscape.geojsonparser.geojson.Feature
import org.scottishtecharmy.soundscape.geojsonparser.geojson.FeatureCollection
import org.scottishtecharmy.soundscape.geojsonparser.geojson.Point

class EntranceMatching {

/**
* buildingNodes is a sparse map which maps from a location within the tile to a list of
* building polygons which have nodes at that point. Every node on any `POI` polygon will appear
* in the map along with any entrance. After processing it should be straightforward to match
* up entrances to their POI polygons.
*/
private val buildingNodes : HashMap< Int, ArrayList<EntranceDetails>> = hashMapOf()

/**
* addLine is called for any line feature that is being added to the FeatureCollection.
* @param line is a new `transportation` layer line to add to the map
* @param details describes the line that is being added.
*
*/
fun addPolygon(line : ArrayList<Pair<Int, Int>>,
details : EntranceDetails
) {
for (point in line) {
if((point.first < 0) || (point.first > 4095) ||
(point.second < 0) || (point.second > 4095)) {
continue
}

// Rather than have a 2D sparse array, turn the coordinates into a single int so that we
// can have a 1D sparse array instead.
val coordinateKey = point.first.shl(12) + point.second
if (buildingNodes[coordinateKey] == null) {
buildingNodes[coordinateKey] = arrayListOf(details.copy())
}
else {
buildingNodes[coordinateKey]?.add(details.copy())
}
}
}

/**
* generateIntersections goes through our hash map and adds an intersection feature to the
* collection wherever it finds out.
* @param collection is where the new intersection features are added
* @param tileX the tile x coordinate so that the tile relative location of the intersection can
* be turned into a latitude/longitude
* @param tileY the tile y coordinate so that the tile relative location of the intersection can
* * be turned into a latitude/longitude
*/
fun generateEntrances(collection: FeatureCollection, tileX : Int, tileY : Int, tileZoom : Int) {
// Add points for the intersections that we found
for ((key, nodes) in buildingNodes) {

// Generate an entrance with a matching POI polygon
var entranceDetails : EntranceDetails? = null
var poiDetails : EntranceDetails? = null
for(node in nodes) {
if(!node.poi) {
// We have an entrance!
entranceDetails = node
} else {
poiDetails = node
}
}

// If we have an entrance at this point then we generate a feature to represent it
// using the POI that it is coincident with if there is one.
if(entranceDetails != null) {
// Turn our coordinate key back into tile relative x,y coordinates
val x = key.shr(12)
val y = key.and(0xfff)
// Convert the tile relative coordinate into a LatLngAlt
val point = arrayListOf(Pair(x, y))
val coordinates = convertGeometry(tileX, tileY, tileZoom, point)

// Create our entrance feature to match those from soundscape-backend
val entrance = Feature()
entrance.geometry =
Point(coordinates[0].longitude, coordinates[0].latitude)
entrance.foreign = HashMap()
entrance.foreign!!["feature_type"] = "entrance"
entrance.foreign!!["feature_value"] = entranceDetails.entranceType
val osmIds = arrayListOf<Double>()
osmIds.add(entranceDetails.osmId)
entrance.foreign!!["osm_ids"] = osmIds

entrance.properties = HashMap()
entrance.properties!!["name"] = entranceDetails.name
if(entranceDetails.name == null)
entrance.properties!!["name"] = poiDetails?.name

collection.addFeature(entrance)

// println("Entrance: ${poiDetails?.name} ${entranceDetails.entranceType} ")
}
}
}
}

data class EntranceDetails(
val name : String?,
val entranceType : String?,
val poi: Boolean,
val osmId : Double,
)
Loading

0 comments on commit 46e75ba

Please sign in to comment.