/*
* 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.plugin.edit;
import java.awt.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.event.*;
import com.vividsolutions.jump.I18N;
import com.vividsolutions.jump.geom.precision.*;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jump.feature.*;
import com.vividsolutions.jump.task.*;
import com.vividsolutions.jump.util.*;
import com.vividsolutions.jump.workbench.*;
import com.vividsolutions.jump.workbench.model.*;
import com.vividsolutions.jump.workbench.plugin.*;
import com.vividsolutions.jump.workbench.ui.*;
import com.vividsolutions.jump.workbench.ui.plugin.FeatureInstaller;
public class PrecisionReducerPlugIn
extends ThreadedBasePlugIn
{
private static final double EXAMPLE_VALUE = 1234567.123123123123;
private final static String LAYER = I18N.get("ui.plugin.edit.PrecisionReducerPlugIn.Layer");
private final static String DECIMAL_PLACES = I18N.get("ui.plugin.edit.PrecisionReducerPlugIn.Decimal-Places");
private final static String SCALE_FACTOR = I18N.get("ui.plugin.edit.PrecisionReducerPlugIn.Scale-Factor");
private MultiInputDialog dialog;
private JTextField decimalPlacesField;
private JTextField scaleFactorField;
private JLabel exampleLabel;
private String layerName;
private int decimalPlaces = 0;
private int scaleFactor = 1;
public PrecisionReducerPlugIn() { }
/**
* Returns a very brief description of this task.
* @return the name of this task
*/
public String getName() { return I18N.get("ui.plugin.edit.PrecisionReducerPlugIn.Precision-Reducer"); }
public void initialize(PlugInContext context) throws Exception
{
FeatureInstaller featureInstaller = new FeatureInstaller(context.getWorkbenchContext());
featureInstaller.addMainMenuItem(
this, //exe
new String[] {MenuNames.TOOLS, MenuNames.TOOLS_EDIT_GEOMETRY}, //menu path
this.getName() + "...", //name methode .getName recieved by AbstractPlugIn
false, //checkbox
null, //icon
createEnableCheck(context.getWorkbenchContext())); //enable check
}
public EnableCheck createEnableCheck(WorkbenchContext workbenchContext) {
EnableCheckFactory checkFactory = new EnableCheckFactory(workbenchContext);
return new MultiEnableCheck()
.add(checkFactory.createWindowWithLayerManagerMustBeActiveCheck())
.add(checkFactory.createAtLeastNLayersMustExistCheck(1))
.add(checkFactory.createAtLeastNLayersMustBeEditableCheck(1));
}
public boolean execute(PlugInContext context) throws Exception {
dialog = new MultiInputDialog(
context.getWorkbenchFrame(), getName(), true);
setDialogValues(dialog, context);
GUIUtil.centreOnWindow(dialog);
dialog.setVisible(true);
if (!dialog.wasOKPressed()) { return false; }
getDialogValues(dialog);
return true;
}
public void run(TaskMonitor monitor, PlugInContext context)
throws Exception
{
monitor.allowCancellationRequests();
monitor.report(I18N.get("ui.plugin.edit.PrecisionReducerPlugIn.Reducing-Precision") + "...");
Layer layer = dialog.getLayer(LAYER);
FeatureCollection fc = layer.getFeatureCollectionWrapper();
List[] bad = reducePrecision(fc, monitor);
layer.fireAppearanceChanged();
if (monitor.isCancelRequested()) return;
if (bad[0].size() > 0) {
Layer lyr = context.getLayerManager().addLayer(StandardCategoryNames.QA,
I18N.get("ui.plugin.edit.PrecisionReducerPlugIn.Invalid-Input-Geometries"), FeatureDatasetFactory.createFromGeometry(bad[0]));
LayerStyleUtil.setLinearStyle(lyr, Color.red, 2, 0);
lyr.fireAppearanceChanged();
Layer lyr2 = context.getLayerManager().addLayer(StandardCategoryNames.QA,
I18N.get("ui.plugin.edit.PrecisionReducerPlugIn.Invalid-Reduced-Geometries"), FeatureDatasetFactory.createFromGeometry(bad[1]));
lyr2.getBasicStyle().setFillColor( ColorUtil.GOLD);
lyr2.getBasicStyle().setLineColor( Layer.defaultLineColor(ColorUtil.GOLD));
lyr2.fireAppearanceChanged();
}
}
private NumberPrecisionReducer createNumberPrecisionReducer()
{
double sf = scaleFactor;
// scaleFactor and decimalPlaces should be in synch, but if they are not use decimalPlaces
if (scaleFactor != NumberPrecisionReducer.scaleFactorForDecimalPlaces(decimalPlaces))
sf = NumberPrecisionReducer.scaleFactorForDecimalPlaces(decimalPlaces);
return new NumberPrecisionReducer(sf);
}
/**
* @return an array of two Lists.
* The first contains the geometries which reduced to invalid geometries.
* The second contains the invalid geometries created
*/
private List[] reducePrecision(FeatureCollection fc, TaskMonitor monitor)
{
List[] bad = { new ArrayList(), new ArrayList() };
int total = fc.size();
int count = 0;
for (Iterator i = fc.iterator(); i.hasNext(); ) {
monitor.report(count++, total, I18N.get("ui.plugin.edit.PrecisionReducerPlugIn.features"));
Feature f = (Feature) i.next();
Geometry g = f.getGeometry();
Geometry g2 = (Geometry) g.clone();
GeometryPrecisionReducer pr = new GeometryPrecisionReducer(createNumberPrecisionReducer());
pr.reduce(g2);
if (g2.isValid()) {
f.setGeometry(g2);
}
else {
bad[0].add(g.clone());
bad[1].add(g2);
}
}
return bad;
}
private void setDialogValues(MultiInputDialog dialog, PlugInContext context) {
dialog.setSideBarImage(new ImageIcon(getClass().getResource("PrecisionReducer.png")));
dialog.setSideBarDescription(I18N.get("ui.plugin.edit.PrecisionReducerPlugIn.Reduces-the-precision-of-the-coordinates-in-a-layer")
);
String fieldName = LAYER;
JComboBox addLayerComboBox = dialog.addLayerComboBox(fieldName, context.getCandidateLayer(0), null, context.getLayerManager());
scaleFactorField = dialog.addIntegerField(SCALE_FACTOR, scaleFactor, 8,
I18N.get("ui.plugin.edit.PrecisionReducerPlugIn.The-scale-factor-to-multiply-by-before-rounding-(-Negative-for-left-of-decimal-point-,-0-if-not-used-)"));
scaleFactorField.getDocument().addDocumentListener(new ScaleFactorDocumentListener());
decimalPlacesField = dialog.addIntegerField(DECIMAL_PLACES, decimalPlaces, 4,
I18N.get("ui.plugin.edit.PrecisionReducerPlugIn.The-number-of-decimal-places-to-round-to-(-Negative-for-left-of-decimal-point-)"));
decimalPlacesField.getDocument().addDocumentListener(new DecimalPlacesDocumentListener());
dialog.addLabel("");
dialog.addLabel(I18N.get("ui.plugin.edit.PrecisionReducerPlugIn.Example") + " " + EXAMPLE_VALUE);
exampleLabel = dialog.addLabel("");
updateExample();
}
private int parseValidInt(String text)
{
int i = 0;
try {
i = Integer.parseInt(text);
}
catch (NumberFormatException ex) {
// leave decPlaces value as 0
}
return i;
}
private void decimalPlacesChanged()
{
decimalPlaces = parseValidInt(decimalPlacesField.getText());
double sf = NumberPrecisionReducer.scaleFactorForDecimalPlaces(decimalPlaces);
scaleFactorField.setText("" + (int) sf);
updateExample();
}
private void scaleFactorChanged()
{
scaleFactor = parseValidInt(scaleFactorField.getText());
// can't update decimalPlaces because it will cause an event cycle
//decimalPlacesField.setText("");
updateExample();
}
private void updateExample()
{
NumberPrecisionReducer cpr = new NumberPrecisionReducer(scaleFactor);
double exampleOutput = cpr.reducePrecision(EXAMPLE_VALUE);
exampleLabel.setText(" ==> " + exampleOutput);
}
private void getDialogValues(MultiInputDialog dialog) {
Layer layer = dialog.getLayer(LAYER);
layerName = layer.getName();
decimalPlaces = dialog.getInteger(DECIMAL_PLACES);
scaleFactor = dialog.getInteger(SCALE_FACTOR);
}
class DecimalPlacesDocumentListener implements DocumentListener {
public void insertUpdate(DocumentEvent e) {
decimalPlacesChanged();
}
public void removeUpdate(DocumentEvent e) {
decimalPlacesChanged();
}
public void changedUpdate(DocumentEvent e) {
decimalPlacesChanged();
}
}
class ScaleFactorDocumentListener implements DocumentListener {
public void insertUpdate(DocumentEvent e) {
scaleFactorChanged();
}
public void removeUpdate(DocumentEvent e) {
scaleFactorChanged();
}
public void changedUpdate(DocumentEvent e) {
scaleFactorChanged();
}
}
}