1 /* 2 * Copyright (c) 2016 Vivid Solutions. 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.jtstest.function; 14 15 import java.awt.Font; 16 import java.util.ArrayList; 17 import java.util.Collections; 18 import java.util.List; 19 20 import org.locationtech.jts.algorithm.Angle; 21 import org.locationtech.jts.awt.*; 22 import org.locationtech.jts.geom.*; 23 import org.locationtech.jts.geom.util.*; 24 import org.locationtech.jts.util.GeometricShapeFactory; 25 import org.locationtech.jtstest.geomfunction.Metadata; 26 27 public class CreateShapeFunctions { 28 29 30 private static final int DEFAULT_POINTSIZE = 100; 31 fontGlyphSerif(Geometry g, String text)32 public static Geometry fontGlyphSerif(Geometry g, String text) 33 { 34 return fontGlyph(g, text, new Font(FontGlyphReader.FONT_SERIF, Font.PLAIN, DEFAULT_POINTSIZE)); 35 } 36 fontGlyphSerifPointSize(Geometry g, String text, @Metadata(title=R) int pointSize)37 public static Geometry fontGlyphSerifPointSize(Geometry g, String text, 38 @Metadata(title="Point size") 39 int pointSize) 40 { 41 return fontGlyph(g, text, new Font(FontGlyphReader.FONT_SERIF, Font.PLAIN, pointSize)); 42 } 43 fontGlyph(Geometry g, String text, @Metadata(title=R) String fontName)44 public static Geometry fontGlyph(Geometry g, String text, 45 @Metadata(title="Font name") 46 String fontName) 47 { 48 return fontGlyph(g, text, new Font(fontName, Font.PLAIN, DEFAULT_POINTSIZE)); 49 } 50 fontGlyphSansSerif(Geometry g, String text)51 public static Geometry fontGlyphSansSerif(Geometry g, String text) 52 { 53 return fontGlyph(g, text, new Font(FontGlyphReader.FONT_SANSSERIF, Font.PLAIN, DEFAULT_POINTSIZE)); 54 } 55 fontGlyphMonospaced(Geometry g, String text)56 public static Geometry fontGlyphMonospaced(Geometry g, String text) 57 { 58 return fontGlyph(g, text, new Font(FontGlyphReader.FONT_MONOSPACED, Font.PLAIN, DEFAULT_POINTSIZE)); 59 } 60 fontGlyph(Geometry g, String text, Font font)61 private static Geometry fontGlyph(Geometry g, String text, Font font) { 62 Envelope env = FunctionsUtil.getEnvelopeOrDefault(g); 63 GeometryFactory geomFact = FunctionsUtil.getFactoryOrDefault(g); 64 65 Geometry textGeom = FontGlyphReader.read(text, font, geomFact); 66 Envelope envText = textGeom.getEnvelopeInternal(); 67 68 if (g != null) { 69 // transform to baseline 70 Coordinate baseText0 = new Coordinate(envText.getMinX(), envText.getMinY()); 71 Coordinate baseText1 = new Coordinate(envText.getMaxX(), envText.getMinY()); 72 Coordinate baseGeom0 = new Coordinate(env.getMinX(), env.getMinY()); 73 Coordinate baseGeom1 = new Coordinate(env.getMaxX(), env.getMinY()); 74 AffineTransformation trans = AffineTransformationFactory.createFromBaseLines(baseText0, baseText1, baseGeom0, baseGeom1); 75 return trans.transform(textGeom); 76 } 77 return textGeom; 78 } 79 grid(Geometry g, int nCells)80 public static Geometry grid(Geometry g, int nCells) 81 { 82 Envelope env = FunctionsUtil.getEnvelopeOrDefault(g); 83 GeometryFactory geomFact = FunctionsUtil.getFactoryOrDefault(g); 84 85 int nCellsOnSideY = (int) Math.sqrt(nCells); 86 int nCellsOnSideX = nCells / nCellsOnSideY; 87 88 // alternate: make square cells, with varying grid width/height 89 //double extent = env.minExtent(); 90 //double nCellsOnSide = Math.max(nCellsOnSideY, nCellsOnSideX); 91 92 double cellSizeX = env.getWidth() / nCellsOnSideX; 93 double cellSizeY = env.getHeight() / nCellsOnSideY; 94 95 List geoms = new ArrayList(); 96 97 for (int i = 0; i < nCellsOnSideX; i++) { 98 for (int j = 0; j < nCellsOnSideY; j++) { 99 double x = env.getMinX() + i * cellSizeX; 100 double y = env.getMinY() + j * cellSizeY; 101 double x2 = env.getMinX() + (i + 1) * cellSizeX; 102 double y2 = env.getMinY() + (j + 1) * cellSizeY; 103 104 Envelope cellEnv = new Envelope(x, x2, y, y2); 105 geoms.add(geomFact.toGeometry(cellEnv)); 106 } 107 } 108 return geomFact.buildGeometry(geoms); 109 } 110 gridPoints(Geometry g, int nCells)111 public static Geometry gridPoints(Geometry g, int nCells) 112 { 113 Envelope env = FunctionsUtil.getEnvelopeOrDefault(g); 114 GeometryFactory geomFact = FunctionsUtil.getFactoryOrDefault(g); 115 116 int nCellsOnSideY = (int) Math.sqrt(nCells); 117 int nCellsOnSideX = nCells / nCellsOnSideY; 118 119 double cellSizeX = env.getWidth() / (nCellsOnSideX - 1); 120 double cellSizeY = env.getHeight() / (nCellsOnSideY - 1); 121 122 CoordinateList pts = new CoordinateList(); 123 124 for (int i = 0; i < nCellsOnSideX; i++) { 125 for (int j = 0; j < nCellsOnSideY; j++) { 126 double x = env.getMinX() + i * cellSizeX; 127 double y = env.getMinY() + j * cellSizeY; 128 129 pts.add( new Coordinate(x, y) ); 130 } 131 } 132 return geomFact.createMultiPointFromCoords(pts.toCoordinateArray()); 133 } 134 supercircle3(Geometry g, int nPts)135 public static Geometry supercircle3(Geometry g, int nPts) 136 { 137 return supercircle(g, nPts, 3); 138 } 139 squircle(Geometry g, int nPts)140 public static Geometry squircle(Geometry g, int nPts) 141 { 142 return supercircle(g, nPts, 4); 143 } 144 supercircle5(Geometry g, int nPts)145 public static Geometry supercircle5(Geometry g, int nPts) 146 { 147 return supercircle(g, nPts, 5); 148 } 149 supercirclePoint5(Geometry g, int nPts)150 public static Geometry supercirclePoint5(Geometry g, int nPts) 151 { 152 return supercircle(g, nPts, 0.5); 153 } 154 155 supercircle(Geometry g, @Metadata(title=R) int nPts, @Metadata(title=R) double pow)156 public static Geometry supercircle(Geometry g, 157 @Metadata(title="Point count") 158 int nPts, 159 @Metadata(title="Power") 160 double pow) 161 { 162 GeometricShapeFactory gsf = new GeometricShapeFactory(); 163 gsf.setNumPoints(nPts); 164 if (g != null) 165 gsf.setEnvelope(g.getEnvelopeInternal()); 166 else 167 gsf.setEnvelope(new Envelope(0, 1, 0, 1)); 168 return gsf.createSupercircle(pow); 169 } 170 ellipse(Geometry g, int nPts)171 public static Geometry ellipse(Geometry g, int nPts) 172 { 173 GeometricShapeFactory gsf = new GeometricShapeFactory(); 174 gsf.setNumPoints(nPts); 175 if (g != null) 176 gsf.setEnvelope(g.getEnvelopeInternal()); 177 else 178 gsf.setEnvelope(new Envelope(0, 1, 0, 1)); 179 return gsf.createCircle(); 180 } ellipseRotate(Geometry g, int nPts, @Metadata(title=R) double ang)181 public static Geometry ellipseRotate(Geometry g, int nPts, 182 @Metadata(title="Angle") 183 double ang) 184 { 185 GeometricShapeFactory gsf = new GeometricShapeFactory(); 186 gsf.setNumPoints(nPts); 187 gsf.setRotation(ang); 188 if (g != null) 189 gsf.setEnvelope(g.getEnvelopeInternal()); 190 else 191 gsf.setEnvelope(new Envelope(0, 1, 0, 1)); 192 return gsf.createCircle(); 193 } 194 sineStar(Geometry g, @Metadata(title=R) int nArms, @Metadata(title=R) int nPts)195 public static Geometry sineStar(Geometry g, 196 @Metadata(title="Arm count") 197 int nArms, 198 @Metadata(title="Point count") 199 int nPts) 200 { 201 Envelope env = FunctionsUtil.getEnvelopeOrDefault(g); 202 GeometryFactory geomFact = FunctionsUtil.getFactoryOrDefault(g); 203 204 double size = Math.min(env.getHeight(), env.getWidth()); 205 SineStarFactory shape = new SineStarFactory(geomFact); 206 shape.setCentre(env.centre()); 207 shape.setSize(size); 208 shape.setNumPoints(nPts); 209 shape.setNumArms(nArms); 210 shape.setArmLengthRatio(0.5); 211 return shape.createSineStar(); 212 } 213 comb(Geometry g, int nArms)214 public static Geometry comb(Geometry g, int nArms) 215 { 216 Envelope env = FunctionsUtil.getEnvelopeOrDefault(g); 217 GeometryFactory geomFact = FunctionsUtil.getFactoryOrDefault(g); 218 219 int npts = 4 * (nArms - 1) + 2 + 2 + 1; 220 Coordinate[] pts = new Coordinate[npts]; 221 double armWidth = env.getWidth() / (2 * nArms - 1); 222 double armLen = env.getHeight() - armWidth; 223 224 double xBase = env.getMinX(); 225 double yBase = env.getMinY(); 226 227 int ipts = 0; 228 for (int i = 0; i < nArms; i++) { 229 double x1 = xBase + i * 2 * armWidth; 230 double y1 = yBase + armLen + armWidth; 231 pts[ipts++] = new Coordinate(x1, y1); 232 pts[ipts++] = new Coordinate(x1 + armWidth, y1); 233 if (i < nArms - 1) { 234 pts[ipts++] = new Coordinate(x1 + armWidth, yBase + armWidth); 235 pts[ipts++] = new Coordinate(x1 + 2 * armWidth, yBase + armWidth); 236 } 237 } 238 pts[ipts++] = new Coordinate(env.getMaxX(), yBase); 239 pts[ipts++] = new Coordinate(xBase, yBase); 240 pts[ipts++] = new Coordinate(pts[0]); 241 242 return geomFact.createPolygon(pts); 243 } 244 pointFieldCentroidStar(Geometry ptsGeom)245 public static Geometry pointFieldCentroidStar(Geometry ptsGeom) 246 { 247 Coordinate[] pts = ptsGeom.getCoordinates(); 248 Geometry centroid = ptsGeom.getCentroid(); 249 return pointFieldStar(ptsGeom, centroid); 250 } 251 pointFieldStar(Geometry ptsGeom, Geometry centrePt)252 public static Geometry pointFieldStar(Geometry ptsGeom, Geometry centrePt) 253 { 254 Coordinate[] pts = ptsGeom.getCoordinates(); 255 Coordinate centre = centrePt.getCoordinate(); 256 257 List<OrderedPoint> orderedPts = new ArrayList<OrderedPoint>(); 258 for (Coordinate p : pts) { 259 double ang = Angle.angle(centre, p); 260 orderedPts.add(new OrderedPoint(p, ang)); 261 } 262 Collections.sort(orderedPts); 263 int n = pts.length+1; 264 Coordinate[] ring = new Coordinate[n]; 265 int i = 0; 266 for (OrderedPoint op : orderedPts) { 267 ring[i++] = op.pt; 268 } 269 // close ring 270 ring[n-1] = ring[0].copy(); 271 return ptsGeom.getFactory().createPolygon(ring); 272 } 273 274 private static class OrderedPoint implements Comparable { 275 Coordinate pt; 276 double index; 277 OrderedPoint(Coordinate p, double index)278 public OrderedPoint(Coordinate p, double index) { 279 this.pt = p; 280 this.index = index; 281 } 282 283 @Override compareTo(Object o)284 public int compareTo(Object o) { 285 OrderedPoint other = (OrderedPoint) o; 286 return Double.compare(index, other.index); 287 } 288 } 289 290 @Metadata(description="Construct a spiral") spiral(Geometry geom, @Metadata(title=R) int nCycles, @Metadata(title=R) int quadrantSegs)291 public static Geometry spiral(Geometry geom, 292 @Metadata(title="Num Cycles") 293 int nCycles, 294 @Metadata(title="Quadrant Segs") 295 int quadrantSegs) { 296 Envelope env = FunctionsUtil.getEnvelopeOrDefault(geom); 297 GeometryFactory geomFact = FunctionsUtil.getFactoryOrDefault(geom); 298 299 double width = Math.min(env.getHeight(), env.getWidth())/2; 300 double pitch = width / nCycles; 301 302 Coordinate centre = env.centre(); 303 304 CoordinateList inside = new CoordinateList(); 305 CoordinateList outside = new CoordinateList(); 306 for (int i = 1; i <= nCycles; i++) { 307 Coordinate[] inCycle = genSpiralCycle(centre, i * pitch - pitch/2, (i+1) * pitch - pitch/2, quadrantSegs); 308 inside.add(inCycle, false); 309 Coordinate[] outCycle = genSpiralCycle(centre, i * pitch, (i+1) * pitch, quadrantSegs); 310 outside.add(outCycle, false); 311 } 312 CoordinateList all = new CoordinateList(); 313 all.add(inside.toCoordinateArray(), false); 314 Coordinate[] outsidePts = outside.toCoordinateArray(); 315 CoordinateArrays.reverse(outsidePts); 316 all.add(outsidePts, false); 317 all.closeRing(); 318 return geomFact.createPolygon(all.toCoordinateArray()); 319 } 320 genSpiralCycle(Coordinate centre, double radiusStart, double radiusEnd, int quadrantSegs)321 private static Coordinate[] genSpiralCycle(Coordinate centre, 322 double radiusStart, double radiusEnd, int quadrantSegs) { 323 int nPts = quadrantSegs * 4 + 1; 324 Coordinate[] pts = new Coordinate[nPts]; 325 double angInc = 2 * Math.PI / (nPts - 1); 326 double radiusInc = (radiusEnd - radiusStart) / (nPts - 1); 327 for (int i = 0; i < nPts; i++) { 328 double radius = radiusStart + i * radiusInc; 329 double x = radius * Math.cos(i *angInc); 330 double y = radius * Math.sin(i *angInc); 331 Coordinate pt = new Coordinate(centre.getX() + x, centre.getY() + y); 332 pts[i] = pt; 333 } 334 return pts; 335 } 336 } 337