Skip to content

Commit

Permalink
#369 monitor vector tiles - proof-of-concept wip
Browse files Browse the repository at this point in the history
  • Loading branch information
vmarc committed Dec 12, 2023
1 parent f006e42 commit c322ee0
Show file tree
Hide file tree
Showing 20 changed files with 236 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import kpn.server.analyzer.engine.analysis.route.analyzers.RouteTileAnalyzer
import kpn.server.analyzer.engine.changes.ChangeSetContext
import kpn.server.analyzer.engine.context.AnalysisContext
import kpn.server.analyzer.engine.context.ElementIds
import kpn.server.analyzer.engine.tile.LinesTileCalculatorImpl
import kpn.server.analyzer.engine.tile.NodeTileCalculatorImpl
import kpn.server.analyzer.engine.tile.RouteTileCalculatorImpl
import kpn.server.analyzer.engine.tile.TileCalculatorImpl
Expand Down Expand Up @@ -85,7 +86,8 @@ class AnalysisStartConfiguration(options: AnalysisStartToolOptions) {
}

private val routeTileAnalyzer = {
val routeTileCalculator = new RouteTileCalculatorImpl(tileCalculator)
val linesTileCalculator = new LinesTileCalculatorImpl(tileCalculator)
val routeTileCalculator = new RouteTileCalculatorImpl(linesTileCalculator)
new RouteTileAnalyzer(routeTileCalculator)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package kpn.core.tools.monitor.support

import kpn.core.loadOld.Parser
import kpn.core.overpass.OverpassQueryExecutor
import kpn.core.overpass.OverpassQueryExecutorRemoteImpl
import kpn.core.overpass.QueryRelationTopLevel
import kpn.database.util.Mongo
import kpn.server.analyzer.engine.context.ElementIds
import kpn.server.analyzer.engine.tile.LinesTileCalculator
import kpn.server.analyzer.engine.tile.LinesTileCalculatorImpl
import kpn.server.analyzer.engine.tile.TileCalculatorImpl
import kpn.server.analyzer.engine.tiles.domain.Line
import kpn.server.analyzer.engine.tiles.domain.Point
import kpn.server.monitor.repository.MonitorGroupRepository
import kpn.server.monitor.repository.MonitorGroupRepositoryImpl
import kpn.server.monitor.repository.MonitorRouteRepository
import kpn.server.monitor.repository.MonitorRouteRepositoryImpl
import kpn.server.monitor.MonitorUtil
import kpn.server.monitor.domain.MonitorRelation
import kpn.server.monitor.repository.MonitorRelationRepository
import kpn.server.monitor.repository.MonitorRelationRepositoryImpl
import kpn.server.monitor.route.update.RelationTopLevelDataBuilder

import scala.xml.XML

object MonitorCreateRelationsTool {
def main(args: Array[String]): Unit = {
Mongo.executeIn("kpn-monitor") { database =>
val groupRepository = new MonitorGroupRepositoryImpl(database)
val routeRepository = new MonitorRouteRepositoryImpl(database)
val relationRepository = new MonitorRelationRepositoryImpl(database)
val overpassQueryExecutor = new OverpassQueryExecutorRemoteImpl()
val linesTileCalculator = new LinesTileCalculatorImpl(new TileCalculatorImpl())
val tool = new MonitorCreateRelationsTool(
groupRepository,
routeRepository,
relationRepository,
overpassQueryExecutor,
linesTileCalculator
)
tool.createMonitorRelations()
}
}
}

class MonitorCreateRelationsTool(
groupRepository: MonitorGroupRepository,
routeRepository: MonitorRouteRepository,
relationRepository: MonitorRelationRepository,
overpassQueryExecutor: OverpassQueryExecutor,
linesTileCalculator: LinesTileCalculator
) {

def createMonitorRelations(): Unit = {
val relationIds = collectionRelationIds()
println(s"collected ${relationIds.size} relation ids")
relationIds.zipWithIndex.foreach { case (relationId, index) =>
println(s"${index + 1}/${relationIds.size}")
val monitorRelation = createMonitorRelation(relationId)
relationRepository.save(monitorRelation)
}
}

private def collectionRelationIds(): Seq[Long] = {
val ids = groupRepository.groups().sortBy(_.name) flatMap { group =>
println(s"group ${group.name}")
groupRepository.groupRoutes(group._id).sortBy(_.name).flatMap { route =>
println(s" route ${route.name}")
route.relation.toSeq.map(_.relationId) ++
MonitorUtil.subRelationsIn(route).map(_.relationId)
}
}
ids.distinct.sorted
}

private def createMonitorRelation(relationId: Long): MonitorRelation = {
val xmlString = overpassQueryExecutor.executeQuery(None, QueryRelationTopLevel(relationId))
val xml = XML.loadString(xmlString)
val rawData = new Parser().parse(xml.head)
val data = new RelationTopLevelDataBuilder(rawData, relationId).data
val nodes = data.nodes.values.toSeq
val ways = data.ways.values.toSeq
val nodeIds = nodes.map(_.id).toSet
val wayIds = ways.map(_.id).toSet
val subRelationIds = data.relations.values.flatMap(_.raw.relationMembers.map(_.ref)).toSet
val elementIds = ElementIds(nodeIds, wayIds, subRelationIds)
val tiles = ways.flatMap { way =>
val lines = way.nodes.sliding(2).map { case Seq(node1, node2) =>
Line(Point(node1.lon, node1.lat), Point(node2.lon, node2.lat))
}.toSeq
(2 to 14).flatMap { z =>
linesTileCalculator.tiles(z, lines)
}
}.distinct.sortBy(tile => (tile.z, tile.x, tile.y))

MonitorRelation(
relationId,
tiles.map(_.name),
elementIds
)
}
}
4 changes: 3 additions & 1 deletion server/src/main/scala/kpn/core/tools/tile/TileTool.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import kpn.database.base.Database
import kpn.database.util.Mongo
import kpn.core.tools.tile.TileTool.log
import kpn.core.util.Log
import kpn.server.analyzer.engine.tile.LinesTileCalculatorImpl
import kpn.server.analyzer.engine.tile.NodeTileCalculatorImpl
import kpn.server.analyzer.engine.tile.RouteTileCalculatorImpl
import kpn.server.analyzer.engine.tile.TileCalculatorImpl
Expand Down Expand Up @@ -77,7 +78,8 @@ object TileTool {
val vectorTileFileRepository = new TileFileRepositoryImpl(tileDir, "mvt")
val tileFileBuilder = new TileFileBuilderImpl(bitmapTileFileRepository, vectorTileFileRepository)
val nodeTileCalculator = new NodeTileCalculatorImpl(tileCalculator)
val routeTileCalculator = new RouteTileCalculatorImpl(tileCalculator)
val linesTileCalculator = new LinesTileCalculatorImpl(tileCalculator)
val routeTileCalculator = new RouteTileCalculatorImpl(linesTileCalculator)
new TilesBuilder(
bitmapTileFileRepository,
vectorTileFileRepository,
Expand Down
3 changes: 3 additions & 0 deletions server/src/main/scala/kpn/database/base/Database.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import kpn.database.actions.statistics.StatisticLongValues
import kpn.server.analyzer.engine.changes.data.Blacklist
import kpn.server.analyzer.engine.changes.network.NetworkChange
import kpn.server.monitor.domain.MonitorGroup
import kpn.server.monitor.domain.MonitorRelation
import kpn.server.monitor.domain.MonitorRoute
import kpn.server.monitor.domain.MonitorRouteChange
import kpn.server.monitor.domain.MonitorRouteChangeGeometry
Expand Down Expand Up @@ -86,6 +87,8 @@ trait Database {

def monitorRouteChangeGeometries: DatabaseCollection[MonitorRouteChangeGeometry]

def monitorRelations: DatabaseCollection[MonitorRelation]

def statistics: DatabaseCollection[StatisticLongValues]

def status: DatabaseCollection[WithStringId]
Expand Down
5 changes: 5 additions & 0 deletions server/src/main/scala/kpn/database/base/DatabaseImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import kpn.database.actions.statistics.StatisticLongValues
import kpn.server.analyzer.engine.changes.data.Blacklist
import kpn.server.analyzer.engine.changes.network.NetworkChange
import kpn.server.monitor.domain.MonitorGroup
import kpn.server.monitor.domain.MonitorRelation
import kpn.server.monitor.domain.MonitorRoute
import kpn.server.monitor.domain.MonitorRouteChange
import kpn.server.monitor.domain.MonitorRouteChangeGeometry
Expand Down Expand Up @@ -142,6 +143,10 @@ class DatabaseImpl(val database: MongoDatabase) extends Database {
new DatabaseCollectionImpl(database.getCollection[MonitorRouteChangeGeometry]("monitor-route-change-geometries"))
}

override def monitorRelations: DatabaseCollection[MonitorRelation] = {
new DatabaseCollectionImpl(database.getCollection[MonitorRelation]("monitor-relations"))
}

override def statistics: DatabaseCollection[StatisticLongValues] = {
new DatabaseCollectionImpl(database.getCollection[StatisticLongValues]("statistics"))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package kpn.server.analyzer.engine.tile

import kpn.server.analyzer.engine.tiles.domain.Line
import kpn.server.analyzer.engine.tiles.domain.Tile

trait LinesTileCalculator {

def tiles(z: Int, lines: Seq[Line]): Seq[Tile]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package kpn.server.analyzer.engine.tile

import kpn.server.analyzer.engine.tiles.domain.Line
import kpn.server.analyzer.engine.tiles.domain.Point
import kpn.server.analyzer.engine.tiles.domain.Tile
import org.springframework.stereotype.Component

@Component
class LinesTileCalculatorImpl(tileCalculator: TileCalculator) extends LinesTileCalculator {

override def tiles(z: Int, lines: Seq[Line]): Seq[Tile] = {

val tileQueue = scala.collection.mutable.Queue[Tile]()
val foundTiles = scala.collection.mutable.Set[Tile]()

val tiles = lines.flatMap(_.points).map { p: Point =>
tileCalculator.tileLonLat(z, p.x, p.y)
}.toSet

foundTiles ++= tiles.toSeq
tileQueue ++= tiles

while (tileQueue.nonEmpty) {
val tile = tileQueue.dequeue()
explore(tileQueue, foundTiles, lines, tile, tile.bounds.left, -1, 0)
explore(tileQueue, foundTiles, lines, tile, tile.bounds.right, 1, 0)
explore(tileQueue, foundTiles, lines, tile, tile.bounds.top, 0, 1)
explore(tileQueue, foundTiles, lines, tile, tile.bounds.bottom, 0, -1)
}
foundTiles.toSeq
}

private def explore(
tileQueue: scala.collection.mutable.Queue[Tile],
foundTiles: scala.collection.mutable.Set[Tile],
lines: Seq[Line],
tile: Tile,
side: Line,
xDelta: Int,
yDelta: Int
): Unit = {
val adjecentTile = tileCalculator.tileXY(tile.z, tile.x + xDelta, tile.y + yDelta)
if (!foundTiles.map(_.name).contains(adjecentTile.name)) {
if (lines.exists(_.intersects(side))) {
tileQueue += adjecentTile
foundTiles += adjecentTile
()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,54 +1,14 @@
package kpn.server.analyzer.engine.tile

import kpn.server.analyzer.engine.tiles.domain.Line
import kpn.server.analyzer.engine.tiles.domain.Point
import kpn.server.analyzer.engine.tiles.domain.Tile
import kpn.server.analyzer.engine.tiles.domain.RouteTileSegment
import org.springframework.stereotype.Component

@Component
class RouteTileCalculatorImpl(tileCalculator: TileCalculator) extends RouteTileCalculator {
class RouteTileCalculatorImpl(linesTileCalculator: LinesTileCalculator) extends RouteTileCalculator {

override def tiles(z: Int, segments: Seq[RouteTileSegment]): Seq[Tile] = {

val tileQueue = scala.collection.mutable.Queue[Tile]()
val foundTiles = scala.collection.mutable.Set[Tile]()

val lines = segments.flatMap(_.lines)

val tiles = lines.flatMap(_.points).map { p: Point =>
tileCalculator.tileLonLat(z, p.x, p.y)
}.toSet

foundTiles ++= tiles.toSeq
tileQueue ++= tiles

while (tileQueue.nonEmpty) {
val tile = tileQueue.dequeue()
explore(tileQueue, foundTiles, lines, tile, tile.bounds.left, -1, 0)
explore(tileQueue, foundTiles, lines, tile, tile.bounds.right, 1, 0)
explore(tileQueue, foundTiles, lines, tile, tile.bounds.top, 0, 1)
explore(tileQueue, foundTiles, lines, tile, tile.bounds.bottom, 0, -1)
}
foundTiles.toSeq
}

private def explore(
tileQueue: scala.collection.mutable.Queue[Tile],
foundTiles: scala.collection.mutable.Set[Tile],
lines: Seq[Line],
tile: Tile,
side: Line,
xDelta: Int,
yDelta: Int
): Unit = {
val adjecentTile = tileCalculator.tileXY(tile.z, tile.x + xDelta, tile.y + yDelta)
if (!foundTiles.map(_.name).contains(adjecentTile.name)) {
if (lines.exists(_.intersects(side))) {
tileQueue += adjecentTile
foundTiles += adjecentTile
()
}
}
linesTileCalculator.tiles(z, lines)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package kpn.server.monitor.domain

import kpn.api.base.WithId
import kpn.server.analyzer.engine.context.ElementIds

case class MonitorRelation(
_id: Long, // relationId
tiles: Seq[String],
elementIds: ElementIds,
) extends WithId
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package kpn.server.monitor.repository

import kpn.server.monitor.domain.MonitorRelation

trait MonitorRelationRepository {

def save(monitorRelation: MonitorRelation): Unit
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package kpn.server.monitor.repository

import kpn.core.util.Log
import kpn.database.base.Database
import kpn.server.monitor.domain.MonitorRelation

class MonitorRelationRepositoryImpl(database: Database) extends MonitorRelationRepository {

private val log = Log(classOf[MonitorRelationRepositoryImpl])

override def save(monitorRelation: MonitorRelation): Unit = {
database.monitorRelations.save(monitorRelation, log)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package kpn.server.opendata.common

import kpn.api.common.tiles.ZoomLevel
import kpn.core.util.Log
import kpn.server.analyzer.engine.tile.LinesTileCalculatorImpl
import kpn.server.analyzer.engine.tile.NodeTileCalculatorImpl
import kpn.server.analyzer.engine.tile.RouteTileCalculatorImpl
import kpn.server.analyzer.engine.tile.TileCalculatorImpl
Expand All @@ -17,7 +18,8 @@ class OpenDataTileBuilder {
private val log = Log(classOf[OpenDataTileBuilder])
private val tileCalculator = new TileCalculatorImpl()
private val nodeTileCalculator = new NodeTileCalculatorImpl(tileCalculator)
private val routeTileCalculator = new RouteTileCalculatorImpl(tileCalculator)
private val linesTileCalculator = new LinesTileCalculatorImpl(tileCalculator)
private val routeTileCalculator = new RouteTileCalculatorImpl(linesTileCalculator)

def build(nodes: Seq[OpenDataNode], routes: Seq[OpenDataRoute], dir: String): Unit = {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import kpn.server.analyzer.engine.analysis.route.analyzers.RouteCountryAnalyzer
import kpn.server.analyzer.engine.analysis.route.analyzers.RouteLocationAnalyzerMock
import kpn.server.analyzer.engine.analysis.route.analyzers.RouteTileAnalyzer
import kpn.server.analyzer.engine.context.AnalysisContext
import kpn.server.analyzer.engine.tile.LinesTileCalculatorImpl
import kpn.server.analyzer.engine.tile.RouteTileCalculatorImpl
import kpn.server.analyzer.engine.tile.TileCalculatorImpl

Expand All @@ -24,7 +25,8 @@ object CaseStudy {
val analysisContext = new AnalysisContext()
val locationAnalyzer = new LocationAnalyzerFixed()
val tileCalculator = new TileCalculatorImpl()
val routeTileCalculator = new RouteTileCalculatorImpl(tileCalculator)
val linesTileCalculator = new LinesTileCalculatorImpl(tileCalculator)
val routeTileCalculator = new RouteTileCalculatorImpl(linesTileCalculator)
val routeTileAnalyzer = new RouteTileAnalyzer(routeTileCalculator)
val routeCountryAnalyzer = new RouteCountryAnalyzer(locationAnalyzer)
val routeLocationAnalyzer = new RouteLocationAnalyzerMock()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import kpn.server.analyzer.engine.analysis.route.analyzers.RouteCountryAnalyzer
import kpn.server.analyzer.engine.analysis.route.analyzers.RouteLocationAnalyzerMock
import kpn.server.analyzer.engine.analysis.route.analyzers.RouteTileAnalyzer
import kpn.server.analyzer.engine.context.AnalysisContext
import kpn.server.analyzer.engine.tile.LinesTileCalculatorImpl
import kpn.server.analyzer.engine.tile.RouteTileCalculatorImpl
import kpn.server.analyzer.engine.tile.TileCalculatorImpl

Expand All @@ -23,7 +24,8 @@ class Issue109_RoundaboutRoute extends UnitTest {
val analysisContext = new AnalysisContext()
val locationAnalyzer = new LocationAnalyzerFixed()
val tileCalculator = new TileCalculatorImpl()
val routeTileCalculator = new RouteTileCalculatorImpl(tileCalculator)
val linesTileCalculator = new LinesTileCalculatorImpl(tileCalculator)
val routeTileCalculator = new RouteTileCalculatorImpl(linesTileCalculator)
val routeTileAnalyzer = new RouteTileAnalyzer(routeTileCalculator)
val routeCountryAnalyzer = new RouteCountryAnalyzer(locationAnalyzer)
val routeLocationAnalyzer = new RouteLocationAnalyzerMock()
Expand Down
Loading

0 comments on commit c322ee0

Please sign in to comment.