/* * The JTS Topology Suite is a collection of Java classes that * implement the fundamental operations required to validate a given * geo-spatial data set to a known topological specification. * * Copyright (C) 2001 Vivid Solutions * * 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; either * version 2.1 of the License, or (at your option) any later version. * * 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * For more information, contact: * * Vivid Solutions * Suite #1A * 2328 Government Street * Victoria BC V8T 5G5 * Canada * * (250)385-6040 * www.vividsolutions.com */ package com.revolsys.geometry.model; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.TreeSet; import javax.measure.quantity.Area; import javax.measure.quantity.Length; import javax.measure.unit.Unit; import com.revolsys.datatype.DataType; import com.revolsys.datatype.DataTypes; import com.revolsys.geometry.model.segment.Segment; import com.revolsys.geometry.model.vertex.MultiPointVertex; import com.revolsys.geometry.model.vertex.Vertex; import com.revolsys.geometry.operation.simple.DuplicateVertexError; import com.revolsys.geometry.operation.valid.GeometryValidationError; import com.revolsys.util.Property; /** * Models a collection of {@link Point}s. * <p> * Any collection of Point is a valid MultiPoint. * *@version 1.7 */ public interface MultiPoint extends GeometryCollection, Punctual { @Override default boolean addIsSimpleErrors(final List<GeometryValidationError> errors, final boolean shortCircuit) { final Set<Point> points = new TreeSet<>(); for (final Vertex vertex : vertices()) { if (points.contains(vertex)) { final DuplicateVertexError error = new DuplicateVertexError(vertex.clone()); if (shortCircuit) { return false; } else { errors.add(error); } } else { points.add(vertex.newPoint2D()); } } return errors.isEmpty(); } @Override @SuppressWarnings("unchecked") default <V extends Geometry> V appendVertex(final Point newPoint, final int... geometryId) { if (newPoint == null || newPoint.isEmpty()) { return (V)this; } else { final GeometryFactory geometryFactory = getGeometryFactory(); if (isEmpty()) { return (V)newPoint.newGeometry(geometryFactory); } else { final List<Point> points = getPoints(); points.add(newPoint); return (V)geometryFactory.punctual(points); } } } @Override Punctual clone(); @Override default double distance(final double x, final double y, final double terminateDistance) { if (isEmpty()) { return Double.POSITIVE_INFINITY; } else { double minDistance = Double.MAX_VALUE; for (final Point point : getPoints()) { final double distance = point.distance(x, y); if (distance < minDistance) { minDistance = distance; if (distance <= terminateDistance) { return distance; } } } return minDistance; } } @Override default double distance(Geometry geometry, final double terminateDistance) { if (isEmpty()) { return Double.POSITIVE_INFINITY; } else if (Property.isEmpty(geometry)) { return Double.POSITIVE_INFINITY; } else { final GeometryFactory geometryFactory = getGeometryFactory(); geometry = geometry.convertGeometry(geometryFactory, 2); double minDistance = Double.MAX_VALUE; for (final Point point : getPoints()) { final double distance = geometry.distance(point, terminateDistance); if (distance < minDistance) { minDistance = distance; if (distance <= terminateDistance) { return distance; } } } return minDistance; } } @Override default boolean equalsExact(final Geometry other, final double tolerance) { if (!isEquivalentClass(other)) { return false; } return GeometryCollection.super.equalsExact(other, tolerance); } @Override default double getArea() { return 0; } @Override default double getArea(final Unit<Area> unit) { return 0; } /** * Gets the boundary of this geometry. * Zero-dimensional geometries have no boundary by definition, * so an empty GeometryCollection is returned. * * @return an empty GeometryCollection * @see Geometry#getBoundary */ @Override default Geometry getBoundary() { return getGeometryFactory().geometryCollection(); } @Override default int getBoundaryDimension() { return Dimension.FALSE; } @Override default Point getCentroid() { int pointCount = 0; double sumX = 0; double sumY = 0; for (final Point point : points()) { if (!point.isEmpty()) { pointCount += 1; final double x = point.getX(); final double y = point.getY(); sumX += x; sumY += y; } } final double centroidX = sumX / pointCount; final double centroidY = sumY / pointCount; final GeometryFactory geometryFactory = getGeometryFactory(); return geometryFactory.point(centroidX, centroidY); } @Override default double getCoordinate(final int partIndex, final int axisIndex) { final Point point = getPoint(partIndex); return point.getCoordinate(axisIndex); } @Override default DataType getDataType() { return DataTypes.MULTI_POINT; } @Override default int getDimension() { return 0; } @Override default double getLength() { return 0; } @Override default double getLength(final Unit<Length> unit) { return 0; } @Override default Point getPoint(final int partIndex) { return (Point)getGeometry(partIndex); } @Override default Segment getSegment(final int... segmentId) { return null; } @Override default Vertex getToVertex(final int... vertexId) { if (vertexId.length <= 2) { if (vertexId.length == 1 || vertexId[1] == 0) { final int vertexIndex = vertexId[0]; final int geometryCount = getGeometryCount(); if (vertexIndex >= 0 || vertexIndex < geometryCount) { return new MultiPointVertex(this, geometryCount - vertexIndex - 1); } } } return null; } @Override default Vertex getVertex(final int... vertexId) { if (vertexId.length <= 2) { if (vertexId.length == 1 || vertexId[1] == 0) { final int vertexIndex = vertexId[0]; if (vertexIndex >= 0 || vertexIndex < getGeometryCount()) { return new MultiPointVertex(this, vertexId); } } } return null; } @Override default boolean hasInvalidXyCoordinates() { for (final Point point : points()) { if (point.hasInvalidXyCoordinates()) { return true; } } return false; } @Override default boolean intersects(final Geometry geometry) { for (final Point point : points()) { if (point.intersects(geometry)) { return true; } } return false; } @Override default boolean isEquivalentClass(final Geometry other) { return other instanceof MultiPoint; } @Override default boolean isHomogeneousGeometryCollection() { return true; } @Override default boolean isValid() { return true; } @Override @SuppressWarnings("unchecked") default <V extends Geometry> V moveVertex(Point newPoint, final int... vertexId) { if (newPoint == null || newPoint.isEmpty()) { return (V)this; } else if (vertexId.length <= 2) { if (isEmpty()) { throw new IllegalArgumentException("Cannot move vertex for empty MultiPoint"); } else { final int partIndex = vertexId[0]; final int partCount = getGeometryCount(); if (partIndex >= 0 && partIndex < partCount) { final GeometryFactory geometryFactory = getGeometryFactory(); newPoint = newPoint.newGeometry(geometryFactory); final List<Point> points = new ArrayList<>(getPoints()); points.set(partIndex, newPoint); return (V)geometryFactory.punctual(points); } else { throw new IllegalArgumentException( "Part index must be between 0 and " + partCount + " not " + partIndex); } } } else { throw new IllegalArgumentException( "Vertex id's for MultiPoint must have length 1. " + Arrays.toString(vertexId)); } } @Override default Punctual newGeometry(final GeometryFactory geometryFactory) { final List<Point> newPoints = new ArrayList<>(); final List<Point> points = getPoints(); for (final Point point : points) { final Point newPoint = point.newGeometry(geometryFactory); newPoints.add(newPoint); } return geometryFactory.punctual(newPoints); } @SuppressWarnings("unchecked") @Override default <G> G newUsingGeometryFactory(final GeometryFactory factory) { if (factory == getGeometryFactory()) { return (G)this; } else if (isEmpty()) { return (G)factory.point(); } else { final Point[] points = new Point[getGeometryCount()]; for (int i = 0; i < getGeometryCount(); i++) { Point point = getPoint(i); point = point.newUsingGeometryFactory(factory); points[i] = point; } return (G)factory.punctual(points); } } @Override @SuppressWarnings("unchecked") default <G extends Geometry> G newValidGeometry() { if (isEmpty()) { return (G)this; } else if (isValid()) { return (G)normalize(); } else { return (G)union(); } } @Override default Punctual normalize() { if (isEmpty()) { return this; } else { final List<Point> geometries = new ArrayList<>(); for (final Geometry part : geometries()) { final Point normalizedPart = (Point)part.normalize(); geometries.add(normalizedPart); } Collections.sort(geometries); final GeometryFactory geometryFactory = getGeometryFactory(); final Punctual normalizedGeometry = geometryFactory.punctual(geometries); return normalizedGeometry; } } @Override default List<Vertex> pointVertices() { if (isEmpty()) { return Collections.emptyList(); } else { final int vertexCount = getVertexCount(); final List<Vertex> vertices = new ArrayList<>(vertexCount); for (int i = 0; i < vertexCount; i++) { final MultiPointVertex vertex = new MultiPointVertex(this, i); vertices.add(vertex); } return vertices; } } @Override default Punctual prepare() { return this; } @Override default Punctual removeDuplicatePoints() { return this; } @Override @SuppressWarnings("unchecked") default <G extends Geometry> G toClockwise() { return (G)this; } @Override @SuppressWarnings("unchecked") default <G extends Geometry> G toCounterClockwise() { return (G)this; } @Override default MultiPointVertex vertices() { return new MultiPointVertex(this, -1); } }