/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* This file contains original code and/or modifications of original code.
* Any modifications made by VoltDB Inc. are licensed under the following
* terms and conditions:
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.voltdb.utils;
import java.util.ArrayList;
import java.util.List;
import org.voltdb.types.GeographyPointValue;
import org.voltdb.types.GeographyValue;
import junit.framework.TestCase;
public class TestPolygonFactory extends TestCase {
static final GeographyPointValue origin = new GeographyPointValue(0.0, 0.0);
static final GeographyPointValue x = new GeographyPointValue(1.0, 0.0);
static final GeographyPointValue y = new GeographyPointValue(0.0, 1.0);
static final double CENTER_SHRINK = 0.3;
public void testRegularConvexPolygon() throws Exception {
// Test a triangle.
GeographyValue pt3 = PolygonFactory.CreateRegularConvex(origin, y.mul(20.0), 3, 0);
String triangle = "POLYGON ((0.0 20.0, -17.320508075689 -10.0, 17.320508075689 -10.0, 0.0 20.0))";
assertEquals(triangle, pt3.toString());
// Test a square.
GeographyValue pt4 = PolygonFactory.CreateRegularConvex(origin, y.mul(20).add(x.mul(20)), 4, 0);
String square = "POLYGON ((20.0 20.0, -20.0 20.0, -20.0 -20.0, 20.0 -20.0, 20.0 20.0))";
assertEquals(square, pt4.toString());
GeographyPointValue offset = x.mul(20).add(y.mul(20));
GeographyValue pt4plus = pt4.add(offset);
String squareOff = "POLYGON ((40.0 40.0, 0.0 40.0, 0.0 0.0, 40.0 0.0, 40.0 40.0))";
assertEquals(squareOff, pt4plus.toString());
// For n = 3 to 10, generate a regular polygon with n points
// centered at the origin starting at the given start vertex.
GeographyPointValue startVertex = x.add(y);
for (int npts = 3; npts < 10; npts += 1) {
GeographyValue regularConvex = PolygonFactory.CreateRegularConvex(origin, startVertex, npts, 0.0);
List<List<GeographyPointValue>> loops = regularConvex.getRings();
assertEquals(1, loops.size());
List<GeographyPointValue> loop = loops.get(0);
assertEquals(npts + 1, loop.size());
regularConvex = PolygonFactory.CreateRegularConvex(origin, startVertex, npts, 0.5);
loops = regularConvex.getRings();
assertEquals(2, loops.size());
assertEquals(npts + 1, loops.get(0).size());
assertEquals(npts + 1, loops.get(1).size());
}
}
public void testStarPolygon() throws Exception {
for (int idx = 3; idx < 10; idx += 1) {
GeographyValue star = PolygonFactory.CreateStar(origin, y.mul(20.0), idx, 0.5, 0.0);
List<List<GeographyPointValue>> loops = star.getRings();
assertEquals(1, loops.size());
List<GeographyPointValue> shell = loops.get(0);
assertEquals(2*idx+1, shell.size());
star = PolygonFactory.CreateStar(origin, y.mul(20).add(x.mul(20)), idx, 0.5, 0.1);
loops = star.getRings();
assertEquals(2, loops.size());
shell = loops.get(0);
List<GeographyPointValue> hole = loops.get(1);
assertEquals(2*idx+1, shell.size());
assertEquals(2*idx+1, hole.size());
}
}
/**
* Create many regular convex polygons. In returnValue.get(n).get(k) we put an
* (n+3)-sided polygon with the given center and start vertex, with hole size
* equal to k/numHoleSizes. If k == 0 there is no hole. Note that k ranges between
* 0 and 4, so k/NumHoleSizes ranges between 0 and (1-1/numHoleSizes). If k == 0
* there is no hole.
*
* @return
*/
private static List<List<GeographyValue>> makeRegularConvexPolygons(GeographyPointValue firstCenter,
GeographyPointValue firstFirstVertex,
int minNumberVertices,
int maxNumberVertices,
int numHoleSizes,
double xmul,
double ymul) {
List<List<GeographyValue>> answer = new ArrayList<List<GeographyValue>>();
for (int numVertices = minNumberVertices; numVertices <= maxNumberVertices; numVertices += 1) {
int idx = numVertices - minNumberVertices;
List<GeographyValue> oneSize = new ArrayList<GeographyValue>();
// The x coordinate is humHoleSizes*idx.
GeographyPointValue sCenter = firstCenter.add(x.mul(numHoleSizes*idx));
for (int hidx = 0; hidx < numHoleSizes; hidx += 1) {
// The y coordinate is ymul * hidx.
GeographyPointValue offset = sCenter.add(y.mul(ymul*hidx));
GeographyPointValue center = firstCenter.add(offset);
GeographyPointValue firstVertex = firstFirstVertex.add(offset);
oneSize.add(PolygonFactory.CreateRegularConvex(center, firstVertex, numVertices, (hidx+0.0)/numHoleSizes));
}
answer.add(oneSize);
}
return answer;
}
/**
* Create many star-like polygons. In returnValue.get(n).get(s).get(k) we put an
* n-pointed polygon with the given center and start vertex. The inner radius is
* (numIrLevels-s+1)/numIrLevels. The hole size is k/numHoleSizeLevels.
* If k == 0, there is no hole.
*
* Note that n ranges between minNumPoints and maxNumPoints inclusive,
* s between 0 numIrLevels-1, k between 0 and numHoleSizeLevels-1. Since
* the hole size and inner radius must both be less than 1, and the inner radius
* must be greater than zero. The hole size can be zero.
*
* @return A 3-dimensional list list of polygons.
*/
private static List<List<List<GeographyValue>>> makeStarPolygons(GeographyPointValue firstCenter,
GeographyPointValue firstFirstVertex,
int minNumPoints,
int maxNumPoints,
int numIRLevels,
int numHoleSizeLevels,
double xmul,
double ymul) {
List<List<List<GeographyValue>>> answer = new ArrayList<List<List<GeographyValue>>>();
for (int numSides = minNumPoints; numSides <= maxNumPoints; numSides += 1) {
int idx = numSides - minNumPoints;
// The x coordinate is xmul * idx
GeographyPointValue column = x.mul(xmul*idx);
List<List<GeographyValue>> oneSize = new ArrayList<List<GeographyValue>>();
for (int ratioLevel = 0; ratioLevel < numIRLevels; ratioLevel += 1) {
GeographyPointValue irCenter = column.add(y.mul(numHoleSizeLevels*ymul*ratioLevel));
List<GeographyValue> oneRadius = new ArrayList<GeographyValue>();
for (int holeNumber = 0; holeNumber < numHoleSizeLevels; holeNumber += 1) {
GeographyPointValue offset = irCenter.add(y.mul(ymul*holeNumber));
GeographyPointValue center = firstCenter.add(offset);
GeographyPointValue firstVertex = firstFirstVertex.add(offset);
oneRadius.add(PolygonFactory.CreateStar(center, firstVertex, numSides, (ratioLevel + 1.0)/numIRLevels, (holeNumber+0.0)/numHoleSizeLevels));
}
oneSize.add(oneRadius);
}
answer.add(oneSize);
}
return answer;
}
public static String formatWKT(String wkt) {
return wkt.replaceAll("([0-9]), ", "$1,\n ").replaceAll("[)], [(]", "),\n (");
}
private static int getIntArg(String args[], int arg, String msg) {
int ans = -1;
if (args.length <= arg) {
System.err.printf("%s\n", msg);
System.exit(100);
} else {
try {
ans = Integer.parseInt(args[arg]);
} catch (IllegalArgumentException ex) {
System.err.printf("%s\n", msg);
System.exit(100);
}
}
return ans;
}
private static double getDoubleArg(String args[], int arg, String msg) {
double ans = -1;
if (args.length <= arg) {
System.err.printf("%s\n", msg);
System.exit(100);
} else {
try {
ans = Double.parseDouble(args[arg]);
} catch (IllegalArgumentException ex) {
System.err.printf("%s\n", msg);
System.exit(100);
}
}
return ans;
}
/**
* This main routine is useful for manual testing. The idea is that one
* runs this routine and WKT polygons are printed. These can be displayed
* with qgis or some other display tool.
*
* @param arg
*/
public static void main(String args[]) {
boolean doStars = false;
boolean doRegs = false;
int minVerts = 3;
int maxVerts = 12;
int numIRs = 5;
int numHoles = 5;
double xmul = 3.0;
double ymul = 3.0;
for (int arg = 0; arg < args.length; arg += 1) {
if (args[arg].equals("--stars")) {
doStars = true;
} else if (args[arg].equals("--reg")) {
doRegs = true;
} else if (args[arg].equals("--minVerts")) {
minVerts = getIntArg(args, ++arg, "--minVerts expects one integer parameters");
} else if (args[arg].equals("--maxVerts")) {
maxVerts = getIntArg(args, ++arg, "--maxVerts expects one integer parameters");
} else if (args[arg].equals("--numHoles")) {
numHoles = getIntArg(args, ++arg, "--numHoles expects one integer parameter");
} else if (args[arg].equals("--numIRs")) {
numIRs = getIntArg(args, ++arg, "--numIRs expects one integer parameter");
} else if (args[arg].equals("--xmul")) {
xmul = getDoubleArg(args, ++arg, "--xmul expects one double parameter");
} else if (args[arg].equals("--ymul")) {
ymul = getDoubleArg(args, ++arg, "--ymul expects one double parameter");
} else {
System.err.printf("Unknown command line parameter \"%s\"\n", args[arg]);
System.exit(100);
}
}
GeographyPointValue center = origin.add(x.mul(10).add(y.mul(10)));
GeographyPointValue firstVertex = center.add(x.mul(CENTER_SHRINK*xmul).add(y.mul(CENTER_SHRINK*ymul)));
if (doRegs) {
List<List<GeographyValue>> polys = makeRegularConvexPolygons(center, firstVertex, minVerts, maxVerts, numHoles, xmul, ymul);
for (int nsides = 0; nsides < polys.size(); nsides += 1) {
for (int holeSize = 0; holeSize < 5; holeSize += 1) {
System.out.printf("%s\n",
formatWKT(polys.get(nsides).get(holeSize).toString()));
}
}
}
if (doStars) {
GeographyPointValue scenter = center;
GeographyPointValue sfirstVertex = firstVertex;
List<List<List<GeographyValue>>> stars = makeStarPolygons(scenter, sfirstVertex, minVerts, maxVerts, numIRs, numHoles, xmul, ymul);
for (int nsides = 0; nsides < stars.size(); nsides += 1) {
List<List<GeographyValue>> oneSize = stars.get(nsides);
for (int innerRadiusIdx = 0; innerRadiusIdx < oneSize.size(); innerRadiusIdx += 1) {
List<GeographyValue> oneInnerRadius = oneSize.get(innerRadiusIdx);
for (int holeSizeIdx = 0; holeSizeIdx < oneInnerRadius.size(); holeSizeIdx += 1) {
GeographyValue oneStar = oneInnerRadius.get(holeSizeIdx);
System.out.printf("%s\n", formatWKT(oneStar.toString()));
}
}
}
}
}
}