/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2013, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotoolkit.display2d.style.j2d; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import org.apache.sis.geometry.GeneralDirectPosition; import org.geotoolkit.referencing.GeodeticCalculator; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.TransformException; /** * Walk along a path using geodetic distances. * * @author Johann Sorel (Geomatys) * @module */ public class GeodeticPathWalker { private final PathIterator pathIterator; private final float lastPoint[] = new float[6]; private final float currentPoint[] = new float[6]; private float lastmoveToX = 0f; private float lastmoveToY = 0f; private float segmentStartX = 0f; private float segmentStartY = 0f; private float segmentEndX = 0f; private float segmentEndY = 0f; private float segmentLenght = 0f; private float remaining = 0f; private float angle = Float.NaN; private boolean finished = false; //for geodetic distance calculation private final GeodeticCalculator calculator; private final GeneralDirectPosition startPos; private final GeneralDirectPosition endPos; public GeodeticPathWalker(final PathIterator iterator, CoordinateReferenceSystem crs) throws TransformException { this.pathIterator = iterator; calculator = new GeodeticCalculator(crs); startPos = new GeneralDirectPosition(crs); endPos = new GeneralDirectPosition(crs); //get the first segment boolean first = true; while (first && !pathIterator.isDone()) { final int type = pathIterator.currentSegment(currentPoint); switch (type) { case PathIterator.SEG_MOVETO: System.arraycopy(currentPoint, 0, lastPoint, 0, 6); segmentStartX = lastPoint[0]; segmentStartY = lastPoint[1]; segmentEndX = currentPoint[0]; segmentEndY = currentPoint[1]; //keep point for close instruction lastmoveToX = currentPoint[0]; lastmoveToY = currentPoint[1]; break; case PathIterator.SEG_CLOSE: currentPoint[0] = lastmoveToX; currentPoint[1] = lastmoveToY; // Fall into.... case PathIterator.SEG_LINETO: segmentStartX = lastPoint[0]; segmentStartY = lastPoint[1]; segmentEndX = currentPoint[0]; segmentEndY = currentPoint[1]; segmentLenght = distance(segmentStartX, segmentStartY, segmentEndX, segmentEndY); angle = Float.NaN; remaining = segmentLenght; first = false; break; } System.arraycopy(currentPoint, 0, lastPoint, 0, 6); pathIterator.next(); } } public boolean isFinished() { return finished; //|| (pathIterator.isDone() && remaining <= 0); } /** * Get the remaining distance until the current line segment end. * @return float */ public float getSegmentLengthRemaining(){ return remaining; } public void walk(float distance) throws TransformException { if (remaining > distance) { remaining -= distance; } else { distance -= remaining; remaining = 0; while (!pathIterator.isDone()) { final int type = pathIterator.currentSegment(currentPoint); switch (type) { case PathIterator.SEG_MOVETO: System.arraycopy(currentPoint, 0, lastPoint, 0, 6); segmentStartX = lastPoint[0]; segmentStartY = lastPoint[1]; segmentEndX = currentPoint[0]; segmentEndY = currentPoint[1]; //keep point for close instruction lastmoveToX = currentPoint[0]; lastmoveToY = currentPoint[1]; break; case PathIterator.SEG_CLOSE: currentPoint[0] = lastmoveToX; currentPoint[1] = lastmoveToY; // Fall into.... case PathIterator.SEG_LINETO: segmentStartX = lastPoint[0]; segmentStartY = lastPoint[1]; segmentEndX = currentPoint[0]; segmentEndY = currentPoint[1]; segmentLenght = distance(segmentStartX, segmentStartY, segmentEndX, segmentEndY); angle = Float.NaN; remaining = segmentLenght; break; } System.arraycopy(currentPoint, 0, lastPoint, 0, 6); pathIterator.next(); if (remaining >= distance) { remaining -= distance; distance = 0; return; } else { distance -= remaining; remaining = 0; } } //if we reach here, it means the iterator is finished and nothing left finished = true; } } public Point2D getPosition(final Point2D pt) { final double perc = 1d - remaining / segmentLenght; final double tlX = (segmentEndX - segmentStartX) * perc + segmentStartX; final double tlY = (segmentEndY - segmentStartY) * perc + segmentStartY; if(pt == null){ return new Point2D.Double(tlX, tlY); }else{ pt.setLocation(tlX, tlY); return pt; } } public float getRotation() { if(Float.isNaN(angle)){ angle = angle(segmentStartX, segmentStartY, segmentEndX, segmentEndY); } return angle; } private float distance(final float x1, final float y1, final float x2, final float y2) throws TransformException { startPos.setCoordinate(x1,y1); endPos.setCoordinate(x2,y2); calculator.setStartingPosition(startPos); calculator.setDestinationPosition(endPos); return (float) calculator.getOrthodromicDistance(); } private static float angle(final float x1, final float y1, final float x2, final float y2) { float dx = x1 - x2; float dy = y1 - y2; return (float) Math.atan2(dy, dx); } }