Class RandomPointsInGridBuilder

  1 /*
  2  * Copyright (c) 2016 Martin Davis.
  3  *
  4  * All rights reserved. This program and the accompanying materials
  5  * are made available under the terms of the Eclipse Public License 2.0
  6  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
  7  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
  8  * and the Eclipse Distribution License is available at
  9  *
 10  * http://www.eclipse.org/org/documents/edl-v10.php.
 11  */
 12  
 13 package org.locationtech.jts.shape.random;
 14  
 15 import org.locationtech.jts.geom.Coordinate;
 16 import org.locationtech.jts.geom.Geometry;
 17 import org.locationtech.jts.geom.GeometryFactory;
 18 import org.locationtech.jts.geom.MultiPoint;
 19 import org.locationtech.jts.math.MathUtil;
 20 import org.locationtech.jts.shape.GeometricShapeBuilder;
 21  
 22  
 23 /**
 24  * Creates random point sets 
 25  * where the points are constrained to lie in the cells of a grid. 
 26  * 
 27  * @author mbdavis
 28  *
 29  */
 30 public class RandomPointsInGridBuilder 
 31 extends GeometricShapeBuilder
 32 {
 33     private boolean isConstrainedToCircle = false;
 34     private double gutterFraction = 0;
 35     
 36   /**
 37    * Create a builder which will create shapes using the default
 38    * {@link GeometryFactory}.
 39    */
 40   public RandomPointsInGridBuilder()
 41   {
 42     super(new GeometryFactory());
 43   }
 44  
 45   /**
 46    * Create a builder which will create shapes using the given
 47    * {@link GeometryFactory}.
 48    *
 49    * @param geomFact the factory to use
 50    */
 51   public RandomPointsInGridBuilder(GeometryFactory geomFact)
 52   {
 53       super(geomFact);
 54   }
 55  
 56   /**
 57    * Sets whether generated points are constrained to lie
 58    * within a circle contained within each grid cell.
 59    * This provides greater separation between points
 60    * in adjacent cells.
 61    * <p>
 62    * The default is to not be constrained to a circle.
 63    * @param isConstrainedToCircle
 64    */
 65   public void setConstrainedToCircle(boolean isConstrainedToCircle)
 66   {
 67       this.isConstrainedToCircle = isConstrainedToCircle;
 68   }
 69   
 70   /**
 71    * Sets the fraction of the grid cell side which will be treated as
 72    * a gutter, in which no points will be created.
 73    * The provided value is clamped to the range [0.0, 1.0].
 74    * 
 75    * @param gutterFraction
 76    */
 77   public void setGutterFraction(double gutterFraction)
 78   {
 79       this.gutterFraction = gutterFraction;
 80   }
 81   
 82   /**
 83    * Gets the {@link MultiPoint} containing the generated point
 84    * 
 85    * @return a MultiPoint
 86    */
 87   public Geometry getGeometry()
 88   {
 89     int nCells = (int) Math.sqrt(numPts);
 90     // ensure that at least numPts points are generated
 91     if (nCells * nCells < numPts)
 92       nCells += 1;
 93  
 94     double gridDX = getExtent().getWidth() / nCells;
 95     double gridDY = getExtent().getHeight() / nCells;
 96  
 97     double gutterFrac = MathUtil.clamp(gutterFraction, 0.01.0);
 98     double gutterOffsetX = gridDX * gutterFrac/2;
 99     double gutterOffsetY = gridDY * gutterFrac/2;
100     double cellFrac = 1.0 - gutterFrac;
101     double cellDX = cellFrac * gridDX;
102     double cellDY = cellFrac * gridDY;
103         
104     Coordinate[] pts = new Coordinate[nCells * nCells];
105     int index = 0;
106     for (int i = 0; i < nCells; i++) {
107       for (int j = 0; j < nCells; j++) {
108           double orgX = getExtent().getMinX() + i * gridDX + gutterOffsetX;
109           double orgY = getExtent().getMinY() + j * gridDY + gutterOffsetY;
110         pts[index++] = randomPointInCell(orgX, orgY, cellDX, cellDY);
111       }
112     }
113     return geomFactory.createMultiPointFromCoords(pts);
114   }
115   
116   private Coordinate randomPointInCell(double orgX, double orgY, double xLen, double yLen)
117   {
118       if (isConstrainedToCircle) {
119           return randomPointInCircle(
120                   orgX, 
121                   orgY, 
122                   xLen, yLen);
123       }
124       return randomPointInGridCell(orgX, orgY, xLen, yLen);
125   }
126   
127   private Coordinate randomPointInGridCell(double orgX, double orgY, double xLen, double yLen)
128   {
129     double x = orgX + xLen * Math.random();
130     double y = orgY + yLen * Math.random();
131     return createCoord(x, y);
132   }
133  
134   private static Coordinate randomPointInCircle(double orgX, double orgY, double width, double height)
135   {
136       double centreX = orgX + width/2;
137       double centreY = orgY + height/2;
138           
139       double rndAng = 2 * Math.PI * Math.random();
140       double rndRadius = Math.random();
141     // use square root of radius, since area is proportional to square of radius
142     double rndRadius2 = Math.sqrt(rndRadius);
143       double rndX = width/2 * rndRadius2 * Math.cos(rndAng); 
144       double rndY = height/2 * rndRadius2 * Math.sin(rndAng); 
145       
146     double x0 = centreX + rndX;
147     double y0 = centreY + rndY;
148     return new Coordinate(x0, y0);    
149   }
150  
151 }
152