/* * 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.awt.Color; import java.awt.Font; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.swing.Icon; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; 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.task.DummyTaskMonitor; import com.vividsolutions.jump.util.CollectionUtil; import com.vividsolutions.jump.warp.Triangle; import com.vividsolutions.jump.warp.Triangulator; import com.vividsolutions.jump.workbench.WorkbenchContext; import com.vividsolutions.jump.workbench.model.Layer; import com.vividsolutions.jump.workbench.model.LayerManagerProxy; import com.vividsolutions.jump.workbench.model.StandardCategoryNames; import com.vividsolutions.jump.workbench.model.UndoableCommand; import com.vividsolutions.jump.workbench.plugin.AbstractPlugIn; import com.vividsolutions.jump.workbench.plugin.EnableCheck; import com.vividsolutions.jump.workbench.plugin.EnableCheckFactory; import com.vividsolutions.jump.workbench.plugin.MultiEnableCheck; import com.vividsolutions.jump.workbench.plugin.PlugInContext; import com.vividsolutions.jump.workbench.ui.images.IconLoader; import com.vividsolutions.jump.workbench.ui.warp.WarpingPanel; import com.vividsolutions.jump.workbench.ui.warp.WarpingVectorLayerFinder; /** * See White, Marvin S., Jr. and Griffin, Patricia. 1985. Piecewise linear * rubber-sheet map transformation. "The American Cartographer" 12:2, * 123-31. */ public class ShowTriangulationPlugIn extends AbstractPlugIn { private final static Color GOLD = new Color(255, 192, 0, 150); private Triangulator triangulator = new Triangulator(); public ShowTriangulationPlugIn(WarpingPanel warpingPanel) { this.warpingPanel = warpingPanel; } private WarpingPanel warpingPanel; public void initialize(PlugInContext context) throws Exception {} public EnableCheck createEnableCheck(WorkbenchContext context) { EnableCheckFactory checkFactory = new EnableCheckFactory(context); return new MultiEnableCheck().add( checkFactory.createWindowWithLayerViewPanelMustBeActiveCheck()); } public final static String SOURCE_LAYER_NAME = I18N.get("ui.plugin.generate.ShowTriangulationPlugIn.initial-triangulation"); public final static String DESTINATION_LAYER_NAME = I18N.get("ui.plugin.generate.ShowTriangulationPlugIn.final-triangulation"); private Layer sourceLayer(LayerManagerProxy layerManagerProxy) { return layerManagerProxy.getLayerManager().getLayer(SOURCE_LAYER_NAME); } private Layer destinationLayer(LayerManagerProxy layerManagerProxy) { return layerManagerProxy.getLayerManager().getLayer(DESTINATION_LAYER_NAME); } private WarpingVectorLayerFinder warpingVectorLayerFinder(LayerManagerProxy proxy) { return new WarpingVectorLayerFinder(proxy); } private Envelope envelopeOfTails(Collection vectors) { Envelope envelope = new Envelope(); for (Iterator i = vectors.iterator(); i.hasNext();) { LineString vector = (LineString) i.next(); envelope.expandToInclude(vector.getCoordinateN(0)); } return envelope; } public boolean execute(final PlugInContext context) throws Exception { context.getLayerManager().getUndoableEditReceiver().reportNothingToUndoYet(); execute(createCommand(context.getWorkbenchContext(), true), context); return true; } private UndoableCommand createCommand( final WorkbenchContext context, final boolean createLayersIfNonExistent) { Envelope datasetEnvelope = new Envelope(); if (warpingPanel.currentSourceLayer() != null) { datasetEnvelope = warpingPanel.currentSourceLayer().getFeatureCollectionWrapper().getEnvelope(); } if (datasetEnvelope.isNull()) { datasetEnvelope = envelopeOfTails(warpingVectorLayerFinder(context).getVectors()); } if (datasetEnvelope.isNull()) { return UndoableCommand.DUMMY; } if (datasetEnvelope.getWidth() == 0) { //Otherwise we could end up with zero-area quadrilaterals [Jon Aquino] datasetEnvelope.expandToInclude( new Coordinate(datasetEnvelope.getMinX() + 1, datasetEnvelope.getMinY())); datasetEnvelope.expandToInclude( new Coordinate(datasetEnvelope.getMinX() - 1, datasetEnvelope.getMinY())); } if (datasetEnvelope.getHeight() == 0) { datasetEnvelope.expandToInclude( new Coordinate(datasetEnvelope.getMinX(), datasetEnvelope.getMinY() + 1)); datasetEnvelope.expandToInclude( new Coordinate(datasetEnvelope.getMinX(), datasetEnvelope.getMinY() - 1)); } Map triangleMap = triangulator.triangleMap( datasetEnvelope, warpingVectorLayerFinder(context).getVectors(), new DummyTaskMonitor()); List[] sourceAndDestinationTriangles = CollectionUtil.keysAndCorrespondingValues(triangleMap); final FeatureCollection sourceFeatureCollection = toFeatureCollection(sourceAndDestinationTriangles[0]); final FeatureCollection destinationFeatureCollection = toFeatureCollection(sourceAndDestinationTriangles[1]); return addUndo(new UndoableCommand(getName()) { public void execute() { if (sourceLayer(context) != null) { sourceLayer(context).setFeatureCollection(sourceFeatureCollection); sourceLayer(context).setVisible(true); } if (sourceLayer(context) == null && createLayersIfNonExistent) { Layer sourceLayer = context.getLayerManager().addLayer( StandardCategoryNames.WORKING, SOURCE_LAYER_NAME, sourceFeatureCollection); init(sourceLayer, Color.gray, 150, 1); } if (destinationLayer(context) != null) { destinationLayer(context).setFeatureCollection(destinationFeatureCollection); destinationLayer(context).setVisible(true); } if (destinationLayer(context) == null && createLayersIfNonExistent) { Layer destinationLayer = context.getLayerManager().addLayer( StandardCategoryNames.WORKING, DESTINATION_LAYER_NAME, destinationFeatureCollection); init(destinationLayer, GOLD, 255, 1); } } public void unexecute() { //Undo is handled by #addUndo. [Jon Aquino] } }, context); } public UndoableCommand addLayerGeneration( final UndoableCommand wrappeeCommand, final WorkbenchContext context, final boolean createLayersIfNonExistent) { return new UndoableCommand(wrappeeCommand.getName()) { private UndoableCommand layerGenerationCommand = null; private UndoableCommand layerGenerationCommand() { if (layerGenerationCommand == null) { layerGenerationCommand = createCommand(context, createLayersIfNonExistent); } return layerGenerationCommand; } public void execute() { wrappeeCommand.execute(); layerGenerationCommand().execute(); } public void unexecute() { layerGenerationCommand().unexecute(); wrappeeCommand.unexecute(); } }; } public static UndoableCommand addUndo( final UndoableCommand wrappeeCommand, final LayerManagerProxy proxy) { return Layer.addUndo( DESTINATION_LAYER_NAME, proxy, Layer.addUndo(SOURCE_LAYER_NAME, proxy, wrappeeCommand)); } private final static String WARP_ID_NAME = "WARP_ID"; private FeatureCollection toFeatureCollection(Collection triangles) { FeatureSchema featureSchema = new FeatureSchema(); featureSchema.addAttribute("GEOMETRY", AttributeType.GEOMETRY); featureSchema.addAttribute(WARP_ID_NAME, AttributeType.INTEGER); FeatureCollection featureCollection = new FeatureDataset(featureSchema); int j = 0; for (Iterator i = triangles.iterator(); i.hasNext();) { Triangle t = (Triangle) i.next(); j++; Feature feature = new BasicFeature(featureSchema); feature.setGeometry(factory.createPolygon(t.toLinearRing(), null)); feature.setAttribute(WARP_ID_NAME, new Integer(j)); featureCollection.add(feature); } return featureCollection; } private GeometryFactory factory = new GeometryFactory(); private void init(Layer layer, Color color, int alpha, int lineWidth) { boolean firingEvents = layer.getLayerManager().isFiringEvents(); layer.getLayerManager().setFiringEvents(false); try { layer.getBasicStyle().setLineColor(color); layer.getBasicStyle().setFillColor(color); layer.getBasicStyle().setAlpha(alpha); layer.getBasicStyle().setLineWidth(lineWidth); layer.getBasicStyle().setRenderingFill(false); layer.getVertexStyle().setEnabled(true); layer.getVertexStyle().setSize(4); layer.getLabelStyle().setEnabled(true); layer.getLabelStyle().setColor(color); layer.getLabelStyle().setFont(layer.getLabelStyle().getFont().deriveFont(Font.PLAIN, 12)); layer.getLabelStyle().setAttribute(WARP_ID_NAME); layer.getLabelStyle().setHeight(12); layer.getLabelStyle().setScaling(false); layer.getLabelStyle().setHidingOverlappingLabels(false); } finally { layer.getLayerManager().setFiringEvents(firingEvents); } layer.fireAppearanceChanged(); } public Icon getIcon() { return IconLoader.icon("Triangle.gif"); } }