[
Date Prev][
Date Next][
Thread Prev][
Thread Next][
Date Index][
Thread Index]
[
List Home]
Re: [udig-devel] How can we find intersection of two geometries?
|
Hello,
not sure if anyone got back to you...
The general solution is quite hard but a working solution can be
obtained using the underlying geometry library (JTS). Essentially you
create your geometric elements (JTS) for cutting
get a list of features, and for each:
get the geometry
use JTS operations to cut it with your elements
re-combine the new geometries with your original or with new features
repackage the whole thing
attached is a file that implements a uDig operation and buffers points
in this way. Hooking up the operation into uDig requires some eclipse
magic around the file---look at the uDig tootip example on the web to
understand the uDig IOp extension point.
hope this helps,
adrian
On Wed, 2007-04-04 at 20:02 +0300, Sergiy Doroshenko wrote:
> Hi all!
>
> How can we create new feature from given one by intersecting its
> geometry by another geometry? For example, split given feature by
> rectangular grid and create new feature for every peace - in this case
> we need to find intersection of multipolygon and rectangles.
>
> Is there some udig's or geotools' instrument, or this geometrical
> operation can be done only with some 3d party libraries?
> _______________________________________________
> User-friendly Desktop Internet GIS (uDig)
> http://udig.refractions.net
> http://lists.refractions.net/mailman/listinfo/udig-devel
/*
* This is a naive buffer operation for uDig, which operates on Point layers.
*
* The uDig operation extention point provides an approach to interact with the
* RichClientPlatform. The operation extention point can be implemented by
* creating four files:
* 1) A plugin activator
* 2) A java file which implements IOp by providing a single method op(...).
* 3) A plugin.xml, which declares the use of the extention point.
* 4) A Manifest
* This file uses the extention point to target uDig 'Layers'.
*
* TODO: CRS: for production use, 'naive buffer' should work in projected space.
* For now, the buffer is done in the euclidean space of the included
* features.
* TODO: Validate: add lots of checks on incoming data type.
* TODO: Use a Logger: change all the System.out/err calls to call the log
* TODO: Sort out how to create the layer (discouraged access vs. deprecation)
*
* (c) 2006 Adrian Custer, the uDig project, and others.
*
* @version: 0.0.3
*
* Targets uDig 1.1M7pre nightly builds of March 2006.
*/
package an.oread.operationsPlugin;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import net.refractions.udig.catalog.CatalogPlugin;
import net.refractions.udig.catalog.IGeoResource;
import net.refractions.udig.catalog.memory.internal.AnotherMemoryDataStore;
import net.refractions.udig.catalog.memory.internal.MemoryServiceImpl;
import net.refractions.udig.project.ILayer;
import net.refractions.udig.project.IMap;
import net.refractions.udig.project.internal.Layer;
import net.refractions.udig.project.internal.Project;
import net.refractions.udig.project.internal.impl.LayerFactoryImpl;
import net.refractions.udig.project.ui.ApplicationGIS;
import net.refractions.udig.ui.operations.IOp;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.geotools.data.FeatureReader;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureStore;
import org.geotools.feature.AttributeType;
import org.geotools.feature.AttributeTypeFactory;
import org.geotools.feature.Feature;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureType;
import org.geotools.feature.FeatureTypeBuilder;
import org.geotools.feature.GeometryAttributeType;
import org.geotools.geometry.jts.JTS;
import org.geotools.referencing.CRS;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.osgi.framework.Bundle;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;
public class BufferPtLayerOp implements IOp {
// This structure is defined in an inner class below. It is placed at the
// class level to ensure access is possible throughout the operation code.
final DialogData theDD = new DialogData();
// This variable defines the units in which we will have to buffer. If the
// user unit (theDD.unit) differs, then we have to convert the user distance
// into values for this unit.
// TODO: change from String to use org.jscience units.
String buffering_unit = null;
// These references to GUI widgets allow us to pass focus from one widget to
// the next when the <Tab> key is pressed. The {verify,modify}Listener
// methods will call .setFocus() on the 'next' Control.
Control unitCtrl, doitCtrl = null;
/**
* This is the implementation of the op(...) method which does the work
* when the menu entry is clicked. This method is the requirement of the IOp
* interface. The menu entry may be invoked from a main menu or from a
* context menu.
*
* @see net.refractions.udig.ui.operations.IOp#op(
* org.eclipse.swt.widgets.Display,
* java.lang.Object,
* org.eclipse.core.runtime.IProgressMonitor)
*/
public void op(final Display display, Object target, IProgressMonitor monitor)
throws Exception {
// To show that we are running our code, even when all else breaks.
// TODO:Cleanup - remove.
System.out.println("\nReached BufferPtLayerOp code...");
System.out.println("Start of operation: Buffer a point layer.");
//reset (for multiple runs)
theDD.distance = 0.0;
theDD.unit = "undefined";
// Get the cannonical references to
// In our plugin.xml, we defined our target to be an ILayer
// so we cast our target from Object to that.
// TODO Check instanceOf; Check it's a point layer; Check there's only 1
// TODO Figure out how to handle a collection of point layers
final ILayer myLayer = (ILayer) target;
// Get the cannonical references to Elements of the running app
final String myLayerName = myLayer.getName();
final IMap myIMap = myLayer.getMap();
final Project myProject = (Project) ApplicationGIS.getActiveProject();
// CoordinateReferenceSystem theCoordRefSys = null;
CoordinateReferenceSystem sourceCoordRefSys; // get from target layer
CoordinateReferenceSystem targetCoordRefSys = myLayer.getMap().getViewportModel().getCRS();
////// STEP 0: Get a buffer distance from the user
// TODO UNIT! Get the units from the renderer and set in the dialog.
// This will need to be set in a global variable
// buffering_unit so the dialog can grab thevalue and set
// it for presentation as the default unit.
// N.B. If we want to buffer on the original layer, this code will have
// to move lower down and be changed to get the unit from the layer.
display.syncExec(new Runnable(){
public void run() {
open(display);
}
});
System.out.println("The DD is: " +
theDD.distance +
" in units: " +
theDD.unit);
// TODO:Cleanup - remove.
System.out.println("Got the user to provide a buffer distance.");
////// STEP 1: Get access to the target's features
// TODO Convert to use expressions? This code accesses the feature
// contents directly which works as of uDig 1.1M4 and Geotools 2.2.pre0
// but is not guaranteed to work forever.
final IGeoResource myIGR = myLayer.getGeoResource(FeatureSource.class);
FeatureSource myFS = null;
if (myIGR.canResolve(FeatureSource.class)) {
try {
myFS = myIGR.resolve(FeatureSource.class, monitor);
} catch (Exception e) {
System.out.println(
"Resolving the IGeoResource failed with exception "+e);
// TODO: re-emit the exception, bail from the operation.
}
}
FeatureCollection myFC = myFS.getFeatures();
// TODO:Cleanup - remove.
System.out.println("Got to access the target's features.");
////// STEP 2: Validate TODO
// 1) all features share a common CRS
// 2) not too many
// For now, because they are from a shapefile, they will share a CRS
// and we can assume the shapefile is reasonable)
////// STEP 3: Get access to the geometries and buffer them into a new geom.
// TODO: CRITICAL the distance is defined in terms of the geom CRS.
// TODO: Handle the buffering of zero distance.-->It's a union
// TODO: Handle the case of unprojected coordinates.
// TODO: Add tolerance to the zero distance check.
Iterator myFCIter = null; //Always close it after use!
Geometry z = null;
MathTransform trans = null;
try {
myFCIter = myFC.iterator();
Feature f = (Feature) myFCIter.next();
sourceCoordRefSys =
f.getFeatureType().getDefaultGeometry().getCoordinateSystem();
// System.out.println(sourceCoordRefSys);
// System.out.println(targetCoordRefSys);
//This may throw a factoryException
trans = CRS.transform(sourceCoordRefSys, targetCoordRefSys, true);
Geometry oldg = f.getDefaultGeometry();
Geometry g = JTS.transform(oldg, trans);
if (0.0 == theDD.distance){
z = g;
} else {
z = g.buffer(theDD.distance);
}
// System.out.println(oldg);
// System.out.println(g);
// System.out.println(z);
// System.out.println(z.getGeometryType());
while ( myFCIter.hasNext()){
f = (Feature) myFCIter.next();
oldg = f.getDefaultGeometry();
g = JTS.transform(oldg, trans);
//TODO: assert that the geometries have the same CRS as z.
if(0.0 == theDD.distance){
z = z.union(g);
} else {
z = z.union(g.buffer(theDD.distance));
}
}
// } catch (FactoryException fe){ //from the transform
// System.out.println("Grrr.l 232 exception on transform---factoryEx.");
}
finally {
myFC.close( myFCIter );
}
// TODO:Cleanup - remove.
System.out.println("Got to access the geometries and made the buffer.");
////// STEP 4: Make a feature from the geometries
//////// 4.1: Make the FeatureType using a factory
////////// 4.1.1: Make the GeometryAttributeType
GeometryAttributeType myGT = null;
AttributeType myGeomAT = null;
// TODO: ASK how do we make this more robust to changes in geometry?
Class c = Class.forName("com.vividsolutions.jts.geom."+ z.getGeometryType());
try {
myGeomAT = AttributeTypeFactory.newAttributeType("the_geom",
c, true, 1, null,targetCoordRefSys);
} catch (Exception e){
System.out.println("The exception is: "+ e.getMessage());
}
// A less elegant approach, robust to change of package but doesn't
// pick up new types of geometry. Which is better?
// try {
// if ("Point" == z.getGeometryType()){
// myGeomAT = AttributeTypeFactory.newAttributeType("the_geom",
// Point.class, true, 1, null,targetCoordRefSys);
// } else if ("MultiPoint" == z.getGeometryType()){
// myGeomAT = AttributeTypeFactory.newAttributeType("the_geom",
// MultiPoint.class, true, 1, null,targetCoordRefSys);
// } else if ("LineString" == z.getGeometryType()){
// myGeomAT = AttributeTypeFactory.newAttributeType("the_geom",
// LineString.class, true, 1, null,targetCoordRefSys);
// } else if ("MultiLineString" == z.getGeometryType()){
// myGeomAT = AttributeTypeFactory.newAttributeType("the_geom",
// MultiLineString.class, true, 1, null,targetCoordRefSys);
// } else if ("Polygon" == z.getGeometryType()){
// myGeomAT = AttributeTypeFactory.newAttributeType("the_geom",
// Polygon.class, true, 1, null,targetCoordRefSys);
// } else if ("MultiPolygon" == z.getGeometryType()){
// myGeomAT = AttributeTypeFactory.newAttributeType("the_geom",
// MultiPolygon.class, true, 1, null,targetCoordRefSys);
// }
// } catch (Exception e){
// System.out.println("The exception is: "+ e.getMessage());
// }
if (myGeomAT instanceof GeometryAttributeType){
myGT = (GeometryAttributeType) myGeomAT;
}
FeatureTypeBuilder myFTBuilder = FeatureTypeBuilder.newInstance(
"orgOreadMultiPolyBUILDER");
// TODO: Add a better check to ensure the name is unique.
// The name must be unique for repeated buffering. Name clashes probably
// fail in the memoryDataStore creation routine.
myFTBuilder.setName(myLayerName
+ ":Buffered-"
+ System.currentTimeMillis()
+ "ms");
myFTBuilder.setDefaultGeometry(myGT);
FeatureType myBufferFT = myFTBuilder.getFeatureType();
//////// 4.2: Now make the Feature
Feature myBufferF = null;
myBufferF = myBufferFT.create(new Object[] {z},
"orgOreadbufferedPointsFEATURE");
// TODO:Cleanup - remove.
System.out.println("Got to make the buffer Feature.");
////// STEP 5: Turn the Geotools Feature into a uDig Layer
List<IGeoResource> geoResourceList2 = null, myGRList = null;
// Use the CatalogPlugin convenience method:
// 1) get an IGeoResource through the convenience method
// 2) resolve the IGeoResource to get a FeatureStore of some kind.
// 3) get an iterator on a List of features to add
// 4) use the deprecated!? addFeatures with a FeatureReader
// 5) create the Layer
System.out.println("...using the modern approach.");
IGeoResource newIGR = CatalogPlugin.getDefault().getLocalCatalog().
createTemporaryResource( myBufferFT );
FeatureStore newFS = null;
if (newIGR.canResolve(FeatureStore.class)) {
try {
newFS = newIGR.resolve(FeatureStore.class, monitor);;
} catch (Exception e) {
System.err.println(
"Resolving the IGeoResource failed with exception " + e);
// TODO: re-emit the exception, bail from the operation.
}
}
final Iterator<Feature> iter = Arrays.asList(myBufferF).iterator();
newFS.addFeatures( (FeatureReader) new FeatureReader(){
public boolean hasNext(){ return iter.hasNext(); }
public Feature next() { return iter.next(); }
// TODO: what should this be?
public void close() { ; }
public void remove() {throw new UnsupportedOperationException();}
public FeatureType getFeatureType(){return this.getFeatureType();}
}
);
// WHY? Creating the Layer is what adds it to the catalog!?
LayerFactoryImpl myLF = LayerFactoryImpl.create();
myLF.createLayer(newIGR);
//Make a List <IGeoResource>
myGRList = new ArrayList<IGeoResource> ();
myGRList.add(newIGR);
// TODO:Cleanup - remove.
System.out.println("Got to create the layer.");
////// STEP 6: Add the layer to a Map
//////// Step 6a: Add the layer to the current map.
ApplicationGIS.addLayersToMap(
myLayer.getMap(),myGRList,myLayer.getZorder(),myProject);
// TODO:Cleanup - remove.
System.out.println("Got to add the layer to the map.");
///// STEP 7: Confirm we are done
// TODO:Cleanup - remove.
System.out.println("Got to end of the operation.\n");
}
/**
* This is the method which shows the dialog and stores the modifed data in
* the final global variable theDD, an instance of DialogData.
* The method also uses an inner class defining the verification code for
* the freeform text box to ensure we get a postiive or zero number.
*
* @param display The Display passed in from the Op method.
*/
public void open(Display display){
// final DialogData opDD = new DialogData();
// Using SWT directly
final Shell s = new Shell(display,SWT.DIALOG_TRIM | SWT.RESIZE);
s.setSize(340,500);
s.setText("uDig-SpAn: Buffer a point layer");
//Place in center of parent
Rectangle rect = display.getActiveShell().getBounds();
Point dialogSize = s.getSize();
s.setLocation(
rect.x + (rect.width - dialogSize.x) / 2,
rect.y + (rect.height - dialogSize.y) /2
);
// Create the shell's form layout
s.setLayout(new FormLayout());
// Make the 'perform' button, the base Composites, and add to the layout
FormData reusableFrmDat;
Button butPerform = new Button(s,SWT.PUSH);
doitCtrl = butPerform;
butPerform.setText("Perform Operation");
reusableFrmDat = new FormData();
reusableFrmDat.right = new FormAttachment(100,-10);
reusableFrmDat.bottom = new FormAttachment(100,-10);
butPerform.setLayoutData(reusableFrmDat);
butPerform.addSelectionListener(new SelectionAdapter(){
public void widgetSelected(SelectionEvent se){
// theDD.setDistance(opDD.distance);
// theDD.setUnit(opDD.unit);
((Button) se.widget).getShell().close();
}
});
Composite titleComp = new Composite(s,SWT.NONE);
// Composite titleComp = new Composite(s,SWT.BORDER);
reusableFrmDat = new FormData();
reusableFrmDat.top = new FormAttachment(0,5);
reusableFrmDat.left = new FormAttachment(0,5);
reusableFrmDat.right = new FormAttachment(100,-5);
titleComp.setLayoutData(reusableFrmDat);
Composite simpleSettingsComp = new Composite(s,SWT.BORDER);
reusableFrmDat = new FormData();
reusableFrmDat.top = new FormAttachment(titleComp,5);
reusableFrmDat.left = new FormAttachment(0,5);
reusableFrmDat.right = new FormAttachment(100,-5);
simpleSettingsComp.setLayoutData(reusableFrmDat);
Composite complexOptionsComp = new Composite(s,SWT.BORDER);
reusableFrmDat = new FormData();
reusableFrmDat.top = new FormAttachment(simpleSettingsComp,5);
reusableFrmDat.left = new FormAttachment(0,5);
reusableFrmDat.right = new FormAttachment(100,-5);
complexOptionsComp.setLayoutData(reusableFrmDat);
// HACK-A-RAMMA!
Composite fillComp = new Composite(s,SWT.NONE);
reusableFrmDat = new FormData();
reusableFrmDat.top = new FormAttachment(complexOptionsComp,5);
reusableFrmDat.left = new FormAttachment(0,5);
reusableFrmDat.right = new FormAttachment(100,-5);
reusableFrmDat.bottom = new FormAttachment(butPerform,-10);
fillComp.setLayoutData(reusableFrmDat);
////////////////////////////////////////////////////////////////////////
// START OF SPECIFIC BLOCK
// TODO: make the code below call into the generic code above and below
//
////////////////// TITLE CONTENTS /////////////////////////////////
titleComp.setLayout(new FormLayout());
Label titleLab = new Label(titleComp,SWT.CENTER);
titleLab.setText("Point Buffer Operation");
Font titleFnt = new Font(null,"Arial", 18, SWT.BOLD); //dispose!
titleLab.setFont(titleFnt);
reusableFrmDat = new FormData();
reusableFrmDat.top = new FormAttachment(0,5);
reusableFrmDat.left = new FormAttachment(0,5);
reusableFrmDat.right = new FormAttachment(100,-5);
titleLab.setLayoutData(reusableFrmDat);
Label pictogramLab = new Label(titleComp,SWT.CENTER);
Bundle bundle = Platform.getBundle("an.oread.operationsPlugin");
IPath path = new Path("icons/uDigSpAn-Buffer.png");
URL url = Platform.find(bundle, path);
ImageDescriptor desc = ImageDescriptor.createFromURL(url);
Image titleImg = desc.createImage(); //dispose!
// ImageRegistry registry = new ImageRegistry();
// registry.put("uDigSpAn-Buffer", desc);
// Image titleImg = registry.get("uDigSpAn-Buffer");
pictogramLab.setImage(titleImg);
reusableFrmDat = new FormData();
reusableFrmDat.top = new FormAttachment(titleLab,10);
reusableFrmDat.left = new FormAttachment(0,5);
reusableFrmDat.right = new FormAttachment(100,-5);
pictogramLab.setLayoutData(reusableFrmDat);
Label explanationLab = new Label(titleComp,SWT.WRAP);
explanationLab.setText(
"The Point Buffer Operation takes a layer containing " +
"Features with point geometries and outputs a " +
"multipolygon whose extent includes all the area " +
"around the points up to the distance set below.");
reusableFrmDat = new FormData();
reusableFrmDat.top = new FormAttachment(pictogramLab,10);
reusableFrmDat.left = new FormAttachment(0,10);
reusableFrmDat.right = new FormAttachment(100,-10);
reusableFrmDat.bottom = new FormAttachment(100,-10);
explanationLab.setLayoutData(reusableFrmDat);
////////////////// SIMPLE SETTINGS CONTENTS ///////////////////////
simpleSettingsComp.setLayout(new FormLayout());
//Create a group for the distance input number and unit.
///////////////////////////////////////////////// START OF GROUP
Group g = new Group(simpleSettingsComp, SWT.BORDER);
g.setText("Buffer Distance");
reusableFrmDat = new FormData();
reusableFrmDat.top = new FormAttachment(0,5);
reusableFrmDat.left = new FormAttachment(0,5);
reusableFrmDat.right = new FormAttachment(100,-5);
reusableFrmDat.bottom = new FormAttachment(100,-5);
reusableFrmDat.height = 50; //Otherwise it's too tall.
g.setLayoutData(reusableFrmDat);
g.setLayout(new FormLayout());
Label lab2 = new Label(g,SWT.CENTER);
lab2.setText("Distance: ");
reusableFrmDat = new FormData();
reusableFrmDat.top = new FormAttachment(0,10);
reusableFrmDat.left = new FormAttachment(0,5);
lab2.setLayoutData(reusableFrmDat);
Text t = new Text(g, SWT.SINGLE | SWT.BORDER | SWT.FILL);
// The listeners are defined in inner classes below; both ensure
// that we obtain a non-negative number while handling keyboard
// navigation.
// Note the setText(...) call triggers the listeners.
t.addVerifyListener(new DistanceVerifyListener());
t.addModifyListener(new DistanceModifyListener());
// TODO: FIXME make resizing work, now we set an absurd number.
// t.setText("0.0");
// t.setSize(1000,2000);
// t.setBounds(10,10,100,20);
t.setText("0000000.0");
t.selectAll();
// t.setSelection(0,7);
reusableFrmDat = new FormData();
reusableFrmDat.top = new FormAttachment(0,10);
reusableFrmDat.left = new FormAttachment(lab2,5);
t.setLayoutData(reusableFrmDat);
Combo c = new Combo(g, SWT.DROP_DOWN |SWT.SINGLE | SWT.READ_ONLY );
c.setItems( new String [] {"undefined","meter", "degree", "inch"});
// The listener sets the unit in the DialogData struture opDD.
c.addModifyListener(new ModifyListener(){
public void modifyText(ModifyEvent me){
theDD.setUnit(( (Combo) me.widget ).getText());
};
});
c.select(0);
reusableFrmDat = new FormData();
reusableFrmDat.top = new FormAttachment(0,10);
reusableFrmDat.left = new FormAttachment(t,5);
c.setLayoutData(reusableFrmDat);
//TODO: Create the logic to handle units
//TODO: Remove this setEnabled(false).
c.setEnabled(false);
Composite fillCmpHoriz = new Composite(g,SWT.NONE);
reusableFrmDat = new FormData();
reusableFrmDat.top = new FormAttachment(0,10);
reusableFrmDat.left = new FormAttachment(c,5);
reusableFrmDat.right = new FormAttachment(100,-5);
reusableFrmDat.height = 10;
fillCmpHoriz.setLayoutData(reusableFrmDat);
/////////////////////////////////////////////////// END OF GROUP
////////////////// COMPLEX OPTIONS CONTENTS ///////////////////////
complexOptionsComp.setLayout(new FormLayout());
//The DETAILS button
Button bMod = new Button(complexOptionsComp,SWT.PUSH);
bMod.setText("Modify default settings ...");
reusableFrmDat = new FormData();
reusableFrmDat.top = new FormAttachment(0,5);
reusableFrmDat.left = new FormAttachment(0,5);
reusableFrmDat.bottom = new FormAttachment(100,-5);
bMod.setLayoutData(reusableFrmDat);
//TODO: Create the other dialog groups: CRS, Ouput (to file)...
//TODO: Remove this setEnabled(false).
//TODO: Create listeners that change the dialog and reopen it.
bMod.setEnabled(false);
t.setFocus();
// END OF SPECIFIC BLOCK
////////////////////////////////////////////////////////////////////////
s.open();
while (!s.isDisposed() ){
if (!display.readAndDispatch() )
display.sleep();
}
titleFnt.dispose();
titleImg.dispose();
}
/**
* This class is designed to ensure that the Distance text field remains
* numeric and positive. This is a quick initial attempt which does not
* address localization and other refined issues.
*
* @author acuster
*
*/
//TODO: Handle the 'return' value.
private class DistanceVerifyListener implements VerifyListener{
public void verifyText(VerifyEvent ve){
Text theText = (Text) ve.widget;
//Handle blanking the field
if (0 == ve.text.length()){
return;
}
// Handle the programatic setting of the value through a call with
// many characters at once.
if (1 < ve.text.length()){
// Check the result appears to be a number. Note that this
// approach actually allows the programatic creation of java
// doubles like 23.4d which we don't want to accept.
try{
Double.parseDouble(ve.text);
} catch (NumberFormatException nfEx){
ve.doit = false;
}
return;
}
// TODO:Refactor to use a 'switch' statement.
// TODO:Localization for number formats 24,000.00 vs. 24.000,00
// TODO:Robustness - handle scientific notation: 1.2232E34
// Handle the common case, where a user types in a single character
// at a time into the Text field.
char c = ve.text.charAt(0);
// System.out.println("verifyEvent c is: "+c);
//Handle <Enter>
if (SWT.CR == c){
ve.doit = false;
return;
}
//Handle <Tab>
//TODO UNITS change the focus to the unit.
if (SWT.TAB == c){
ve.doit = false;
doitCtrl.setFocus();
return;
}
//Handle a leading "."
if (('.' == c) && (0 == theText.getText().length())){
ve.text = new String ("0.");
return;
}
String newString =
theText.getText().substring(0,ve.start) +
ve.text +
theText.getText().substring( ve.end, theText.getText().length() );
// Check the input text is acceptable.
//TODO: deal with locale commas, etc.
String validChars = "0123456789.";
if (-1 == validChars.indexOf(c)){
ve.doit = false;
return;
}
// Check the result appears to be a number. Note that this approach
// allows the java numbers 23.4d and 2443.0f which we don't want to
// accept.
try{
Double.parseDouble(newString);
} catch (NumberFormatException nfEx){
ve.doit = false;
}
}
}//end of class DistanceVerificationListener
/**
* This handles the modifications. We dropped opDD from here, from the
* "Perform Operation" button, and from its declaration at the begining of
* the operation.
*/
private class DistanceModifyListener implements ModifyListener{
public void modifyText(ModifyEvent me){
String newDistStr = ((Text) me.widget ).getText();
// Allow blanking
if (0 == newDistStr.length()){
return;
}
double d = -1.0;
// Need to check because the value may be set programtically
// eventhough when this is triggered by UI we have just checked the
// number is parsable.
try{
d = Double.parseDouble(newDistStr);
} catch (NumberFormatException nfEx){
System.out.println("We win a NumberFormatException!");
}
theDD.setDistance(d);
}
}
/**
* This class holds the data we wish to obtain from the user through the
* dialog.
*
* @author acuster
*
*/
private class DialogData {
double distance;
String unit = null;
//Should not be needed since we should not access these before they are
// set. Check by commenting out.
// DialogData(){
// this.distance = 0.0;
// this.unit = "meter";
// }
void setDistance(double d){
this.distance = d;
}
double getDistance(){;
return distance;
}
void setUnit(String u){
this.unit = u;
}
String getUnit(){
return unit;
}
}//end of class DialogData
}