From e4aadae2568ceb822300e1dfc5a5730eaac84631 Mon Sep 17 00:00:00 2001 From: vmarc Date: Tue, 16 Jul 2024 09:55:11 +0200 Subject: [PATCH] #368 migrate node analysis --- .../route/RouteDetailMainAnalyzer.scala | 3 +- .../analysis/route/RouteNameAnalysis.scala | 4 +- .../analysis/route/RouteNodeAnalysis.scala | 2 +- .../route/RouteNodeAnalysisFormatter.scala | 24 +-- .../engine/analysis/route/RouteNodeData.scala | 10 +- .../route/analyzers/RouteNodeAnalyzer.scala | 133 ++++++++++------- .../route/report/RouteLinksReport.scala | 4 +- .../report/RouteNodeAnalysisReport.scala | 2 +- .../route/report/RoutePathReport.scala | 4 +- .../route/report/RouteSegmentReport.scala | 8 +- .../structure/RouteSegmentAnalyzer.scala | 8 +- .../route/structure/StructureAnalyzer.scala | 20 +-- .../analyzers/RouteNodeAnalyzerTest.scala | 140 ++++++------------ .../RouteDetailAnalysisTestContext.scala | 6 +- 14 files changed, 172 insertions(+), 196 deletions(-) diff --git a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteDetailMainAnalyzer.scala b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteDetailMainAnalyzer.scala index 35ea79ab1..e69965813 100644 --- a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteDetailMainAnalyzer.scala +++ b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteDetailMainAnalyzer.scala @@ -18,7 +18,6 @@ import kpn.server.analyzer.engine.analysis.route.analyzers.FixmeTodoRouteAnalyze import kpn.server.analyzer.engine.analysis.route.analyzers.GeometryDigestAnalyzer import kpn.server.analyzer.engine.analysis.route.analyzers.IncompleteOkRouteAnalyzer import kpn.server.analyzer.engine.analysis.route.analyzers.IncompleteRouteAnalyzer -import kpn.server.analyzer.engine.analysis.route.analyzers.OldRouteNodeAnalyzer import kpn.server.analyzer.engine.analysis.route.analyzers.OldRouteNodeTagAnalyzer import kpn.server.analyzer.engine.analysis.route.analyzers.OldRouteStructureAnalyzer import kpn.server.analyzer.engine.analysis.route.analyzers.OldRouteTileAnalyzer @@ -84,7 +83,7 @@ class RouteDetailMainAnalyzer( OldRouteNodeTagAnalyzer, RouteNameAnalyzer, - OldRouteNodeAnalyzer, + //OldRouteNodeAnalyzer, RouteNameFromNodesAnalyzer, ExpectedNameRouteAnalyzer, // <== needs further updating SuspiciousWaysRouteAnalyzer, // OK diff --git a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteNameAnalysis.scala b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteNameAnalysis.scala index 78216d7ff..b013f8f6c 100644 --- a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteNameAnalysis.scala +++ b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteNameAnalysis.scala @@ -9,7 +9,9 @@ case class RouteNameAnalysis( derivedFromDeprecatedNoteTag: Boolean = false ) { - def isStartNodeNameSameAsEndNodeName: Boolean = startNodeName.isDefined && endNodeName.isDefined && startNodeName == endNodeName + def isStartNodeNameSameAsEndNodeName: Boolean = { + startNodeName.isDefined && endNodeName.isDefined && startNodeName == endNodeName + } def hasStandardNodeNames: Boolean = { startNodeName match { diff --git a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteNodeAnalysis.scala b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteNodeAnalysis.scala index 40fd36caf..933fb5f27 100644 --- a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteNodeAnalysis.scala +++ b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteNodeAnalysis.scala @@ -9,5 +9,5 @@ case class RouteNodeAnalysis( ) { def nodes: Seq[RouteNodeData] = startNode.toSeq ++ endNode.toSeq ++ startTentacleNodes ++ endTentacleNodes - def nodeIds: Seq[Long] = nodes.map(_.node.id) + def nodeIds: Seq[Long] = nodes.map(_.nodeId) } diff --git a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteNodeAnalysisFormatter.scala b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteNodeAnalysisFormatter.scala index 8eee3c7ba..54a60c8ab 100644 --- a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteNodeAnalysisFormatter.scala +++ b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteNodeAnalysisFormatter.scala @@ -4,23 +4,23 @@ class RouteNodeAnalysisFormatter(analysis: RouteNodeAnalysis) { def nodeStrings: Seq[String] = { List( - nodeStrings("Start", analysis.startNode.toSeq), - nodeStrings("End", analysis.endNode.toSeq), - nodeStrings("Start tentacle from", analysis.startTentacleNodes), - nodeStrings("End tentacle to", analysis.endTentacleNodes), - nodeStrings("Redundant", analysis.redundantNodes), + nodeStrings("start", analysis.startNode.toSeq), + nodeStrings("end", analysis.endNode.toSeq), + nodeStrings("start-tentacle", analysis.startTentacleNodes), + nodeStrings("end-tentacle", analysis.endTentacleNodes), + nodeStrings("redundant", analysis.redundantNodes), ).flatten } - private def nodeStrings(title: String, routeNodeDatas: Seq[RouteNodeData]): Seq[String] = { - routeNodeDatas.map(n => s"$title=(${nodeString(n)})") + private def nodeStrings(title: String, nodeDatas: Seq[RouteNodeData]): Seq[String] = { + nodeDatas.map(n => s"$title=${nodeString(n)}") } - private def nodeString(routeNodeData: RouteNodeData): String = { - "%s/%s/%s".format( - routeNodeData.node.id, - routeNodeData.name, - if (routeNodeData.isInWay) "W" else "R", + private def nodeString(nodeData: RouteNodeData): String = { + "%s(%s)%s".format( + nodeData.nodeId, + nodeData.alternateName, + if (nodeData.isInWay) "W" else "R", ) } } diff --git a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteNodeData.scala b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteNodeData.scala index dd470f4ea..53c8d6a4c 100644 --- a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteNodeData.scala +++ b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/RouteNodeData.scala @@ -1,9 +1,13 @@ package kpn.server.analyzer.engine.analysis.route -import kpn.api.common.data.Node - case class RouteNodeData( - node: Node, + nodeId: Long, + // latitude: String, + // longitude: String, name: String, + alternateName: String, + // longName: Option[String] = None, + // definedInRelation: Boolean = false, + // definedInWay: Boolean = false, isInWay: Boolean, ) diff --git a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/analyzers/RouteNodeAnalyzer.scala b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/analyzers/RouteNodeAnalyzer.scala index 63b07472e..aa6fbeaff 100644 --- a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/analyzers/RouteNodeAnalyzer.scala +++ b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/analyzers/RouteNodeAnalyzer.scala @@ -48,108 +48,116 @@ class RouteNodeAnalyzer(context: RouteDetailAnalysisContext) { } private def analyzeRouteWithSingleNetworkType(networkType: NetworkType): RouteDetailAnalysisContext = { - val routeNodeDatas = findRouteNodes(networkType) - val routeNodeAnalysis = if (routeNodeDatas.isEmpty) { + val nodeDatas = findRouteNodes(networkType) + val nodeAnalysis = if (nodeDatas.isEmpty) { facts += RouteWithoutNodes RouteNodeAnalysis() } else { - if (routeNodeDatas.isEmpty) { + if (nodeDatas.isEmpty) { RouteNodeAnalysis() } else { - val wayRouteNodeDatas = routeNodeDatas.filter(_.isInWay) + val wayRouteNodeDatas = nodeDatas.filter(_.isInWay) + val startNodeName = determineStartNodeName(nodeDatas, wayRouteNodeDatas) + val endNodeNameOption: Option[String] = determineEndNodeName(startNodeName, nodeDatas, wayRouteNodeDatas) - val startNodeName = { - if (wayRouteNodeDatas.nonEmpty) { - wayRouteNodeDatas.head.name // prefer node included in way over node that is only included in the relation - } - else { - routeNodeDatas.head.name - } - } + val startNodes = withSuffixes(nodeDatas.filter(_.name == startNodeName).reverse) + val endNodes = withSuffixes(nodeDatas.filter(n => endNodeNameOption.contains(n.name))) + val nodeIds = startNodes.map(_.nodeId) ++ endNodes.map(_.nodeId) + val redundantNodes = nodeDatas.filterNot(n => nodeIds.contains(n.nodeId)) - val endNodeNameOption: Option[String] = { - val nonStartNodes = wayRouteNodeDatas.filter(_.name != startNodeName) - if (nonStartNodes.nonEmpty) { - Some(nonStartNodes.last.name) - } - else { - val allNonStartNodes = routeNodeDatas.filter(_.name != startNodeName) - if (allNonStartNodes.nonEmpty) { - Some(allNonStartNodes.last.name) - } - else { - None // no route nodes with name not equal to startNodeName - } - } - } - - val startNodes = routeNodeDatas.filter(_.name == startNodeName) - val endNodes = routeNodeDatas.filter(n => endNodeNameOption.contains(n.name)) - val redundantNodes = routeNodeDatas.filterNot(n => startNodes.contains(n) || endNodes.contains(n)) - - val routeNodeAnalysis = RouteNodeAnalysis( - startNode = startNodes.lastOption, + val nodeAnalysis = RouteNodeAnalysis( + startNode = startNodes.headOption, endNode = endNodes.headOption, - startTentacleNodes = startNodes.dropRight(1), + startTentacleNodes = startNodes.drop(1), endTentacleNodes = endNodes.drop(1), redundantNodes = redundantNodes ) - if (routeNodeAnalysis.startNode.nonEmpty && !routeNodeAnalysis.startNode.exists(_.isInWay)) { + if (nodeAnalysis.startNode.nonEmpty && !nodeAnalysis.startNode.exists(_.isInWay)) { facts += RouteNodeMissingInWays } - else if (routeNodeAnalysis.endNode.nonEmpty && !routeNodeAnalysis.endNode.exists(_.isInWay)) { + else if (nodeAnalysis.endNode.nonEmpty && !nodeAnalysis.endNode.exists(_.isInWay)) { facts += RouteNodeMissingInWays } - if (routeNodeAnalysis.redundantNodes.nonEmpty) { + if (nodeAnalysis.redundantNodes.nonEmpty) { facts += RouteRedundantNodes } - routeNodeAnalysis + nodeAnalysis } } context.copy( - _nodeAnalysis = Some(routeNodeAnalysis) + _nodeAnalysis = Some(nodeAnalysis) ).withFacts(facts.toSeq *) } + private def determineStartNodeName(nodeDatas: Seq[RouteNodeData], wayNodeDatas: Seq[RouteNodeData]): String = { + if (wayNodeDatas.nonEmpty) { + wayNodeDatas.head.name // prefer node included in way over node that is only included in the relation + } + else { + nodeDatas.head.name + } + } + + private def determineEndNodeName( + startNodeName: String, + nodeDatas: Seq[RouteNodeData], + wayNodeDatas: Seq[RouteNodeData] + ): Option[String] = { + + val nonStartNodes = wayNodeDatas.filter(_.name != startNodeName) + if (nonStartNodes.nonEmpty) { + Some(nonStartNodes.last.name) + } + else { + val allNonStartNodes = nodeDatas.filter(_.name != startNodeName) + if (allNonStartNodes.nonEmpty) { + Some(allNonStartNodes.last.name) + } + else { + None // no route nodes with name not equal to startNodeName + } + } + } + private def findRouteNodes(networkType: NetworkType): Seq[RouteNodeData] = { - val routeNodeDatas = ListBuffer[RouteNodeData]() + val nodeDatas = ListBuffer[RouteNodeData]() context.relation.members.foreach { case wayMember: WayMember => wayMember.way.nodes.distinct.foreach { node => wayNodeData(networkType, node) match { case None => // not a node network node - case Some(routeNodeData) => - if (!routeNodeDatas.filter(_.isInWay).map(_.node.id).contains(routeNodeData.node.id)) { - routeNodeDatas += routeNodeData + case Some(nodeData) => + if (!nodeDatas.filter(_.isInWay).map(_.nodeId).contains(nodeData.nodeId)) { + nodeDatas += nodeData } } } case nodeMember: NodeMember => - if (routeNodeDatas.map(_.node.id).contains(nodeMember.node.id)) { + if (nodeDatas.map(_.nodeId).contains(nodeMember.node.id)) { // we prefer the position of the node in the ways over the position in the route relation } else { standaloneNodeData(networkType, nodeMember.node) match { case None => // not a node network node - case Some(routeNodeData) => - if (!routeNodeDatas.filterNot(_.isInWay).map(_.node.id).contains(routeNodeData.node.id)) { - routeNodeDatas += routeNodeData + case Some(nodeData) => + if (!nodeDatas.filterNot(_.isInWay).map(_.nodeId).contains(nodeData.nodeId)) { + nodeDatas += nodeData } } } case _ => } - val wayNodeIds = routeNodeDatas.toSeq.filter(_.isInWay).map(_.node.id) - routeNodeDatas.toSeq.filter { routeNodeData => - routeNodeData.isInWay || !wayNodeIds.contains(routeNodeData.node.id) + val wayNodeIds = nodeDatas.toSeq.filter(_.isInWay).map(_.nodeId) + nodeDatas.toSeq.filter { nodeData => + nodeData.isInWay || !wayNodeIds.contains(nodeData.nodeId) } } @@ -172,7 +180,8 @@ class RouteNodeAnalyzer(context: RouteDetailAnalysisContext) { private def wayNodeData(networkType: NetworkType, node: Node): Option[RouteNodeData] = { nodeName(networkType, node).map { name => RouteNodeData( - node, + node.id, + name, name, isInWay = true ) @@ -182,7 +191,8 @@ class RouteNodeAnalyzer(context: RouteDetailAnalysisContext) { private def standaloneNodeData(networkType: NetworkType, node: Node): Option[RouteNodeData] = { nodeName(networkType, node).map { name => RouteNodeData( - node, + node.id, + name, name, isInWay = false ) @@ -217,4 +227,21 @@ class RouteNodeAnalyzer(context: RouteDetailAnalysisContext) { None } } + + private def withSuffixes(nodeDatas: Seq[RouteNodeData]): Seq[RouteNodeData] = { + nodeDatas.zipWithIndex.map { case (nodeData, index) => + val suffixes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + if (nodeDatas.size == 1) { + nodeData.copy(alternateName = nodeData.name) + } + else { + if (index < suffixes.length) { + nodeData.copy(alternateName = s"${nodeData.name}.${suffixes(index)}") + } + else { + nodeData.copy(alternateName = nodeData.name) + } + } + } + } } diff --git a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/report/RouteLinksReport.scala b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/report/RouteLinksReport.scala index ca01c5084..137370a75 100644 --- a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/report/RouteLinksReport.scala +++ b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/report/RouteLinksReport.scala @@ -95,7 +95,7 @@ class RouteLinksReport(context: RouteDetailAnalysisContext) { } private def nodes(nodeIds: Seq[Long], nodeType: String, nodeDatas: Seq[RouteNodeData]): Seq[String] = { - val filteredNodeDatas = nodeDatas.filter(n => nodeIds.contains(n.node.id)) - filteredNodeDatas.map(nodeData => s"$nodeType=${nodeData.node.id}(${nodeData.name})") + val filteredNodeDatas = nodeDatas.filter(n => nodeIds.contains(n.nodeId)) + filteredNodeDatas.map(nodeData => s"$nodeType=${nodeData.nodeId}(${nodeData.name})") } } diff --git a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/report/RouteNodeAnalysisReport.scala b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/report/RouteNodeAnalysisReport.scala index 9ff07f859..d198ea0a5 100644 --- a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/report/RouteNodeAnalysisReport.scala +++ b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/report/RouteNodeAnalysisReport.scala @@ -39,7 +39,7 @@ object RouteNodeAnalysisReport { private def routeNodeReport(nodeType: String, routeNode: RouteNodeData): String = { s""" | $nodeType - | ${routeNode.node.id} + | ${routeNode.nodeId} | ${routeNode.name} | ${yes(routeNode.isInWay)} | diff --git a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/report/RoutePathReport.scala b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/report/RoutePathReport.scala index f83290143..8027f0328 100644 --- a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/report/RoutePathReport.scala +++ b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/report/RoutePathReport.scala @@ -27,8 +27,8 @@ class RoutePathReport(context: RouteDetailAnalysisContext) { context.segments.flatMap(_.elements).map { element => val fromNode = element.fromNetworkNode val toNode = element.toNetworkNode - val from = fromNode.map(n => s"${ReportUtil.osmNodeLink(n.node.id)}(${n.name})").getOrElse("") - val to = toNode.map(n => s"${ReportUtil.osmNodeLink(n.node.id)}(${n.name})").getOrElse("") + val from = fromNode.map(n => s"${ReportUtil.osmNodeLink(n.nodeId)}(${n.name})").getOrElse("") + val to = toNode.map(n => s"${ReportUtil.osmNodeLink(n.nodeId)}(${n.name})").getOrElse("") val elementIds = "" + element.id s""" diff --git a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/report/RouteSegmentReport.scala b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/report/RouteSegmentReport.scala index 4155ce1e8..4a03fd3d1 100644 --- a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/report/RouteSegmentReport.scala +++ b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/report/RouteSegmentReport.scala @@ -53,8 +53,8 @@ class RouteSegmentReport(context: RouteDetailAnalysisContext) { private def segmentElements(segment: RouteAnalysisSegment): String = { segment.elements.map { element => - val from = element.fromNetworkNode.map(node => s"from=${ReportUtil.osmNodeLink(node.node.id)}(${node.name})").getOrElse("") - val to = element.toNetworkNode.map(node => s"to=${ReportUtil.osmNodeLink(node.node.id)}(${node.name})").getOrElse("") + val from = element.fromNetworkNode.map(node => s"from=${ReportUtil.osmNodeLink(node.nodeId)}(${node.name})").getOrElse("") + val to = element.toNetworkNode.map(node => s"to=${ReportUtil.osmNodeLink(node.nodeId)}(${node.name})").getOrElse("") s""" | | Segment element ${element.id} ${element.direction.toString.toLowerCase} $from $to @@ -125,7 +125,7 @@ class RouteSegmentReport(context: RouteDetailAnalysisContext) { } private def nodes(nodeIds: Seq[Long], nodeType: String, nodeDatas: Seq[RouteNodeData]): Seq[String] = { - val filteredNodeDatas = nodeDatas.filter(n => nodeIds.contains(n.node.id)) - filteredNodeDatas.map(nodeData => s"$nodeType=${nodeData.node.id}(${nodeData.name})") + val filteredNodeDatas = nodeDatas.filter(n => nodeIds.contains(n.nodeId)) + filteredNodeDatas.map(nodeData => s"$nodeType=${nodeData.nodeId}(${nodeData.name})") } } diff --git a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/structure/RouteSegmentAnalyzer.scala b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/structure/RouteSegmentAnalyzer.scala index 986dc10aa..5f2ed8571 100644 --- a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/structure/RouteSegmentAnalyzer.scala +++ b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/structure/RouteSegmentAnalyzer.scala @@ -203,8 +203,8 @@ class RouteSegmentAnalyzer(context: RouteDetailAnalysisContext) { RoutePathDirection.Bidirectional } - val fromNetworkNode = context.nodeAnalysis.nodes.find(_.node.id == fromNodeId) - val toNetworkNode = context.nodeAnalysis.nodes.find(_.node.id == toNodeId) + val fromNetworkNode = context.nodeAnalysis.nodes.find(_.nodeId == fromNodeId) + val toNetworkNode = context.nodeAnalysis.nodes.find(_.nodeId == toNodeId) buildElement( direction, @@ -228,8 +228,8 @@ class RouteSegmentAnalyzer(context: RouteDetailAnalysisContext) { private def buildFragmentElement(routeLinkWay: RouteLinkWay, direction: RoutePathDirection, nodeIds: Seq[Long]): RouteAnalysisElement = { // this is a closed loop at the end of the route val fragment = toFragment(routeLinkWay, nodeIds) - val fromNetworkNode = context.nodeAnalysis.nodes.find(_.node.id == fragment.fromNodeId) - val toNetworkNode = context.nodeAnalysis.nodes.find(_.node.id == fragment.toNodeId) + val fromNetworkNode = context.nodeAnalysis.nodes.find(_.nodeId == fragment.fromNodeId) + val toNetworkNode = context.nodeAnalysis.nodes.find(_.nodeId == fragment.toNodeId) buildElement( direction, diff --git a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/structure/StructureAnalyzer.scala b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/structure/StructureAnalyzer.scala index 1e1613833..894873f59 100644 --- a/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/structure/StructureAnalyzer.scala +++ b/server/src/main/scala/kpn/server/analyzer/engine/analysis/route/structure/StructureAnalyzer.scala @@ -116,7 +116,7 @@ class StructureAnalyzer(context: RouteDetailAnalysisContext, traceEnabled: Boole context.nodeAnalysis.endTentacleNodes.flatMap { toNode => val usedElementIds = forwardPath.toSeq.flatMap(_.elementIds) ++ backwardPath.toSeq.flatMap(_.elementIds) ++ startTentaclePaths.flatMap(_.elementIds) val remainingElements = context.segments.flatMap(_.elements).filterNot(element => usedElementIds.contains(element.id)) - remainingElements.find(_.toNodeId == toNode.node.id) match { + remainingElements.find(_.toNodeId == toNode.nodeId) match { case None => None case Some(firstElement) => Some( @@ -141,7 +141,7 @@ class StructureAnalyzer(context: RouteDetailAnalysisContext, traceEnabled: Boole val usedElementIds = forwardPath.toSeq.flatMap(_.elementIds) ++ backwardPath.toSeq.flatMap(_.elementIds) val remainingElements = context.segments.flatMap(_.elements).filterNot(element => usedElementIds.contains(element.id)) context.nodeAnalysis.startTentacleNodes.flatMap { fromNode => - remainingElements.find(_.nodeIds.head == fromNode.node.id) match { + remainingElements.find(_.nodeIds.head == fromNode.nodeId) match { case None => None case Some(firstElement) => Some( @@ -167,20 +167,20 @@ class StructureAnalyzer(context: RouteDetailAnalysisContext, traceEnabled: Boole None } else { - nodeNetworkFindFirstBackwardPathIndex(context.segments.flatMap(_.elements), mainEndNode.node.id).flatMap { index => + nodeNetworkFindFirstBackwardPathIndex(context.segments.flatMap(_.elements), mainEndNode.nodeId).flatMap { index => val lastBackwardElement = context.segments.flatMap(_.elements)(index) val remainingElements = context.segments.flatMap(_.elements).take(index).reverse val element = StructurePathElement( lastBackwardElement, reversed = true ) - val elements = findNextBackwardPath(Seq(element), remainingElements, mainStartNode.node.id) + val elements = findNextBackwardPath(Seq(element), remainingElements, mainStartNode.nodeId) if (elements.nonEmpty) { Some( StructurePath( pathIds.next(), - mainEndNode.node.id, - mainStartNode.node.id, + mainEndNode.nodeId, + mainStartNode.nodeId, elements ) ) @@ -194,20 +194,20 @@ class StructureAnalyzer(context: RouteDetailAnalysisContext, traceEnabled: Boole private def nodeNetworkForwardPath(mainStartNode: RouteNodeData, mainEndNode: RouteNodeData): Option[StructurePath] = { - findFirstForwardPathIndex(context.segments.flatMap(_.elements), mainStartNode.node.id).flatMap { index => + findFirstForwardPathIndex(context.segments.flatMap(_.elements), mainStartNode.nodeId).flatMap { index => val firstForwardElement = context.segments.flatMap(_.elements)(index) val element = StructurePathElement( firstForwardElement, reversed = false ) val remainingElements = context.segments.flatMap(_.elements).drop(index + 1) - val elements = nodeNetworkFindNextForwardPath(Seq(element), remainingElements, mainEndNode.node.id) + val elements = nodeNetworkFindNextForwardPath(Seq(element), remainingElements, mainEndNode.nodeId) if (elements.nonEmpty) { Some( StructurePath( pathIds.next(), - mainStartNode.node.id, - mainEndNode.node.id, + mainStartNode.nodeId, + mainEndNode.nodeId, elements ) ) diff --git a/server/src/test/scala/kpn/server/analyzer/engine/analysis/route/analyzers/RouteNodeAnalyzerTest.scala b/server/src/test/scala/kpn/server/analyzer/engine/analysis/route/analyzers/RouteNodeAnalyzerTest.scala index e1d6f900c..3a086b472 100644 --- a/server/src/test/scala/kpn/server/analyzer/engine/analysis/route/analyzers/RouteNodeAnalyzerTest.scala +++ b/server/src/test/scala/kpn/server/analyzer/engine/analysis/route/analyzers/RouteNodeAnalyzerTest.scala @@ -39,8 +39,8 @@ class RouteNodeAnalyzerTest extends UnitTest { analyze(d).shouldMatchTo( Seq( - "Start=(1/01/W)", - "End=(2/02/W)" + "start=1(01)W", + "end=2(02)W" ) ) } @@ -56,8 +56,8 @@ class RouteNodeAnalyzerTest extends UnitTest { analyze(d).shouldMatchTo( Seq( - "Start=(1/01/R)", - "End=(2/02/R)", + "start=1(01)R", + "end=2(02)R", "RouteNodeMissingInWays" ) ) @@ -75,8 +75,8 @@ class RouteNodeAnalyzerTest extends UnitTest { analyze(d).shouldMatchTo( Seq( - "Start=(2/20/20/W)", - "End=(1/100/100/W)", + "start=2(20)W", + "end=1(100)W)", "(reversed)" ) ) @@ -101,54 +101,17 @@ class RouteNodeAnalyzerTest extends UnitTest { analyze(d).shouldMatchTo( Seq( - "Start=(1/01/W)", // 01.a - "Start=(2/01/W)", // 01.b - "Start=(3/01/W)", // 01.c - "End=(4/02/W)", // 02.a - "End=(5/02/W)", // 02.b - "End=(6/02/W)" // 02.c + "start=3(01.a)W", + "end=4(02.a)W", + "start-tentacle=2(01.b)W", + "start-tentacle=1(01.c)W", + "end-tentacle=5(02.b)W", + "end-tentacle=6(02.c)W", ) ) } - test("route with extra start and end nodes in reverse order") { - - pending // TODO redesign - this logic is not needed anymore in the new design? - - val d = new RouteTestData("01-02") { - node(1, "01") - node(2, "01") - node(3, "01") - node(4, "02") - node(5, "02") - node(6, "02") - memberWay(11, "", 6) - memberWay(12, "", 5) - memberWay(13, "", 4) - memberWay(14, "", 3) - memberWay(15, "", 2) - memberWay(16, "", 1) - } - - analyze(d).shouldMatchTo( - Seq( - "Start=(6/02/W)", - "Start=(5/02/W)", - "Start=(4/02/W)", - "End=(3/01/W)", - "End=(2/01/W)", - "End=(1/01/W)", - // "(reversed)" - ) - /* TODO redesign - was: - "Start=(3/01/01.a/W,2/01/01.b/W,1/01/01.c/W)," + - "End=(4/02/02.a/W,5/02/02.b/W,6/02/02.c/W)," + - "(reversed)" - */ - ) - } - - test("route with extra end nodes not in reverse order") { + test("route with extra end node") { val d = new RouteTestData("01-02") { node(1, "01") @@ -160,29 +123,9 @@ class RouteNodeAnalyzerTest extends UnitTest { analyze(d).shouldMatchTo( Seq( - "Start=(1/01/W)", - "End=(2/02/W)", - "End=(3/02/W)" - ) - ) - } - - test("route with extra end nodes not in reverse order 2") { - - pending // TODO redesign - this test is not needed anymore in the new design? - - val d = new RouteTestData("01-02") { - node(1, "01") - node(2, "02") - node(3, "02") - memberWay(11, "", 2, 1) - memberWay(12, "", 2, 3) - } - - analyze(d).shouldMatchTo( - Seq( - "Start=(1/01/01/W)", - "End=(2/02/02.a/W,3/02/02.b/W)" + "start=1(01)W", + "end=2(02.a)W", + "end-tentacle=3(02.b)W", ) ) } @@ -201,9 +144,9 @@ class RouteNodeAnalyzerTest extends UnitTest { analyze(d).shouldMatchTo( Seq( - "Start=(1/01/W)", - "End=(2/02/W)", - "Redundant=(3/03/R)", + "start=1(01)W", + "end=2(02)W", + "redundant=3(03)R", "RouteRedundantNodes" ) ) @@ -221,10 +164,10 @@ class RouteNodeAnalyzerTest extends UnitTest { analyze(d).shouldMatchTo( Seq( - "Start=(1/01/W)", - "End=(3/03/W)", - "Redundant=(2/02/W)", - "RouteRedundantNodes" + "start=1(01)W", + "end=3(03)W", + "redundant=2(02)W", + "RouteRedundantNodes", ) ) } @@ -243,8 +186,8 @@ class RouteNodeAnalyzerTest extends UnitTest { analyze(d).shouldMatchTo( Seq( - "Start=(1/01/01/W)", - "End=(2/02/02/W)", + "start=(1/01/01/W)", + "end=(2/02/02/W)", "Redundant=(3/03/03/W)", "RouteRedundantNodes" ) @@ -268,6 +211,7 @@ class RouteNodeAnalyzerTest extends UnitTest { memberWay(17, "", 7, 8) // 8: end --> 01 memberWay(18, "", 8, 9) // 9: redundant --> 02 } + analyze(d).foreach(a => println(s""""$a",""")) analyze(d).shouldMatchTo( Seq( @@ -314,8 +258,8 @@ class RouteNodeAnalyzerTest extends UnitTest { analyze(d).shouldMatchTo( Seq( - "Start=(1/01/01/W)", - "End=(3/02/02/W)", + "start=(1/01/01/W)", + "end=(3/02/02/W)", "Redundant=(5/03/03/W)", "RouteRedundantNodes" ) @@ -334,8 +278,8 @@ class RouteNodeAnalyzerTest extends UnitTest { analyze(d).shouldMatchTo( Seq( - "Start=(1/01/01/R)", - "End=(3/02/02/RW)", + "start=(1/01/01/R)", + "end=(3/02/02/RW)", "RouteNodeMissingInWays" ) ) @@ -353,7 +297,7 @@ class RouteNodeAnalyzerTest extends UnitTest { analyze(d).shouldMatchTo( Seq( - "End=(2/02/02/RW)", + "end=(2/02/02/RW)", "Redundant=(1/01/01/RW)", "RouteRedundantNodes" ) @@ -372,7 +316,7 @@ class RouteNodeAnalyzerTest extends UnitTest { analyze(d).shouldMatchTo( Seq( - "Start=(1/01/01/RW)", + "start=(1/01/01/RW)", "Redundant=(2/02/02/RW)", "RouteRedundantNodes" ) @@ -408,8 +352,8 @@ class RouteNodeAnalyzerTest extends UnitTest { analyze(d).shouldMatchTo( Seq( - "Start=(1/01/01/RW)", - "End=(2/02/02/RW)" + "start=(1/01/01/RW)", + "end=(2/02/02/RW)" ) ) } @@ -443,8 +387,8 @@ class RouteNodeAnalyzerTest extends UnitTest { analyze(d).shouldMatchTo( Seq( - "Start=(1/02/02/W)", - "End=(3/01/01/W)" + "start=(1/02/02/W)", + "end=(3/01/01/W)" ) ) } @@ -466,8 +410,8 @@ class RouteNodeAnalyzerTest extends UnitTest { analyze(d).shouldMatchTo( Seq( - "Start=(5/01/01/W)", - "End=(3/03/03/W)", + "start=(5/01/01/W)", + "end=(3/03/03/W)", "Redundant=(1/02/02/W)", "(reversed)", "RouteNameMissing", @@ -504,8 +448,8 @@ class RouteNodeAnalyzerTest extends UnitTest { analyze(d).shouldMatchTo( Seq( - "Start=(1/01/01/W)", - "End=(2/02/02/W)" + "start=(1/01/01/W)", + "end=(2/02/02/W)" ) ) } @@ -538,8 +482,8 @@ class RouteNodeAnalyzerTest extends UnitTest { analyze(d, proposed = true).shouldMatchTo( Seq( - "Start=(1/01/01/W)", - "End=(2/02/02/W)" + "start=(1/01/01/W)", + "end=(2/02/02/W)" ) ) } diff --git a/server/src/test/scala/kpn/server/analyzer/engine/analysis/route/structure/RouteDetailAnalysisTestContext.scala b/server/src/test/scala/kpn/server/analyzer/engine/analysis/route/structure/RouteDetailAnalysisTestContext.scala index cf9fda008..32d7242d0 100644 --- a/server/src/test/scala/kpn/server/analyzer/engine/analysis/route/structure/RouteDetailAnalysisTestContext.scala +++ b/server/src/test/scala/kpn/server/analyzer/engine/analysis/route/structure/RouteDetailAnalysisTestContext.scala @@ -55,8 +55,8 @@ case class RouteDetailAnalysisTestContext(context: RouteDetailAnalysisContext) { else { "←" } - val from = element.fromNetworkNode.map(n => s" ${n.node.id}(${n.name})").getOrElse("") - val to = element.toNetworkNode.map(n => s" ${n.node.id}(${n.name})").getOrElse("") + val from = element.fromNetworkNode.map(n => s" ${n.nodeId}(${n.name})").getOrElse("") + val to = element.toNetworkNode.map(n => s" ${n.nodeId}(${n.name})").getOrElse("") val nodes = element.nodeIds.mkString(", ") s""" element-${element.id} ${element.fromNodeId}>${element.toNodeId}$from$to $direction nodes=$nodes""" } @@ -71,6 +71,6 @@ case class RouteDetailAnalysisTestContext(context: RouteDetailAnalysisContext) { } private def networkNodeStrings(nodeType: String, nodeDatas: Seq[RouteNodeData]): Seq[String] = { - nodeDatas.map(routeNodeData => s"$nodeType=${routeNodeData.node.id}(${routeNodeData.name})") + nodeDatas.map(routeNodeData => s"$nodeType=${routeNodeData.nodeId}(${routeNodeData.name})") } }