/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2010, Open Source Geospatial Foundation (OSGeo) * * 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.geotools.grid.hexagon; import com.vividsolutions.jts.densify.Densifier; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import org.geotools.geometry.jts.JTSFactoryFinder; import org.geotools.geometry.jts.ReferencedEnvelope; import org.opengis.referencing.crs.CoordinateReferenceSystem; /** * Default implementation of {@code Hexagon}. * * @author mbedward * @since 2.7 * * * @source $URL$ * @version $Id$ */ public class HexagonImpl implements Hexagon { private static final double ROOT3 = Math.sqrt(3.0); private static final GeometryFactory geomFactory = JTSFactoryFinder.getGeometryFactory(null); private final double sideLen; private final double area; private final double minX; private final double minY; private final HexagonOrientation orientation; private Coordinate[] vertices; private final CoordinateReferenceSystem crs; /** * Creates a new hexagon. * * @param minX the min X ordinate of the bounding rectangle * * @param minY the min Y ordinate of the bounding rectangle * * @param sideLen the side length * * @param orientation either {@code Hexagon.Orientation.FLAT} or * {@code Hexagon.Orientation.ANGLED} * * @param crs the coordinate reference system (may be {@code null}) */ public HexagonImpl(double minX, double minY, double sideLen, HexagonOrientation orientation, CoordinateReferenceSystem crs) { if (sideLen <= 0.0) { throw new IllegalArgumentException("side length must be > 0"); } if (orientation == null) { throw new IllegalArgumentException("orientation must be a non-null value"); } this.sideLen = sideLen; this.minX = minX; this.minY = minY; this.orientation = orientation; this.crs = crs; this.area = Hexagons.sideLengthToArea(sideLen); calculateVertices(); } /** * {@inheritDoc} */ public double getSideLength() { return sideLen; } /** * {@inheritDoc} */ public double getArea() { return area; } /** * {@inheritDoc} */ public HexagonOrientation getOrientation() { return orientation; } /** * {@inheritDoc} * * @return an array of copies of the vertex {@code Coordinates} */ public Coordinate[] getVertices() { Coordinate[] copy = new Coordinate[6]; for (int i = 0; i < 6; i++) { copy[i] = new Coordinate(vertices[i]); } return copy; } /** * {@inheritDoc} */ public ReferencedEnvelope getBounds() { if (orientation == HexagonOrientation.FLAT) { return new ReferencedEnvelope( minX, minX + 2.0 * sideLen, minY, minY + ROOT3 * sideLen, crs); } else { // ANGLED return new ReferencedEnvelope( minX, minX + ROOT3 * sideLen, minY, minY + 2.0 * sideLen, crs); } } /** * {@inheritDoc} */ public Coordinate getCenter() { if (orientation == HexagonOrientation.FLAT) { return new Coordinate(minX + sideLen, minY + ROOT3 * 0.5 * sideLen); } else { // ANGLED return new Coordinate(minX + ROOT3 * 0.5 * sideLen, minY + sideLen); } } /** * {@inheritDoc} */ public Geometry toGeometry() { Coordinate[] ring = new Coordinate[7]; System.arraycopy(vertices, 0, ring, 0, 6); ring[6] = vertices[0]; return geomFactory.createPolygon(geomFactory.createLinearRing(ring), null); } /** * {@inheritDoc} * * @throws IllegalArgumentException if {@code maxSpacing} is not a positive value */ public Geometry toDenseGeometry(double maxSpacing) { if (maxSpacing <= 0.0) { throw new IllegalArgumentException("maxSpacing must be a positive value"); } return Densifier.densify(this.toGeometry(), maxSpacing); } /** * Calculates vertex coordinates. */ private void calculateVertices() { if (orientation == null) { throw new IllegalStateException( "Orientation must be set before calculating vertices"); } vertices = new Coordinate[6]; final double span = ROOT3 * sideLen; if (orientation == HexagonOrientation.FLAT) { vertices[0] = new Coordinate(minX + 0.5 * sideLen, minY + span); vertices[1] = new Coordinate(minX + 1.5 * sideLen, minY + span); vertices[2] = new Coordinate(minX + 2.0 * sideLen, minY + span/2.0); vertices[3] = new Coordinate(minX + 1.5 * sideLen, minY); vertices[4] = new Coordinate(minX + 0.5 * sideLen, minY); vertices[5] = new Coordinate(minX, minY + span/2.0); } else { // Orientation.ANGLED vertices[0] = new Coordinate(minX + 0.5 * span, minY + 2.0 * sideLen); vertices[1] = new Coordinate(minX + span, minY + 1.5 * sideLen); vertices[2] = new Coordinate(minX + span, minY + 0.5 * sideLen); vertices[3] = new Coordinate(minX + 0.5 * span, minY); vertices[4] = new Coordinate(minX, minY + 0.5 * sideLen); vertices[5] = new Coordinate(minX, minY + 1.5 * sideLen); } } }