-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
380 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
141 changes: 141 additions & 0 deletions
141
server/src/main/scala/kpn/server/analyzer/engine/monitor/MonitorRouteElementAnalyzer.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package kpn.server.analyzer.engine.monitor | ||
|
||
import kpn.api.common.data.Member | ||
import kpn.api.common.data.RelationMember | ||
import kpn.api.common.data.WayMember | ||
|
||
import scala.collection.mutable | ||
|
||
class MonitorRouteElementAnalyzer { | ||
|
||
private val connectionAnalyzer = new MonitorRouteConnectionAnalyzer | ||
private val nextRouteAnalyzer = new MonitorRouteNextRouteAnalyzer | ||
|
||
private var startWayMember: Option[WayMember] = None | ||
private var processedStartWayMember = false | ||
|
||
val elements = mutable.Buffer[MonitorRouteElement]() | ||
val currentElementFragments = mutable.Buffer[MonitorRouteFragment]() | ||
var loopFragments: Option[Seq[MonitorRouteFragment]] = None | ||
var endNodeId: Long = 0 | ||
|
||
def analyzeRoute(members: Seq[Member]): Seq[MonitorRouteElement] = { | ||
members.foreach { member => | ||
member match { | ||
case wayMember: WayMember => processWayMember(wayMember) | ||
case relationMember: RelationMember => processRelationMember(relationMember) | ||
case _ => None | ||
} | ||
} | ||
|
||
startWayMember match { | ||
case None => // nothing more to do here | ||
case Some(wayMember) => | ||
if (processedStartWayMember == false) { | ||
currentElementFragments.addOne(MonitorRouteFragment.from(wayMember)) | ||
} | ||
} | ||
|
||
if (currentElementFragments.nonEmpty) { | ||
elements.addOne(MonitorRouteElement.from(currentElementFragments.toSeq)) | ||
} | ||
elements.toSeq | ||
} | ||
|
||
private def processRelationMember(relationMember: RelationMember): Unit = { | ||
// still to be implemented | ||
} | ||
|
||
private def processWayMember(currentWayMember: WayMember): Unit = { | ||
if (currentWayMember.way.nodes.length >= 2) { | ||
if (!processedStartWayMember) { | ||
startWayMember match { | ||
case None => | ||
startWayMember = Some(currentWayMember) | ||
case Some(wayMember) => | ||
processStart(wayMember, currentWayMember) | ||
} | ||
} | ||
else { | ||
processNextWayMember(currentWayMember) | ||
} | ||
} | ||
} | ||
|
||
private def processStart(wayMember1: WayMember, wayMember2: WayMember): Unit = { | ||
val routeWays = connectionAnalyzer.analyze(wayMember1, wayMember2) | ||
if (routeWays.isEmpty) { | ||
// false start, the first 2 ways do not connect | ||
// make way1 a separate segment | ||
// make way2 the first way | ||
elements.addOne(MonitorRouteElement.from(Seq(MonitorRouteFragment.from(wayMember1)))) | ||
startWayMember = Some(wayMember2) | ||
} | ||
else { | ||
currentElementFragments.addAll(routeWays) | ||
startWayMember = None | ||
processedStartWayMember = true | ||
endNodeId = routeWays.last.endNode.id | ||
} | ||
} | ||
|
||
private def processNextWayMember(wayMember: WayMember): Unit = { | ||
loopFragments match { | ||
case Some(loopRouteWays) => processLoop(loopRouteWays, wayMember) | ||
case None => processNextWayMemberAnalyze(wayMember) | ||
} | ||
} | ||
|
||
private def processNextWayMemberAnalyze(wayMember: WayMember): Unit = { | ||
nextRouteAnalyzer.analyze(endNodeId, wayMember) match { | ||
case Some(routeWay) => | ||
endNodeId = routeWay.endNode.id | ||
|
||
currentElementFragments.addOne(routeWay) | ||
|
||
val startNodeIds = currentElementFragments.map(_.startNode.id) | ||
|
||
if (startNodeIds.contains(endNodeId)) { | ||
// loop, split currentSegment | ||
val index = currentElementFragments.lastIndexWhere(_.startNode.id == endNodeId) | ||
val segmentBeforeLoop = currentElementFragments.take(index) | ||
loopFragments = Some(currentElementFragments.drop(index).toSeq) | ||
elements.addOne(MonitorRouteElement.from(segmentBeforeLoop.toSeq)) | ||
currentElementFragments.clear() | ||
} | ||
|
||
case None => | ||
if (currentElementFragments.nonEmpty) { | ||
elements.addOne(MonitorRouteElement.from(currentElementFragments.toSeq)) | ||
currentElementFragments.clear() | ||
} | ||
startWayMember = Some(wayMember) | ||
processedStartWayMember = false | ||
} | ||
} | ||
|
||
private def processLoop(loopRouteWays: Seq[MonitorRouteFragment], wayMember: WayMember): Unit = { | ||
val startNodeId = wayMember.way.nodes.head.id | ||
val endNodeId = wayMember.way.nodes.last.id | ||
|
||
val index = loopRouteWays.indexWhere { routeWay => | ||
routeWay.endNode.id == startNodeId | ||
} | ||
if (index >= 0) { | ||
val routeWays1 = loopRouteWays.take(index + 1) | ||
val routeWays2 = loopRouteWays.drop(index + 1) | ||
elements.addOne(MonitorRouteElement.from(routeWays1)) | ||
elements.addOne(MonitorRouteElement.from(routeWays2)) | ||
} | ||
else { | ||
elements.addOne(MonitorRouteElement.from(loopRouteWays)) | ||
} | ||
|
||
startWayMember = Some(wayMember) | ||
processedStartWayMember = false | ||
} | ||
|
||
private def debug(message: String): Unit = { | ||
println(message) | ||
} | ||
} |
100 changes: 100 additions & 0 deletions
100
server/src/main/scala/kpn/server/analyzer/engine/monitor/MonitorRouteMemberAnalyzer.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package kpn.server.analyzer.engine.monitor | ||
|
||
import kpn.api.common.data.Member | ||
import kpn.api.custom.Relation | ||
|
||
import scala.collection.mutable | ||
|
||
class MonitorRouteMemberAnalyzer { | ||
|
||
// see: MonitorFilter.ignoredRoles Seq("place_of_worship", "guest_house", "outer", "inner") | ||
private val rolesIgnore = Seq( | ||
"outer", | ||
"inner", | ||
"place_of_worship", | ||
"guest_house" | ||
) | ||
|
||
private val roles = Seq( | ||
"alternative", | ||
"excursion", | ||
"approach", | ||
"connection", | ||
"variant", | ||
"link", | ||
) | ||
|
||
def analyzeRoute(relation: Relation): Seq[MonitorRouteMemberGroup] = { | ||
|
||
var currentMemberGroupOption: Option[MonitorRouteMemberGroup] = None | ||
val mainMembers = mutable.Buffer[Member]() | ||
val memberGroups = mutable.Buffer[MonitorRouteMemberGroup]() | ||
|
||
relation.members.foreach { member => | ||
if (isRelevant(member)) { | ||
alternativeRole(member) match { | ||
case Some(role) => | ||
currentMemberGroupOption match { | ||
case None => | ||
// first member with this role | ||
currentMemberGroupOption = Some(MonitorRouteMemberGroup(Some(role), Seq(member))) | ||
|
||
case Some(currentMemberGroup) => | ||
if (!currentMemberGroup.role.contains(role)) { | ||
memberGroups.addOne(currentMemberGroup) | ||
currentMemberGroupOption = Some(MonitorRouteMemberGroup(Some(role), Seq(member))) | ||
} | ||
currentMemberGroupOption = Some( | ||
currentMemberGroup.copy( | ||
members = currentMemberGroup.members :+ member | ||
) | ||
) | ||
} | ||
|
||
case None => | ||
currentMemberGroupOption match { | ||
case None => | ||
case Some(currentMemberGroup) => | ||
memberGroups.addOne(currentMemberGroup) | ||
currentMemberGroupOption = None | ||
} | ||
mainMembers.addOne(member) | ||
} | ||
} | ||
} | ||
|
||
currentMemberGroupOption match { | ||
case None => | ||
case Some(currentMemberGroup) => | ||
memberGroups.addOne(currentMemberGroup) | ||
currentMemberGroupOption = None | ||
} | ||
memberGroups.addOne(MonitorRouteMemberGroup(None, mainMembers.toSeq)) | ||
memberGroups.toSeq | ||
} | ||
|
||
private def isRelevant(member: Member): Boolean = { | ||
if (member.isWay || member.isRelation) { | ||
member.role match { | ||
case Some(role) => !rolesIgnore.contains(role) | ||
case None => true | ||
} | ||
} | ||
else { | ||
false | ||
} | ||
} | ||
|
||
private def alternativeRole(member: Member): Option[String] = { | ||
member.role match { | ||
case Some(role) => | ||
if (roles.contains(role)) { | ||
Some(role) | ||
} | ||
else { | ||
None | ||
} | ||
case None => None | ||
} | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
server/src/main/scala/kpn/server/analyzer/engine/monitor/MonitorRouteMemberGroup.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package kpn.server.analyzer.engine.monitor | ||
|
||
import kpn.api.common.data.Member | ||
|
||
case class MonitorRouteMemberGroup(role: Option[String], members: Seq[Member]) |
119 changes: 119 additions & 0 deletions
119
...r/src/test/scala/kpn/server/analyzer/engine/monitor/MonitorRouteElementAnalyzerTest.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package kpn.server.analyzer.engine.monitor | ||
|
||
import kpn.core.util.UnitTest | ||
|
||
class MonitorRouteElementAnalyzerTest extends UnitTest { | ||
|
||
test("continous") { | ||
analyze( | ||
new MonitorRouteTestData() { | ||
memberWay(10, "", 1, 2, 3) | ||
memberWay(11, "", 3, 4, 5) | ||
memberWay(12, "", 5, 6, 7) | ||
} | ||
).shouldMatchTo( | ||
Seq( | ||
"1>3>5>7" | ||
) | ||
) | ||
} | ||
|
||
test("continous - first way reversed") { | ||
analyze( | ||
new MonitorRouteTestData() { | ||
memberWay(10, "", 3, 2, 1) | ||
memberWay(11, "", 3, 4, 5) | ||
memberWay(12, "", 5, 6, 7) | ||
} | ||
).shouldMatchTo( | ||
Seq( | ||
"1>3>5>7" | ||
) | ||
) | ||
} | ||
|
||
test("continous - second and third way reversed") { | ||
analyze( | ||
new MonitorRouteTestData() { | ||
memberWay(10, "", 1, 2, 3) | ||
memberWay(11, "", 5, 4, 3) | ||
memberWay(12, "", 7, 6, 5) | ||
} | ||
).shouldMatchTo( | ||
Seq( | ||
"1>3>5>7" | ||
) | ||
) | ||
} | ||
|
||
test("2 gaps - 3 segments") { | ||
analyze( | ||
new MonitorRouteTestData() { | ||
memberWay(10, "", 1, 2) | ||
memberWay(11, "", 3, 4) | ||
memberWay(12, "", 4, 5) | ||
memberWay(13, "", 6, 7) | ||
memberWay(14, "", 7, 8) | ||
} | ||
).shouldMatchTo( | ||
Seq( | ||
"1>2", | ||
"3>4>5", | ||
"6>7>8" | ||
) | ||
) | ||
} | ||
|
||
test("start way has forward role") { | ||
analyze( | ||
new MonitorRouteTestData() { | ||
memberWay(10, "forward", 1, 2) | ||
memberWay(11, "", 3, 2) | ||
} | ||
).shouldMatchTo( | ||
Seq( | ||
"1>2>3 (oneWay)" | ||
) | ||
) | ||
} | ||
|
||
test("start way has forward role, no connection to second way") { | ||
analyze( | ||
new MonitorRouteTestData() { | ||
memberWay(10, "forward", 2, 1) | ||
memberWay(11, "", 3, 2) | ||
} | ||
).shouldMatchTo( | ||
Seq( | ||
"2>1 (oneWay)", | ||
"3>2" | ||
) | ||
) | ||
} | ||
|
||
test("forward") { | ||
val result = analyze( | ||
new MonitorRouteTestData() { | ||
memberWay(10, "", 1, 2) | ||
memberWay(11, "forward", 2, 3) | ||
memberWay(12, "forward", 3, 8) | ||
memberWay(13, "forward", 8, 7) | ||
memberWay(14, "forward", 7, 2) | ||
memberWay(15, "", 8, 9) | ||
} | ||
).shouldMatchTo( | ||
Seq( | ||
"1>2", | ||
"2>3>8 (oneWay)", | ||
"8>7>2 (oneWay)", | ||
"8>9", | ||
) | ||
) | ||
} | ||
|
||
private def analyze(data: MonitorRouteTestData): Seq[String] = { | ||
val relation = data.relation | ||
val elements = new MonitorRouteElementAnalyzer().analyzeRoute(relation.members) | ||
elements.map(_.string) | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
...er/src/test/scala/kpn/server/analyzer/engine/monitor/MonitorRouteMemberAnalyzerTest.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package kpn.server.analyzer.engine.monitor | ||
|
||
class MonitorRouteMemberAnalyzerTest { | ||
|
||
} |