/* * Copyright (c) 2015 Data Harmonisation Panel * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * Data Harmonisation Panel <http://www.dhpanel.eu> */ package eu.esdihumboldt.util.geometry; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Function; import javax.annotation.Nullable; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.MultiLineString; import eu.esdihumboldt.util.DependencyOrderedList; /** * Helper methods for curves. * * @author Simon Templer */ public class CurveHelper { /** * Convert the given {@link MultiLineString} to a curve. * * @param lineStrings the source multi line stirng * @param fact a geometry factory * @param strict if it should be checked if the geometry fulfills the strict * requirements of a curve * @return the combined {@link MultiLineString} or <code>null</code> if the * geometry did not meet the requirements of the strict mode */ @Nullable public static MultiLineString combineCurve(MultiLineString lineStrings, GeometryFactory fact, boolean strict) { List<LineString> list = new ArrayList<>(); for (int i = 0; i < lineStrings.getNumGeometries(); i++) { list.add((LineString) lineStrings.getGeometryN(i)); } return combineCurve(list, fact, strict); } /** * Combine the given {@link LineString}s to a curve / * {@link MultiLineString}. * * @param lineStrings the line strings * @param fact a geometry factory * @param strict if it should be checked if the geometry fulfills the strict * requirements of a curve * @return the combined {@link MultiLineString} or <code>null</code> if the * geometry did not meet the requirements of the strict mode */ @Nullable public static MultiLineString combineCurve(List<? extends LineString> lineStrings, GeometryFactory fact, boolean strict) { return combineCurve(lineStrings, strict, geoms -> { return fact.createMultiLineString(geoms.toArray(new LineString[geoms.size()])); }); } /** * Combine the given {@link LineString}s using the given builder. * * @param lineStrings the line strings * @param strict if it should be checked if the geometry fulfills the strict * requirements of a curve * @param builder the builder function creating a combination of the * individual {@link LineString}s * @return the combined {@link MultiLineString} or <code>null</code> if the * geometry did not meet the requirements of the strict mode */ @Nullable public static <T> T combineCurve(List<? extends LineString> lineStrings, boolean strict, Function<List<LineString>, T> builder) { // try to order by start/end point (e.g. for composite curves) Map<Coordinate, LineString> endPoints = new HashMap<>(); for (LineString element : lineStrings) { endPoints.put(element.getEndPoint().getCoordinate(), element); } Map<LineString, Set<LineString>> dependencies = new HashMap<>(); for (LineString element : lineStrings) { // check if there is another line that ends at this line's start // and build the dependency map accordingly LineString dependsOn = endPoints.get(element.getStartPoint().getCoordinate()); @SuppressWarnings("unchecked") Set<LineString> deps = (Set<LineString>) ((dependsOn == null) ? Collections.emptySet() : Collections.singleton(dependsOn)); dependencies.put(element, deps); } // use dependency ordered list to achieve sorting // will only yield a perfect result if all lines can be combined into // one DependencyOrderedList<LineString> ordered = new DependencyOrderedList<>(dependencies); if (strict) { Coordinate lastEndPoint = null; for (LineString lineString : ordered.getInternalList()) { if (lastEndPoint != null) { // start point must be equal to last end point if (!lineString.getStartPoint().getCoordinate().equals(lastEndPoint)) { // not a strict curve return null; } } lastEndPoint = lineString.getEndPoint().getCoordinate(); } } else { // "best effort" } return builder.apply(ordered.getInternalList()); } /** * Combine the given {@link MultiLineString} to a single {@link LineString} * if possible. * * @param lineStrings the multi line string * @param fact a geometry factory * @return the combined {@link LineString} or <code>null</code> if the * geometry did not meet the requirements of the strict mode */ @Nullable public static LineString combineCurve(MultiLineString lineStrings, GeometryFactory fact) { List<LineString> list = new ArrayList<>(); for (int i = 0; i < lineStrings.getNumGeometries(); i++) { list.add((LineString) lineStrings.getGeometryN(i)); } return combineCurve(list, fact); } /** * Combine the given {@link LineString}s to a single {@link LineString} if * possible. * * @param lineStrings the line strings * @param fact a geometry factory * @return the combined {@link LineString} or <code>null</code> if the * geometry did not meet the requirements of the strict mode */ @Nullable public static LineString combineCurve(List<? extends LineString> lineStrings, GeometryFactory fact) { return combineCurve(lineStrings, true, geoms -> { List<Coordinate> coordinates = new ArrayList<>(); boolean skipFirst = false; for (LineString geom : geoms) { int index = 0; if (!skipFirst) { skipFirst = true; } else { index = 1; } for (int i = index; i < geom.getNumPoints(); i++) { coordinates.add(geom.getCoordinateN(i)); } } return fact.createLineString(coordinates.toArray(new Coordinate[coordinates.size()])); }); } }