001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
003: * for visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032:
033: package com.vividsolutions.jump.workbench.ui.plugin.generate;
034:
035: import java.util.ArrayList;
036: import java.util.List;
037:
038: import com.vividsolutions.jts.geom.Coordinate;
039: import com.vividsolutions.jts.geom.GeometryFactory;
040: import com.vividsolutions.jts.geom.Polygon;
041: import com.vividsolutions.jump.I18N;
042: import com.vividsolutions.jump.feature.AttributeType;
043: import com.vividsolutions.jump.feature.BasicFeature;
044: import com.vividsolutions.jump.feature.Feature;
045: import com.vividsolutions.jump.feature.FeatureCollection;
046: import com.vividsolutions.jump.feature.FeatureDataset;
047: import com.vividsolutions.jump.feature.FeatureSchema;
048: import com.vividsolutions.jump.workbench.model.StandardCategoryNames;
049: import com.vividsolutions.jump.workbench.plugin.PlugInContext;
050:
051: public class BoundaryMatchDataEngine {
052: private Coordinate southwestCornerOfLeftLayer = new Coordinate(0, 0);
053: private int layerHeightInCells = 4;
054: private int layerWidthInCells = 1;
055: private double cellSideLength = 100;
056: private int verticesPerCellSide = 4;
057: private double boundaryAmplitude = 20;
058: private double boundaryPeriod = 150;
059: private int verticesPerBoundarySide = 6;
060: private double maxBoundaryPerturbation = 1.0;
061: private double perturbationProbability = 0.5;
062: private GeometryFactory factory = new GeometryFactory();
063:
064: public BoundaryMatchDataEngine() {
065: }
066:
067: public void setSouthwestCornerOfLeftLayer(
068: Coordinate newSouthwestCornerOfLeftLayer) {
069: southwestCornerOfLeftLayer = newSouthwestCornerOfLeftLayer;
070: }
071:
072: public void setLayerHeightInCells(int newLayerHeightInCells) {
073: layerHeightInCells = newLayerHeightInCells;
074: }
075:
076: public void setLayerWidthInCells(int newLayerWidthInCells) {
077: layerWidthInCells = newLayerWidthInCells;
078: }
079:
080: public void setCellSideLength(double newCellSideLength) {
081: cellSideLength = newCellSideLength;
082: }
083:
084: public void setVerticesPerCellSide(int newVerticesPerCellSide) {
085: verticesPerCellSide = newVerticesPerCellSide;
086: }
087:
088: public void setBoundaryAmplitude(double newBoundaryAmplitude) {
089: boundaryAmplitude = newBoundaryAmplitude;
090: }
091:
092: public void setBoundaryPeriod(double newBoundaryPeriod) {
093: boundaryPeriod = newBoundaryPeriod;
094: }
095:
096: public void setVerticesPerBoundarySide(
097: int newVerticesPerBoundarySide) {
098: verticesPerBoundarySide = newVerticesPerBoundarySide;
099: }
100:
101: public void setMaxBoundaryPerturbation(
102: double newMaxBoundaryPerturbation) {
103: maxBoundaryPerturbation = newMaxBoundaryPerturbation;
104: }
105:
106: public void setPerturbationProbability(
107: double newPerturbationProbability) {
108: perturbationProbability = newPerturbationProbability;
109: }
110:
111: public Coordinate getSouthwestCornerOfLeftLayer() {
112: return southwestCornerOfLeftLayer;
113: }
114:
115: public int getLayerHeightInCells() {
116: return layerHeightInCells;
117: }
118:
119: public int getLayerWidthInCells() {
120: return layerWidthInCells;
121: }
122:
123: public double getCellSideLength() {
124: return cellSideLength;
125: }
126:
127: public int getVerticesPerCellSide() {
128: return verticesPerCellSide;
129: }
130:
131: public double getBoundaryAmplitude() {
132: return boundaryAmplitude;
133: }
134:
135: public double getBoundaryPeriod() {
136: return boundaryPeriod;
137: }
138:
139: public int getVerticesPerBoundarySide() {
140: return verticesPerBoundarySide;
141: }
142:
143: public double getMaxBoundaryPerturbation() {
144: return maxBoundaryPerturbation;
145: }
146:
147: public double getPerturbationProbability() {
148: return perturbationProbability;
149: }
150:
151: public void execute(PlugInContext context) {
152: FeatureSchema featureSchema = new FeatureSchema();
153: featureSchema.addAttribute("GEOMETRY", AttributeType.GEOMETRY);
154:
155: FeatureCollection leftFeatureCollection = new FeatureDataset(
156: featureSchema);
157: FeatureCollection rightFeatureCollection = new FeatureDataset(
158: featureSchema);
159: addLeftSquareCells(leftFeatureCollection);
160: addRightSquareCells(rightFeatureCollection);
161: addBoundaryCells(leftFeatureCollection, rightFeatureCollection);
162: context
163: .addLayer(
164: StandardCategoryNames.WORKING,
165: I18N
166: .get("ui.plugin.generate.BoundaryMatchDataEngine.left"),
167: leftFeatureCollection);
168: context
169: .addLayer(
170: StandardCategoryNames.WORKING,
171: I18N
172: .get("ui.plugin.generate.BoundaryMatchDataEngine.right"),
173: rightFeatureCollection);
174: }
175:
176: private double segmentLength() {
177: return cellSideLength / (verticesPerCellSide - 1);
178: }
179:
180: private void addBoundaryCells(
181: FeatureCollection leftFeatureCollection,
182: FeatureCollection rightFeatureCollection) {
183: Coordinate southwestCornerOfBoundary = new Coordinate(
184: southwestCornerOfLeftLayer.x
185: + (layerWidthInCells * cellSideLength),
186: southwestCornerOfLeftLayer.y);
187: Coordinate topLeftBoundaryCoordinate = null;
188: Coordinate topRightBoundaryCoordinate = null;
189: double boundaryX = southwestCornerOfBoundary.x
190: + (cellSideLength / 2);
191:
192: for (int j = 0; j < layerHeightInCells; j++) {
193: topLeftBoundaryCoordinate = addBoundaryCell(
194: leftFeatureCollection, boundaryX,
195: southwestCornerOfBoundary.x,
196: southwestCornerOfBoundary.y + (j * cellSideLength),
197: topLeftBoundaryCoordinate);
198: topRightBoundaryCoordinate = addBoundaryCell(
199: rightFeatureCollection, boundaryX,
200: southwestCornerOfBoundary.x + cellSideLength,
201: southwestCornerOfBoundary.y + (j * cellSideLength),
202: topRightBoundaryCoordinate);
203: }
204: }
205:
206: /**
207: *@return the northernmost boundary coordinate
208: */
209: private Coordinate addBoundaryCell(
210: FeatureCollection featureCollection, double boundaryX,
211: double flatX, double south,
212: Coordinate prevCellsTopBoundaryCoordinate) {
213: List boundaryCoordinates = boundaryCoordinates(boundaryX,
214: south, prevCellsTopBoundaryCoordinate);
215: add(boundaryCell(flatX, south, boundaryCoordinates),
216: featureCollection);
217:
218: return (Coordinate) boundaryCoordinates.get(0);
219: }
220:
221: /**
222: *@param x position of the flat wall (as opposed to the sinusoidal wall)
223: */
224: private Polygon boundaryCell(double x, double south,
225: List boundaryCoordinates) {
226: ArrayList coordinates = new ArrayList();
227:
228: for (int i = 0; i < verticesPerCellSide; i++) {
229: coordinates.add(round(new Coordinate(x, south
230: + (i * segmentLength()))));
231: }
232:
233: coordinates.addAll(boundaryCoordinates);
234: coordinates.add(coordinates.get(0));
235:
236: return polygon(coordinates);
237: }
238:
239: /**
240: *@param west x coordinate of the boundary cell
241: *@param south y coordinate of the boundary cell
242: *@return coordinates of the boundary for the given cell, from the
243: * north vertex to the south vertex
244: */
245: private List boundaryCoordinates(double boundaryX, double south,
246: Coordinate prevCellsTopBoundaryCoordinate) {
247: ArrayList boundaryCoordinates = new ArrayList();
248: double segmentLength = cellSideLength
249: / (verticesPerBoundarySide - 1);
250:
251: for (int i = verticesPerBoundarySide - 1; i >= 0; i--) {
252: if ((i == 0) && (prevCellsTopBoundaryCoordinate != null)) {
253: //Ensure continuity of the boundary despite perturbations [Jon Aquino]
254: boundaryCoordinates.add(prevCellsTopBoundaryCoordinate);
255:
256: continue;
257: }
258:
259: double y = south + (i * segmentLength);
260: double x = boundaryX
261: + (boundaryAmplitude * Math.sin((2 * Math.PI * y)
262: / boundaryPeriod));
263:
264: //To ensure perturbations are negative half the time, multiply by two
265: //then subtract the max perturbation. [Jon Aquino]
266: if (Math.random() < perturbationProbability) {
267: x += ((2 * Math.random() * maxBoundaryPerturbation) - maxBoundaryPerturbation);
268: y += ((2 * Math.random() * maxBoundaryPerturbation) - maxBoundaryPerturbation);
269: }
270:
271: // round the coordinates to be integers [MD]
272: boundaryCoordinates.add(round(new Coordinate(x, y)));
273: }
274:
275: return boundaryCoordinates;
276: }
277:
278: private void addLeftSquareCells(
279: FeatureCollection leftFeatureCollection) {
280: addSquareCells(leftFeatureCollection,
281: southwestCornerOfLeftLayer);
282: }
283:
284: private void addRightSquareCells(
285: FeatureCollection rightFeatureCollection) {
286: Coordinate southwestCornerOfRightLayer = new Coordinate(
287: //Divide by 2 because layer width is half of layer height
288: //Add 1 for boundary column
289: southwestCornerOfLeftLayer.x
290: + ((layerWidthInCells + 1) * cellSideLength),
291: southwestCornerOfLeftLayer.y);
292: addSquareCells(rightFeatureCollection,
293: southwestCornerOfRightLayer);
294: }
295:
296: private void addSquareCells(FeatureCollection featureCollection,
297: Coordinate southwestCornerOfLayer) {
298: for (int i = 0; i < layerWidthInCells; i++) {
299: for (int j = 0; j < layerHeightInCells; j++) {
300: add(squareCell(i, j, southwestCornerOfLayer),
301: featureCollection);
302: }
303: }
304: }
305:
306: private void add(Polygon polygon,
307: FeatureCollection featureCollection) {
308: Feature feature = new BasicFeature(featureCollection
309: .getFeatureSchema());
310: feature.setGeometry(polygon);
311: featureCollection.add(feature);
312: }
313:
314: private Polygon squareCell(int i, int j,
315: Coordinate southwestCornerOfLayer) {
316: return squareCell(southwestCornerOfLayer.x
317: + (i * cellSideLength), southwestCornerOfLayer.y
318: + (j * cellSideLength));
319: }
320:
321: private Polygon squareCell(double west, double south) {
322: ArrayList coordinates = new ArrayList();
323:
324: for (int i = 0; i < (verticesPerCellSide - 1); i++) {
325: coordinates.add(round(new Coordinate(west, south
326: + (i * segmentLength()))));
327: }
328:
329: for (int i = 0; i < (verticesPerCellSide - 1); i++) {
330: coordinates.add(round(new Coordinate(west
331: + (i * segmentLength()), south + cellSideLength)));
332: }
333:
334: for (int i = verticesPerCellSide - 1; i > 0; i--) {
335: coordinates.add(round(new Coordinate(west + cellSideLength,
336: south + (i * segmentLength()))));
337: }
338:
339: for (int i = verticesPerCellSide - 1; i > 0; i--) {
340: coordinates.add(round(new Coordinate(west
341: + (i * segmentLength()), south)));
342: }
343:
344: coordinates.add(coordinates.get(0));
345:
346: return polygon(coordinates);
347: }
348:
349: private Polygon polygon(List coordinates) {
350: Coordinate[] coordinateArray = (Coordinate[]) coordinates
351: .toArray(new Coordinate[] {
352:
353: });
354:
355: return factory.createPolygon(factory
356: .createLinearRing(coordinateArray), null);
357: }
358:
359: private Coordinate round(Coordinate coord) {
360: coord.x = Math.floor(coord.x);
361: coord.y = Math.floor(coord.y);
362:
363: return coord;
364: }
365: }
|