Java tutorial
/** * Licensed to the TomTom International B.V. under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. TomTom International B.V. * licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /** * Copyright (C) 2009-2012 TomTom International B.V. * * TomTom (Legal Department) * Email: legal@tomtom.com * * TomTom (Technical contact) * Email: openlr@tomtom.com * * Address: TomTom International B.V., Oosterdoksstraat 114, 1011DK Amsterdam, * the Netherlands */ package openlr.map.sqlite.impl; import java.awt.geom.Path2D; import java.awt.geom.Point2D; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import openlr.map.FormOfWay; import openlr.map.FunctionalRoadClass; import openlr.map.GeoCoordinates; import openlr.map.GeoCoordinatesImpl; import openlr.map.Line; import openlr.map.MapDatabase; import openlr.map.Node; import openlr.map.utils.GeometryUtils; import org.apache.commons.lang.builder.HashCodeBuilder; /** * Implementation of the OpenLR {@link openlr.map.Line} interface for use with a * TomTom digital map in SQLite format. * * <p> * OpenLR is a trade mark of TomTom International B.V. * <p> * email: software@openlr.org * * @author TomTom International B.V. */ public final class LineImpl implements Line { /** * The ID of the {@link openlr.map.Node} located at the end of this * {@link openlr.map.Line}. */ private final long endNodeId; /** * The {@link openlr.map.FormOfWay} attribute value. */ private final FormOfWay formOfWay; /** * The {@link openlr.map.FunctionalRoadClass} attribute value. */ private final FunctionalRoadClass functionalRoadClass; /** * The unique ID of this {@link openlr.map.Line}. */ private final long id; /** * The length of this line in meters. */ private final int length; /** * The internal representation of the geometry. */ private final List<GeoCoordinates> shape; /** * The {@link openlr.map.MapDatabase} instance which manages this feature. */ private final MapDatabase mdb; /** * A map containing naming information in different languages. */ private final Map<Locale, List<String>> names; /** * The ID of the {@link openlr.map.Node} located at the start of this * {@link openlr.map.Line}. */ private final long startNodeId; /** * Creates an instance of this class representing a {@link openlr.map.Line} * feature with a given ID in the network managed by the given * * @param mapDB the {@link openlr.map.MapDatabase} this feature is managed by. * @param idValue the unique ID of this {@link openlr.map.Line}. * @param startNode the start node id * @param endNode the end node id * @param fow the {@link openlr.map.FormOfWay} attribute value. * @param frc the {@link openlr.map.FunctionalRoadClass} attribute value. * @param lineS the list of shape points defining the geometry. * @param lengthValue the length of this line in meters. * @param roadNames the naming information for the road which is represented by * this {@link openlr.map.Line}. * {@link openlr.map.MapDatabase} instance. */ LineImpl(final MapDatabase mapDB, final long idValue, final long startNode, final long endNode, final FormOfWay fow, final FunctionalRoadClass frc, final List<GeoCoordinates> lineS, final int lengthValue, final Map<Locale, List<String>> roadNames) { this.mdb = mapDB; this.id = idValue; this.startNodeId = startNode; this.endNodeId = endNode; this.formOfWay = fow; this.functionalRoadClass = frc; this.shape = lineS; this.length = lengthValue; this.names = Collections.unmodifiableMap(roadNames); } /** * {@inheritDoc} */ @Override public int distanceToPoint(final double arg0, final double arg1) { return determineShapeIndex(arg0, arg1).getDist(); } /** * {@inheritDoc} */ @Override public boolean equals(final Object other) { boolean ret = true; if (this == other) { ret = true; } else if (other == null) { ret = false; } else if (getClass() != other.getClass()) { ret = false; } else { final Line otherLine = (Line) other; if (getID() != otherLine.getID()) { ret = false; } } return ret; } /** * {@inheritDoc} */ @Override public Node getEndNode() { return mdb.getNode(endNodeId); } /** * {@inheritDoc} */ @Override public FormOfWay getFOW() { return formOfWay; } /** * {@inheritDoc} */ @Override public FunctionalRoadClass getFRC() { return functionalRoadClass; } /** * {@inheritDoc} */ @Override public long getID() { return id; } /** * {@inheritDoc} */ @Override public int getLineLength() { return length; } /** * {@inheritDoc} */ @Override public Map<Locale, List<String>> getNames() { return names; } /** * {@inheritDoc} */ @Override public Iterator<Line> getNextLines() { return getEndNode().getOutgoingLines(); } /** * {@inheritDoc} * @deprecated This method still exists to keep backwards compatibility * and will be removed in future releases, use * {@link #getGeoCoordinateAlongLine(int)} instead. */ @Override @Deprecated public Point2D.Double getPointAlongLine(final int distanceAlong) { GeoCoordinates point = getGeoCoordinateAlongLine(distanceAlong); return new Point2D.Double(point.getLongitudeDeg(), point.getLatitudeDeg()); } /** * {@inheritDoc} */ @Override public GeoCoordinates getGeoCoordinateAlongLine(int distanceAlong) { if (distanceAlong == 0) { return GeoCoordinatesImpl.newGeoCoordinatesUnchecked(getStartNode().getLongitudeDeg(), getStartNode().getLatitudeDeg()); } else if (distanceAlong >= getLineLength()) { return GeoCoordinatesImpl.newGeoCoordinatesUnchecked(getEndNode().getLongitudeDeg(), getEndNode().getLatitudeDeg()); } GeoCoordinates previous = null; double remaining = distanceAlong; for (GeoCoordinates shapePoint : shape) { if (previous != null) { double dist = GeometryUtils.distance(previous.getLongitudeDeg(), previous.getLatitudeDeg(), shapePoint.getLongitudeDeg(), shapePoint.getLatitudeDeg()); if (remaining > dist) { remaining -= dist; } else { double frac = remaining / dist; return getPointOnLine(previous, shapePoint, frac); } } previous = shapePoint; } throw new IllegalStateException("No shape points available"); } /** * {@inheritDoc} */ @Override public Iterator<Line> getPrevLines() { return getStartNode().getIncomingLines(); } /** * Delivers the shape in form of a {@link Path2D}. This method is marked * deprecated in the OpenLR interface. It is implemented in an inefficient * way here to keep it working but clients should switch to the successor * method {@link #getShapeCoordinates()}! * * @deprecated This method is kept but inefficient, please use {@link #getShapeCoordinates()} instead! */ @Override @Deprecated public Path2D.Double getShape() { Path2D.Double path = new Path2D.Double(); boolean first = true; for (GeoCoordinates coord : shape) { if (first) { path.moveTo(coord.getLongitudeDeg(), coord.getLatitudeDeg()); first = false; } else { path.lineTo(coord.getLongitudeDeg(), coord.getLatitudeDeg()); } } return path; } /** * {@inheritDoc} */ @Override public List<GeoCoordinates> getShapeCoordinates() { return shape; } /** * {@inheritDoc} */ @Override public Node getStartNode() { return mdb.getNode(startNodeId); } /** * {@inheritDoc} */ @Override public int hashCode() { HashCodeBuilder builder = new HashCodeBuilder(); builder.append(id); return builder.toHashCode(); } /** * {@inheritDoc} */ @Override public int measureAlongLine(final double arg0, final double arg1) { int currLength = 0; LinePointRelation linePoint = determineShapeIndex(arg0, arg1); Iterator<GeoCoordinates> coordsIter = shape.iterator(); GeoCoordinates previous = null; int counter = 1; int idx = linePoint.getIndex(); while (coordsIter.hasNext() && counter <= idx) { GeoCoordinates current = coordsIter.next(); if (previous != null) { if (counter < idx) { int dist = (int) GeometryUtils.distance(previous.getLongitudeDeg(), previous.getLatitudeDeg(), current.getLongitudeDeg(), current.getLatitudeDeg()); currLength += dist; } else if (counter == idx) { int dist = (int) GeometryUtils.distance(previous.getLongitudeDeg(), previous.getLatitudeDeg(), current.getLongitudeDeg(), current.getLatitudeDeg()); currLength += linePoint.getProjectionFactor() * dist; } } previous = current; counter++; } return currLength; } /** * Determine shape index. * * @param lon the lon * @param lat the lat * @return the line point relation */ private LinePointRelation determineShapeIndex(final double lon, final double lat) { int idx = -1; int minDist = Integer.MAX_VALUE; GeoCoordinates previous = null; int counter = 1; double minProjFac = 0.0; for (GeoCoordinates current : shape) { if (previous != null) { double projFac = projectionFactor(previous.getLongitudeDeg(), previous.getLatitudeDeg(), current.getLongitudeDeg(), current.getLatitudeDeg(), lon, lat); GeoCoordinates d = getPointOnLine(previous, current, projFac); int dist = (int) GeometryUtils.distance(lon, lat, d.getLongitudeDeg(), d.getLatitudeDeg()); if (dist < minDist) { idx = counter; minDist = dist; minProjFac = projFac; } } previous = current; counter++; } return new LinePointRelation(idx, minDist, minProjFac); } /** * Projection factor. * * @param p1Lon the p1 lon * @param p1Lat the p1 lat * @param p2Lon the p2 lon * @param p2Lat the p2 lat * @param lon the lon * @param lat the lat * @return the double */ private double projectionFactor(final double p1Lon, final double p1Lat, final double p2Lon, final double p2Lat, final double lon, final double lat) { double dx = p2Lon - p1Lon; double dy = p2Lat - p1Lat; double len = dx * dx + dy * dy; double r = ((lon - p1Lon) * dx + (lat - p1Lat) * dy) / len; if (r < 0.0) { r = 0.0; } else if (r > 1.0) { r = 1.0; } return r; } /** * Gets the point on line. * * @param p1 the p1 * @param p2 the p2 * @param frac the frac * @return the point on line */ private GeoCoordinates getPointOnLine(final GeoCoordinates p1, final GeoCoordinates p2, final double frac) { if (frac <= 0.0) { return p1; } if (frac >= 1.0) { return p2; } double x = (p2.getLongitudeDeg() - p1.getLongitudeDeg()) * frac + p1.getLongitudeDeg(); double y = (p2.getLatitudeDeg() - p1.getLatitudeDeg()) * frac + p1.getLatitudeDeg(); return GeoCoordinatesImpl.newGeoCoordinatesUnchecked(x, y); } /** * The Class LinePointRelation. */ private static class LinePointRelation { /** The idx. */ private final int idx; /** The dist. */ private final int dist; /** The projection factor. */ private final double projectionFactor; /** * Instantiates a new line point relation. * * @param index the index * @param d the d * @param projFac the proj fac */ LinePointRelation(final int index, final int d, final double projFac) { idx = index; projectionFactor = projFac; dist = d; } /** * Gets the index. * * @return the index */ int getIndex() { return idx; } /** * Gets the dist. * * @return the dist */ int getDist() { return dist; } /** * Gets the projection factor. * * @return the projection factor */ double getProjectionFactor() { return projectionFactor; } } /** * {@inheritDoc} */ @Override public String toString() { return String.valueOf(id); } }