/* * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI * for visualizing and manipulating spatial features with geometry and attributes. * * Copyright (C) 2003 Vivid Solutions * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; 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.vividsolutions.jump.workbench.ui.plugin.generate; import java.util.ArrayList; import java.util.List; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jump.I18N; import com.vividsolutions.jump.feature.AttributeType; import com.vividsolutions.jump.feature.BasicFeature; import com.vividsolutions.jump.feature.Feature; import com.vividsolutions.jump.feature.FeatureCollection; import com.vividsolutions.jump.feature.FeatureDataset; import com.vividsolutions.jump.feature.FeatureSchema; import com.vividsolutions.jump.workbench.model.StandardCategoryNames; import com.vividsolutions.jump.workbench.plugin.PlugInContext; public class BoundaryMatchDataEngine { private Coordinate southwestCornerOfLeftLayer = new Coordinate(0, 0); private int layerHeightInCells = 4; private int layerWidthInCells = 1; private double cellSideLength = 100; private int verticesPerCellSide = 4; private double boundaryAmplitude = 20; private double boundaryPeriod = 150; private int verticesPerBoundarySide = 6; private double maxBoundaryPerturbation = 1.0; private double perturbationProbability = 0.5; private GeometryFactory factory = new GeometryFactory(); public BoundaryMatchDataEngine() { } public void setSouthwestCornerOfLeftLayer( Coordinate newSouthwestCornerOfLeftLayer) { southwestCornerOfLeftLayer = newSouthwestCornerOfLeftLayer; } public void setLayerHeightInCells(int newLayerHeightInCells) { layerHeightInCells = newLayerHeightInCells; } public void setLayerWidthInCells(int newLayerWidthInCells) { layerWidthInCells = newLayerWidthInCells; } public void setCellSideLength(double newCellSideLength) { cellSideLength = newCellSideLength; } public void setVerticesPerCellSide(int newVerticesPerCellSide) { verticesPerCellSide = newVerticesPerCellSide; } public void setBoundaryAmplitude(double newBoundaryAmplitude) { boundaryAmplitude = newBoundaryAmplitude; } public void setBoundaryPeriod(double newBoundaryPeriod) { boundaryPeriod = newBoundaryPeriod; } public void setVerticesPerBoundarySide(int newVerticesPerBoundarySide) { verticesPerBoundarySide = newVerticesPerBoundarySide; } public void setMaxBoundaryPerturbation(double newMaxBoundaryPerturbation) { maxBoundaryPerturbation = newMaxBoundaryPerturbation; } public void setPerturbationProbability(double newPerturbationProbability) { perturbationProbability = newPerturbationProbability; } public Coordinate getSouthwestCornerOfLeftLayer() { return southwestCornerOfLeftLayer; } public int getLayerHeightInCells() { return layerHeightInCells; } public int getLayerWidthInCells() { return layerWidthInCells; } public double getCellSideLength() { return cellSideLength; } public int getVerticesPerCellSide() { return verticesPerCellSide; } public double getBoundaryAmplitude() { return boundaryAmplitude; } public double getBoundaryPeriod() { return boundaryPeriod; } public int getVerticesPerBoundarySide() { return verticesPerBoundarySide; } public double getMaxBoundaryPerturbation() { return maxBoundaryPerturbation; } public double getPerturbationProbability() { return perturbationProbability; } public void execute(PlugInContext context) { FeatureSchema featureSchema = new FeatureSchema(); featureSchema.addAttribute("GEOMETRY", AttributeType.GEOMETRY); FeatureCollection leftFeatureCollection = new FeatureDataset(featureSchema); FeatureCollection rightFeatureCollection = new FeatureDataset(featureSchema); addLeftSquareCells(leftFeatureCollection); addRightSquareCells(rightFeatureCollection); addBoundaryCells(leftFeatureCollection, rightFeatureCollection); context.addLayer(StandardCategoryNames.WORKING, I18N.get("ui.plugin.generate.BoundaryMatchDataEngine.left"), leftFeatureCollection); context.addLayer(StandardCategoryNames.WORKING, I18N.get("ui.plugin.generate.BoundaryMatchDataEngine.right"), rightFeatureCollection); } private double segmentLength() { return cellSideLength / (verticesPerCellSide - 1); } private void addBoundaryCells(FeatureCollection leftFeatureCollection, FeatureCollection rightFeatureCollection) { Coordinate southwestCornerOfBoundary = new Coordinate(southwestCornerOfLeftLayer.x + (layerWidthInCells * cellSideLength), southwestCornerOfLeftLayer.y); Coordinate topLeftBoundaryCoordinate = null; Coordinate topRightBoundaryCoordinate = null; double boundaryX = southwestCornerOfBoundary.x + (cellSideLength / 2); for (int j = 0; j < layerHeightInCells; j++) { topLeftBoundaryCoordinate = addBoundaryCell(leftFeatureCollection, boundaryX, southwestCornerOfBoundary.x, southwestCornerOfBoundary.y + (j * cellSideLength), topLeftBoundaryCoordinate); topRightBoundaryCoordinate = addBoundaryCell(rightFeatureCollection, boundaryX, southwestCornerOfBoundary.x + cellSideLength, southwestCornerOfBoundary.y + (j * cellSideLength), topRightBoundaryCoordinate); } } /** *@return the northernmost boundary coordinate */ private Coordinate addBoundaryCell(FeatureCollection featureCollection, double boundaryX, double flatX, double south, Coordinate prevCellsTopBoundaryCoordinate) { List boundaryCoordinates = boundaryCoordinates(boundaryX, south, prevCellsTopBoundaryCoordinate); add(boundaryCell(flatX, south, boundaryCoordinates), featureCollection); return (Coordinate) boundaryCoordinates.get(0); } /** *@param x position of the flat wall (as opposed to the sinusoidal wall) */ private Polygon boundaryCell(double x, double south, List boundaryCoordinates) { ArrayList coordinates = new ArrayList(); for (int i = 0; i < verticesPerCellSide; i++) { coordinates.add(round(new Coordinate(x, south + (i * segmentLength())))); } coordinates.addAll(boundaryCoordinates); coordinates.add(coordinates.get(0)); return polygon(coordinates); } /** *@param west x coordinate of the boundary cell *@param south y coordinate of the boundary cell *@return coordinates of the boundary for the given cell, from the * north vertex to the south vertex */ private List boundaryCoordinates(double boundaryX, double south, Coordinate prevCellsTopBoundaryCoordinate) { ArrayList boundaryCoordinates = new ArrayList(); double segmentLength = cellSideLength / (verticesPerBoundarySide - 1); for (int i = verticesPerBoundarySide - 1; i >= 0; i--) { if ((i == 0) && (prevCellsTopBoundaryCoordinate != null)) { //Ensure continuity of the boundary despite perturbations [Jon Aquino] boundaryCoordinates.add(prevCellsTopBoundaryCoordinate); continue; } double y = south + (i * segmentLength); double x = boundaryX + (boundaryAmplitude * Math.sin((2 * Math.PI * y) / boundaryPeriod)); //To ensure perturbations are negative half the time, multiply by two //then subtract the max perturbation. [Jon Aquino] if (Math.random() < perturbationProbability) { x += ((2 * Math.random() * maxBoundaryPerturbation) - maxBoundaryPerturbation); y += ((2 * Math.random() * maxBoundaryPerturbation) - maxBoundaryPerturbation); } // round the coordinates to be integers [MD] boundaryCoordinates.add(round(new Coordinate(x, y))); } return boundaryCoordinates; } private void addLeftSquareCells(FeatureCollection leftFeatureCollection) { addSquareCells(leftFeatureCollection, southwestCornerOfLeftLayer); } private void addRightSquareCells(FeatureCollection rightFeatureCollection) { Coordinate southwestCornerOfRightLayer = new Coordinate( //Divide by 2 because layer width is half of layer height //Add 1 for boundary column southwestCornerOfLeftLayer.x + ((layerWidthInCells + 1) * cellSideLength), southwestCornerOfLeftLayer.y); addSquareCells(rightFeatureCollection, southwestCornerOfRightLayer); } private void addSquareCells(FeatureCollection featureCollection, Coordinate southwestCornerOfLayer) { for (int i = 0; i < layerWidthInCells; i++) { for (int j = 0; j < layerHeightInCells; j++) { add(squareCell(i, j, southwestCornerOfLayer), featureCollection); } } } private void add(Polygon polygon, FeatureCollection featureCollection) { Feature feature = new BasicFeature(featureCollection.getFeatureSchema()); feature.setGeometry(polygon); featureCollection.add(feature); } private Polygon squareCell(int i, int j, Coordinate southwestCornerOfLayer) { return squareCell(southwestCornerOfLayer.x + (i * cellSideLength), southwestCornerOfLayer.y + (j * cellSideLength)); } private Polygon squareCell(double west, double south) { ArrayList coordinates = new ArrayList(); for (int i = 0; i < (verticesPerCellSide - 1); i++) { coordinates.add(round(new Coordinate(west, south + (i * segmentLength())))); } for (int i = 0; i < (verticesPerCellSide - 1); i++) { coordinates.add(round(new Coordinate(west + (i * segmentLength()), south + cellSideLength))); } for (int i = verticesPerCellSide - 1; i > 0; i--) { coordinates.add(round(new Coordinate(west + cellSideLength, south + (i * segmentLength())))); } for (int i = verticesPerCellSide - 1; i > 0; i--) { coordinates.add(round(new Coordinate(west + (i * segmentLength()), south))); } coordinates.add(coordinates.get(0)); return polygon(coordinates); } private Polygon polygon(List coordinates) { Coordinate[] coordinateArray = (Coordinate[]) coordinates.toArray(new Coordinate[] { }); return factory.createPolygon(factory.createLinearRing(coordinateArray), null); } private Coordinate round(Coordinate coord) { coord.x = Math.floor(coord.x); coord.y = Math.floor(coord.y); return coord; } }