Skip to content

Commit

Permalink
#368 improve route links
Browse files Browse the repository at this point in the history
  • Loading branch information
vmarc committed Jun 23, 2024
1 parent 6562944 commit 4a1e392
Show file tree
Hide file tree
Showing 18 changed files with 336 additions and 360 deletions.
6 changes: 6 additions & 0 deletions frontend/src/app/api/common/data/relation-id-member.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// this file is generated, please do not modify

export interface RelationIdMember {
readonly relationId: number;
readonly role: string;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package kpn.server.analyzer.engine.analysis.route.structure.reference;

public class WayInfo {
// equivalent of Josm WayConnectionType
public class ReferenceLink {

private final boolean nodeOrRelationMember; // member does not contain a way
public boolean linkedToPreviousMember;
Expand All @@ -25,7 +26,7 @@ public boolean isRoundabout() {
public boolean isOnewayHead;
public boolean isOnewayTail;

public WayInfo(
public ReferenceLink(
final boolean linkedToPreviousMember,
final boolean linkedToNextMember,
final Direction direction
Expand All @@ -37,15 +38,15 @@ public WayInfo(
nodeOrRelationMember = false;
}

public WayInfo(final boolean nodeOrRelationMember) {
public ReferenceLink(final boolean nodeOrRelationMember) {
this.linkedToPreviousMember = false;
this.linkedToNextMember = false;
this.isLoop = false;
this.direction = Direction.NONE;
this.nodeOrRelationMember = nodeOrRelationMember;
}

public WayInfo() {
public ReferenceLink() {
this(true);
}

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package kpn.server.analyzer.engine.analysis.route.structure.reference;

import kpn.server.analyzer.engine.analysis.route.structure.reference.WayInfo.Direction;
import kpn.server.analyzer.engine.analysis.route.structure.reference.ReferenceLink.Direction;

import static kpn.server.analyzer.engine.analysis.route.structure.reference.WayInfo.Direction.NONE;
import static kpn.server.analyzer.engine.analysis.route.structure.reference.WayInfo.Direction.ROUNDABOUT_RIGHT;
import static kpn.server.analyzer.engine.analysis.route.structure.reference.ReferenceLink.Direction.NONE;
import static kpn.server.analyzer.engine.analysis.route.structure.reference.ReferenceLink.Direction.ROUNDABOUT_RIGHT;

public class Utils {

Expand Down
93 changes: 46 additions & 47 deletions server/src/main/scala/kpn/core/analysis/Link.scala
Original file line number Diff line number Diff line change
@@ -1,75 +1,74 @@
package kpn.core.analysis

import kpn.core.analysis.LinkType.BACKWARD
import kpn.core.analysis.LinkType.FORWARD
import kpn.core.analysis.LinkType.NONE
import kpn.core.analysis.LinkType.ROUNDABOUT

import scala.collection.mutable

case class Link(
linkType: LinkType.Value,
linkType: LinkType,
hasPrev: Boolean,
hasNext: Boolean,
isLoop: Boolean,
isOnewayLoopForwardPart: Boolean,
isOnewayLoopBackwardPart: Boolean,
isOnewayHead: Boolean,
isOnewayTail: Boolean,
invalid: Boolean
) {

def isValid: Boolean = !invalid

def name: String = {
if (!isValid) {
"n"
val linkTypeLetter = linkType match {
case LinkType.Forward => "f"
case LinkType.Backward => "b"
case LinkType.RoundaboutLeft => "r"
case LinkType.RoundaboutRight => "r"
case LinkType.Unknown => "n"
}
else {

val linkTypeLetter = linkType match {
case FORWARD => "f"
case BACKWARD => "b"
case ROUNDABOUT => "r"
case NONE => "n"
}

val code = (if (hasPrev) 1 else 0) +
(if (hasNext) 2 else 0) +
(if (isLoop) 4 else 0) +
(if (isOnewayLoopForwardPart) 8 else 0) +
(if (isOnewayLoopBackwardPart) 16 else 0) +
(if (isOnewayHead) 32 else 0) +
(if (isOnewayTail) 64 else 0)
val code = (if (hasPrev) 1 else 0) +
(if (hasNext) 2 else 0) +
(if (isLoop) 4 else 0) +
(if (isOnewayLoopForwardPart) 8 else 0) +
(if (isOnewayLoopBackwardPart) 16 else 0) +
(if (isOnewayHead) 32 else 0) +
(if (isOnewayTail) 64 else 0)

"w%s%03d".format(linkTypeLetter, code)
}
"w%s%03d".format(linkTypeLetter, code)
}

def description: String = {
if (!isValid) {
"I"
}
else {
val sb = new mutable.StringBuilder
val sb = new mutable.StringBuilder

if (!hasPrev) {
sb.append("*")
}
if (!hasPrev) {
sb.append("*")
}

val elements = (if (isLoop) Seq("loop") else Seq.empty) ++
(if (isOnewayLoopForwardPart) Seq("fp") else Seq.empty) ++
(if (isOnewayLoopBackwardPart) Seq("bp") else Seq.empty) ++
(if (isOnewayHead) Seq("head") else Seq.empty) ++
(if (isOnewayTail) Seq("tail") else Seq.empty) ++
Seq(linkType.toString.toLowerCase)
val elements = (if (isLoop) Seq("loop") else Seq.empty) ++
(if (isOnewayLoopForwardPart) Seq("fp") else Seq.empty) ++
(if (isOnewayLoopBackwardPart) Seq("bp") else Seq.empty) ++
(if (isOnewayHead) Seq("head") else Seq.empty) ++
(if (isOnewayTail) Seq("tail") else Seq.empty) ++
Seq(linkType.toString.toLowerCase)

sb.append(elements.mkString("-"))
sb.append(elements.mkString("-"))

if (!hasNext) {
sb.append("*")
}
sb.toString()
if (!hasNext) {
sb.append("*")
}
sb.toString()
}

def reportString: String = {
val sb = new StringBuilder
sb.append("p " + bool(hasPrev))
sb.append(" n " + bool(hasNext))
sb.append(" loop " + bool(isLoop))
sb.append(" fp " + bool(isOnewayLoopForwardPart))
sb.append(" bp " + bool(isOnewayLoopBackwardPart))
sb.append(" head " + bool(isOnewayHead))
sb.append(" tail " + bool(isOnewayTail))
sb.append(String.format(" d %s", linkType.entryName.toLowerCase))
sb.toString
}

private def bool(value: Boolean): String = {
if (value) "" else " "
}
}
20 changes: 18 additions & 2 deletions server/src/main/scala/kpn/core/analysis/LinkType.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
package kpn.core.analysis

object LinkType extends Enumeration {
val FORWARD, BACKWARD, ROUNDABOUT, NONE = Value
import enumeratum.Enum
import enumeratum.EnumEntry

sealed trait LinkType extends EnumEntry

object LinkType extends Enum[LinkType] {

val values: IndexedSeq[LinkType] = findValues

case object Forward extends LinkType // the first node of this way is connected to the previous way and/or the last node of this way is connected to the next way

case object Backward extends LinkType

case object RoundaboutLeft extends LinkType // tagged as roundabout and connected to the previous/next member

case object RoundaboutRight extends LinkType

case object Unknown extends LinkType
}
15 changes: 15 additions & 0 deletions server/src/main/scala/kpn/core/analysis/RouteMember.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,37 @@ import kpn.api.common.route.RouteNetworkNodeInfo

trait RouteMember {
def endNodes: Seq[Node]

def memberType: String

def link: Option[Link]

def linkName: String

def nodes: Seq[RouteNetworkNodeInfo]

def id: Long

def role: Option[String]

def element: Element

def linkDescription: String

def length: String

def nodeCount: String

def name: String

def description: String

def from: String

def to: String

def fromNode: Node

def toNode: Node

def accessible: Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ case class RouteMemberNode(name: String, alternateName: String, longName: Option

def memberType: String = "node"

def linkType: LinkType.Value = LinkType.NONE
def link: Option[Link] = None

def linkType: LinkType = LinkType.Unknown

def linkName: String = "n"

Expand Down
15 changes: 11 additions & 4 deletions server/src/main/scala/kpn/core/analysis/RouteMemberWay.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import kpn.server.analyzer.engine.analysis.route.WayAnalyzer

case class RouteMemberWay(
name: String,
link: Link,
link: Option[Link],
role: Option[String],
way: Way,
fromNode: Node,
toNode: Node,
from: String,
to:String,
to: String,
accessible: Boolean,
routeNodes: Seq[RouteNode]
) extends RouteMember {
Expand All @@ -34,7 +34,8 @@ case class RouteMemberWay(
}
}

def linkName: String = link.name
def linkName: String = link.map(_.name).getOrElse("")

def nodes: Seq[RouteNetworkNodeInfo] = routeNodes.map { rn =>
RouteNetworkNodeInfo(
rn.id,
Expand All @@ -45,10 +46,16 @@ case class RouteMemberWay(
rn.node.longitude
)
}

def id: Long = way.id

def element: Element = way
def linkDescription: String = link.description

def linkDescription: String = link.map(_.description).getOrElse("")

def length: String = way.length.toString + " m"

def nodeCount: String = way.nodes.size.toString

def description: String = name
}
46 changes: 26 additions & 20 deletions server/src/main/scala/kpn/core/report/LinkImageBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,28 @@ object LinkImageBuilder {

def build(fileName: String, link: Link): Unit = {
val image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB)
val g2 = image.createGraphics()
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY)
val g = buildGraphicsContext(image)
new LinkImageBuilder(g, link).paint()
write(fileName, image)
}

def buildNode(fileName: String): Unit = {
val image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB)
val g2 = buildGraphicsContext(image)
new LinkImageBuilder(g2, null).paintNode()
write(fileName, image)
}

private def buildGraphicsContext(image: BufferedImage): Graphics2D = {
val g = image.createGraphics()
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY)
g.setColor(Color.white)
g.fillRect(0, 0, size, size)
g
}

g2.setColor(Color.white)
g2.fillRect(0, 0, size, size)
new LinkImageBuilder(g2, link).paint()
private def write(fileName: String, image: BufferedImage): Unit = {
val out = new FileOutputStream(fileName)
ImageIO.write(image, "png", out)
out.close()
Expand All @@ -45,16 +60,6 @@ class LinkImageBuilder(g: Graphics2D, link: Link) {
import kpn.core.report.LinkImageBuilder.*

def paint(): Unit = {

if (link == null || !link.isValid) {
paintNode()
}
else {
paintWay()
}
}

private def paintWay(): Unit = {
val width = size
val height = size

Expand Down Expand Up @@ -183,7 +188,7 @@ class LinkImageBuilder(g: Graphics2D, link: Link) {
g.setStroke(new BasicStroke())
}

private def paintNode(): Unit = {
def paintNode(): Unit = {
val w = size / 4
val x1 = (size / 2) - (w / 2)
g.setColor(Color.blue)
Expand All @@ -193,16 +198,17 @@ class LinkImageBuilder(g: Graphics2D, link: Link) {

private def drawRoundabout(x: Int, y: Int): Unit = {
val image = link.linkType match {
case LinkType.ROUNDABOUT => Some(roundabout)
case LinkType.RoundaboutLeft => Some(roundabout)
case LinkType.RoundaboutRight => Some(roundabout)
case _ => None
}
image.foreach(i => g.drawImage(i, x, y, null))
}

private def drawArrow(xLeft: Int, xRight: Int, y: Int): Unit = {
val down = link.linkType match {
case LinkType.FORWARD => Some(true)
case LinkType.BACKWARD => Some(false)
case LinkType.Forward => Some(true)
case LinkType.Backward => Some(false)
case _ => None
}

Expand Down
Loading

0 comments on commit 4a1e392

Please sign in to comment.