Re: [OpenMap Users] Re: ESRIPolygonRecord with holes

From: OpenMap Support <openmap_at_bbn.com>
Date: Tue, 27 Jul 2004 14:40:14 -0400

Hi Alexander,

Sorry I haven't gotten back to you on this. It sounds like a good idea
that should work. I didn't really think about polgons with holes when
I wrote OMAreaList, but the more I think about it it's a pretty simple
solution. I think OMAreaList would have to be modified slightly so it
could be set the way you describe.

One bad thing about OMAreaList, it doesn't handle being stretched back
around the earth very well, a part on each side of the map. I haven't
figured out the algorithm to connect the lines properly for those
cases, I don't know how that affects you drawing your polygons.

- Don

On Jun 25, 2004, at 10:50 AM, alexander sokolov wrote:

> Hi Don,
>
> It seems I found a way to treat ESRIPolygonRecord with holes
> correctly. We can use
> OMAreaList instead of OMGraphicList to store OMPolys of the ESRIRecord.
> Modifications are very small (see the method addOMGraphics in attached
> ESRIPolygonRecord.java)
> and also we have to set
>
> firstPoint to true in appendShapeEdge in the BasicGeometry.java.
>
> This modifications works well for me but I am not sure about possible
> side effects.
>
> What do you think about this?
>
> Regards
>
> Alexander
>
>
>
>
> OpenMap Support wrote:
>
>> Hi Alexander,
>>
>> That's correct, holds aren't handled well for either class. The code
>> would have to be modified to handle them.
>>
>> - Don
>>
>> On Jun 22, 2004, at 8:26 AM, alexander sokolov wrote:
>>
>>> Hi Don,
>>>
>>> I'm trying to work a with shape file of seas with islands and
>>> it seems neither EsriLayer nor ShapeLayer support ESRI records with
>>> holes.
>>> I mean it seems openmap does not pay attention to the order of
>>> polygon's vertices.
>>> (clockwise or counterclockwise). Is it so and what we can do?
>>>
>>> Regards
>>> Alexander
>>>
>>>
>>
>>
>> =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
>> Don Dietrick, dietrick_at_bbn.com
>> BBN Technologies, Cambridge, MA
>> =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
>>
> //
> **********************************************************************
> //
> // <copyright>
> //
> // BBN Technologies, a Verizon Company
> // 10 Moulton Street
> // Cambridge, MA 02138
> // (617) 873-8000
> //
> // Copyright (C) BBNT Solutions LLC. All rights reserved.
> //
> // </copyright>
> //
> **********************************************************************
> //
> // $Source:
> /cvs/distapps/openmap/src/openmap/com/bbn/openmap/layer/shape/
> ESRIPolygonRecord.java,v $
> // $RCSfile: ESRIPolygonRecord.java,v $
> // $Revision: 1.3 $
> // $Date: 2004/01/26 18:18:11 $
> // $Author: dietrick $
> //
> //
> **********************************************************************
>
>
> package com.bbn.openmap.layer.shape;
>
> import java.io.IOException;
> import com.bbn.openmap.omGraphics.*;
> import com.bbn.openmap.omGraphics.geom.*;
> import com.bbn.openmap.proj.ProjMath;
>
> /**
> * The Polygon record type. This class implements the ESRI Shapefile
> * polygon AND arc/polyline record types.
> * _at_author Ray Tomlinson
> * _at_author Tom Mitchell <tmitchell_at_bbn.com>
> * _at_author HACK-author blame it on aculline
> * _at_version $Revision: 1.3 $ $Date: 2004/01/26 18:18:11 $
> */
> public class ESRIPolygonRecord extends ESRIRecord {
>
> /** Polygon or arc/polyline?. */
> protected int shapeType = SHAPE_TYPE_POLYGON;
>
> /** The bounding box. */
> public ESRIBoundingBox bounds;
>
> /** An array of polygons. */
> public ESRIPoly[] polygons;
>
> public ESRIPolygonRecord() {
> bounds = new ESRIBoundingBox();
> polygons = new ESRIPoly[0];
> }
>
> /**
> * Initialize a polygon record from the given buffer.
> *
> * _at_param b the buffer
> * _at_param off the offset into the buffer where the data starts
> */
> public ESRIPolygonRecord(byte b[], int off) throws IOException {
> super(b, off);
>
> int ptr = off+8;
>
> shapeType = readLEInt(b, ptr);
> ptr += 4;
> if ((shapeType != SHAPE_TYPE_POLYGON) && (shapeType !=
> SHAPE_TYPE_ARC)) {
> throw new IOException("Invalid polygon record. Expected
> shape " +
> "type " + SHAPE_TYPE_POLYGON + " or
> type " +
> SHAPE_TYPE_ARC + ", but found " +
> shapeType);
> }
> boolean ispolyg = isPolygon();
>
> bounds = readBox(b, ptr);
> ptr += 32; // A box is 4 doubles (4 x 8bytes)
>
> int numParts = readLEInt(b, ptr);
> ptr += 4;
>
> int numPoints = readLEInt(b, ptr);
> ptr += 4;
>
> if (numParts <= 0) return;
>
> polygons = new ESRIPoly[numParts];
> int origin = 0;
> int _len;
> for (int i = 0; i < numParts; i++) {
>
> int nextOrigin = readLEInt(b, ptr);
> ptr += 4;
>
> if (i > 0) {
> _len = nextOrigin - origin;
> if (ispolyg) ++_len;//connect pairs
> polygons[i-1] = new ESRIPoly.ESRIFloatPoly(_len);
> }
> origin = nextOrigin;
> }
> _len = numPoints - origin;
> if (ispolyg) ++_len;//connect pairs
> polygons[numParts-1] = new ESRIPoly.ESRIFloatPoly(_len);
> for (int i = 0; i < numParts; i++) {
> ptr += polygons[i].read(b, ptr, ispolyg);
> }
> }
>
> /**
> * Is this a polygon or a arc/polyline?
> * _at_return boolean
> */
> public boolean isPolygon() {
> return shapeType == SHAPE_TYPE_POLYGON;
> }
>
> /**
> * Set the poly type (polygon or arc/polyline).
> */
> public void setPolygon(boolean isPolygon) {
> shapeType = isPolygon ? SHAPE_TYPE_POLYGON : SHAPE_TYPE_ARC;
> }
>
> /**
> * Add a poly to the record.
> * _at_param radians coordinates: y,x,y,x,... (lat,lon) order in
> * RADIANS!
> */
> public void add(float radians[]) {
> ESRIPoly newPoly = new ESRIPoly.ESRIFloatPoly(radians);
>
> int numParts = polygons.length;
> ESRIPoly oldPolys[] = polygons;
> polygons = new ESRIPoly[numParts + 1];
> for (int i = 0; i < numParts; i++) {
> polygons[i] = oldPolys[i];
> }
>
> polygons[numParts] = newPoly;
>
> int len = radians.length;
> for (int i=0; i<len; i+=2) {
> // REMEMBER: switch to x,y order
> bounds.addPoint(
> ProjMath.radToDeg(radians[i+1]),//x (lon)
> ProjMath.radToDeg(radians[i]));//y (lat)
> }
> }
>
> /**
> * Generates 2D OMGraphics and adds them to the given list. If
> * you are using jdk1.1.X, you'll have to comment out this method,
> * because jdk1.1.X doesn't know about the java.awt.Stroke and
> * java.awt.Paint interfaces.
> *
> * _at_param list the graphics list
> * _at_param drawingAttributes the drawingAttributes to paint the
> poly.
> */
> public void addOMGraphics(OMGraphicList list,
> DrawingAttributes drawingAttributes) {
>
> int nPolys = polygons.length;
> if (nPolys <= 0) return;
> OMPoly p=null;
> float[] pts;
> boolean ispolyg = isPolygon();
> /*
> * modifications in the next 4 lines marked with as:
> * allow to treat ESRIPolygonRecord with holes correctly
> * (ESRIPolys with counterclockwise order of vertices)
> *
> * Note: we also have to set firstPoint to true in
> * the BasinsGeometry.appendShapeEdge() (probably a bug)
> * 2004-06-25
> */
> OMAreaList sublist = null; //as: was OMGraphicList sublist = null;
>
> if (nPolys > 1) {
> sublist = new OMAreaList(10); //as: was OMGraphicList(10);
> drawingAttributes.setTo(sublist); //as: new line
>
> sublist.setVague(true); // Treat list as one object.
> list.add(sublist);
> sublist.setAppObject(new Integer(getRecordNumber()));
> }
>
> for (int i=0, j, k; i<nPolys; i++) {
> // these points are already in RADIAN lat,lon order!...
> pts = ((ESRIPoly.ESRIFloatPoly)polygons[i]).getRadians();
> int len = pts.length;
> p = new OMPoly(pts,
> OMGraphic.RADIANS,
> OMGraphic.LINETYPE_STRAIGHT);
>
> drawingAttributes.setTo(p);
> if (!ispolyg) {
> p.setIsPolygon(false);
> }
>
> if (sublist != null) {
> sublist.add(p);
> } else {
> // There should be only one.
> p.setAppObject(new Integer(getRecordNumber()));
> list.add(p);
> }
> }
> }
>
> /**
> * Generates OMGeometry and adds them to the given list.
> *
> * _at_param list the geometry list
> */
> public OMGeometry addOMGeometry(OMGeometryList list) {
>
> int nPolys = polygons.length;
> if (nPolys <= 0) {
> return null;
> }
>
> float[] pts;
> boolean ispolyg = isPolygon();
> OMGeometry geom = null;
>
> for (int i=0, j, k; i<nPolys; i++) {
> // these points are already in RADIAN lat,lon order!...
> pts = ((ESRIPoly.ESRIFloatPoly)polygons[i]).getRadians();
> int len = pts.length;
> if (ispolyg) {
>
> geom = new PolygonGeometry.LL(pts,
> OMGraphic.RADIANS,
>
> OMGraphic.LINETYPE_STRAIGHT);
> } else {
> geom = new PolylineGeometry.LL(pts,
> OMGraphic.RADIANS,
>
> OMGraphic.LINETYPE_STRAIGHT);
> }
> list.add(geom);
> }
> return geom;
> }
>
> /**
> * Gets this record's bounding box.
> *
> * _at_return a bounding box
> */
> public ESRIBoundingBox getBoundingBox() {
> return bounds;
> }
>
> /**
> * Gets this record's shape type as an int. Shape types
> * are enumerated on the ShapeUtils class.
> *
> * _at_return the shape type as an int (either SHAPE_TYPE_POLYGON or
> * SHAPE_TYPE_ARC)
> */
> public int getShapeType() {
> return shapeType;
> }
>
> /**
> * Yields the length of this record's data portion.
> * <p>
> * (44 + (numParts * 4) + (numPoints * 16))
> * <br>
> * 3 Integers + 4 doubles == 3 * 4bytes + 4 * 8bytes == 12 + 32 ==
> 44.
> *
> * _at_return number of bytes equal to the size of this record's data
> */
> public int getRecordLength() {
> int numParts = polygons.length;
> int numPoints = 0;
> for (int i = 0; i < numParts; i++) {
> numPoints += polygons[i].nPoints;
> }
> return (44 + (numParts * 4) + (numPoints * 16));
> }
>
> /**
> * Writes this polygon to the given buffer at the given offset.
> *
> * _at_param b the buffer
> * _at_param off the offset
> * _at_return the number of bytes written
> */
> public int write(byte[] b, int off) {
> int nBytes = super.write(b, off);
> nBytes += writeLEInt(b, off + nBytes, shapeType);
> // bounds
> nBytes += writeBox(b, off + nBytes, bounds);
> // numparts
> int numParts = polygons.length;
> nBytes += writeLEInt(b, off + nBytes, numParts);
> // numpoints
> int numPoints = 0;
> for (int i = 0; i < numParts; i++) {
> numPoints += polygons[i].nPoints;
> }
> nBytes += writeLEInt(b, off + nBytes, numPoints);
> // parts
> int ptr = 0;
> for (int i = 0; i < numParts; i++) {
> nBytes += writeLEInt(b, off + nBytes, ptr);
> ptr += polygons[i].nPoints;
> }
>
> // points
> for (int i = 0; i < numParts; i++) {
> // REMEMBER: stored internally as y,x order (lat,lon order)
> float[] pts =
> ((ESRIPoly.ESRIFloatPoly)polygons[i]).getRadians();
> int nPts = pts.length;
> for (int j=0; j<nPts; j+=2) {
> nBytes += writeLEDouble(
> b, off + nBytes,
> (double)ProjMath.radToDeg(pts[j+1]));//x (lon)
> nBytes += writeLEDouble(
> b, off + nBytes,
> (double)ProjMath.radToDeg(pts[j]));//y (lat)
> }
> }
>
> // return number of bytes written
> return nBytes;
> }
> }
> //
> **********************************************************************
> //
> // <copyright>
> //
> // BBN Technologies, a Verizon Company
> // 10 Moulton Street
> // Cambridge, MA 02138
> // (617) 873-8000
> //
> // Copyright (C) BBNT Solutions LLC. All rights reserved.
> //
> // </copyright>
> //
> **********************************************************************
> //
> // $Source:
> /cvs/distapps/openmap/src/openmap/com/bbn/openmap/omGraphics/geom/
> BasicGeometry.java,v $
> // $RCSfile: BasicGeometry.java,v $
> // $Revision: 1.8 $
> // $Date: 2004/01/26 18:18:13 $
> // $Author: dietrick $
> //
> //
> **********************************************************************
>
>
> package com.bbn.openmap.omGraphics.geom;
>
> import com.bbn.openmap.omGraphics.OMGeometry;
> import com.bbn.openmap.omGraphics.OMGraphicConstants;
> import com.bbn.openmap.proj.*;
> import com.bbn.openmap.util.Debug;
>
> import java.awt.*;
> import java.awt.geom.*;
> import java.io.Serializable;
>
> /**
> * Base class implementation of OpenMap OMGeometry. <p>
> *
> * The geometry classes are intended to pull the object location data
> * out of the OMGraphics. If you have a bunch of OMGraphics that are
> * all rendered with common attributes, you can create a bunch of
> * OMGeometry objects to plavce in a OMGeometryList that will render
> * them all alike.
> *
> * _at_see PolygonGeometry
> * _at_see PolylineGeometry
> * _at_see com.bbn.openmap.omGraphics.OMGeometryList
> * _at_see Projection
> */
> public abstract class BasicGeometry
> implements OMGeometry, Serializable, OMGraphicConstants {
>
> /**
> * The lineType describes the way a line will be drawn between
> * points. LINETYPE_STRAIGHT will mean the line is drawn straight
> * between the pixels of the endpoints of the line, across the
> * window. LINETYPE_GREATCIRCLE means the line will be drawn on
> the
> * window representing the shortest line along the
> * land. LINETYPE_RHUMB means a line will be drawn along a constant
> * bearing between the two points.
> */
> protected int lineType = LINETYPE_UNKNOWN;
>
> /** Flag to indicate that the object needs to be reprojected. */
> protected boolean needToRegenerate = true;
>
> /**
> * Space for an application to associate geometry with an
> * application object. This object can contain attribute
> * information about the geometry.
> *
> * _at_see #setAppObject
> * _at_see #getAppObject
> */
> protected Object appObject;
>
> /**
> * A flag to render this geometry visible.
> */
> protected boolean visible = true;
>
> /**
> * The Java 2D containing the Shape of the Graphic. There may be
> * an array of them, in case the graphic wraps around the earth,
> * and we need to show the other edge of the graphic on the other
> * side of the earth.
> */
> protected transient GeneralPath shape = null;
>
> //////////////////////////////////////////////////////////
>
> /**
> * Set the line type for the graphic, which will affect how the
> * lines will be drawn. See the definition of the lineType
> * parameter. Accepts LINETYPE_RHUMB, LINETYPE_STRAIGHT and
> * LINETYPE_GREATCIRCLE. Any weird values get set to
> * LINETYPE_STRAIGHT.
> *
> * _at_param value the line type of the graphic.
> * */
> public void setLineType(int value) {
> if (lineType == value) return;
> setNeedToRegenerate(true); // flag dirty
>
> lineType = value;
> }
>
> /**
> * Return the line type.
> *
> * _at_return the linetype - LINETYPE_RHUMB, LINETYPE_STRAIGHT,
> * LINETYPE_GREATCIRCLE or LINETYPE_UNKNOWN.
> */
> public int getLineType() {
> return lineType;
> }
>
> /**
> * Return the render type.
> *
> * _at_return the rendertype of the object - RENDERTYPE_LATLON,
> * RENDERTYPE_XY, RENDERTYPE_OFFSET and RENDERTYPE_UNKNOWN.
> */
> public abstract int getRenderType();
>
> /**
> * Sets the regenerate flag for the graphic.
> * This flag is used to determine if extra work needs to be done
> * to prepare the object for rendering.
> *
> * _at_param value boolean
> */
> public void setNeedToRegenerate(boolean value) {
> needToRegenerate = value;
> if (value == true) {
> shape = null;
> }
> }
>
> /**
> * Return the regeneration status.
> *
> * _at_return boolean
> */
> public boolean getNeedToRegenerate() {
> return needToRegenerate;
> }
>
> /**
> * Set the visibility variable.
> * NOTE:<br>
> * This is checked by the OMGeometryList when it iterates through
> its list
> * for render and gesturing. It is not checked by the internal
> OMGeometry
> * methods, although maybe it should be...
> *
> * _at_param visible boolean
> */
> public void setVisible(boolean visible) {
> this.visible = visible;
> }
>
> /**
> * Get the visibility variable.
> *
> * _at_return boolean
> */
> public boolean isVisible() {
> return visible;
> }
>
> /**
> * Let the geometry object know it's selected. No action mandated.
> */
> public void select() {}
>
> /**
> * Let the geometry object know it's deselected. No action
> mandated.
> */
> public void deselect() {}
>
> /**
> * Holds an application specific object for later access.
> * This can be used to associate an application object with
> * an OMGeometry for later retrieval. For instance, when
> * the graphic is clicked on, the application gets the OMGeometry
> * object back from the OMGeometryList, and can then get back
> * to the application level object through this pointer.
> *
> * _at_param obj Object
> */
> public synchronized void setAppObject(Object obj) {
> appObject = obj;
> }
>
> /**
> * Gets the application's object pointer.
> *
> * _at_return Object
> */
> public synchronized Object getAppObject() {
> return appObject;
> }
>
> //////////////////////////////////////////////////////////////////////
> ////
>
> /**
> * Prepare the geometry for rendering.
> * This must be done before calling <code>render()</code>! If a
> * vector graphic has lat-lon components, then we project these
> * vertices into x-y space. For raster graphics we prepare in a
> * different fashion. <p>
> * If the generate is unsuccessful, it's usually because of some
> * oversight, (for instance if <code>proj</code> is null), and if
> * debugging is enabled, a message may be output to the controlling
> * terminal. <p>
> *
> * _at_param proj Projection
> * _at_return boolean true if successful, false if not.
> * _at_see #regenerate
> */
> public abstract boolean generate(Projection proj);
>
> /**
> *
> */
> public boolean isRenderable() {
> return (!getNeedToRegenerate() &&
> isVisible() && shape != null);
> }
>
> /**
> * Paint the graphic, as a filled shape. <P>
> *
> * This paints the graphic into the Graphics context. This is
> * similar to <code>paint()</code> function of
> * java.awt.Components. Note that if the graphic has not been
> * generated or if it isn't visible, it will not be
> * rendered. <P>
> *
> * This method used to be abstract, but with the conversion of
> * OMGeometrys to internally represent themselves as java.awt.Shape
> * objects, it's a more generic method. If the OMGeometry hasn't
> * been updated to use Shape objects, it should have its own
> * render method.
> *
> * _at_param g Graphics2D context to render into.
> */
> public void fill(Graphics g) {
> if (isRenderable()) {
> ((Graphics2D)g).fill(shape);
> }
> }
>
> /**
> * Paint the graphic, as an outlined shape. <P>
> *
> * This paints the graphic into the Graphics context. This is
> * similar to <code>paint()</code> function of
> * java.awt.Components. Note that if the graphic has not been
> * generated or if it isn't visible, it will not be
> * rendered. <P>
> *
> * This method used to be abstract, but with the conversion of
> * OMGeometrys to internally represent themselves as java.awt.Shape
> * objects, it's a more generic method. If the OMGeometry hasn't
> * been updated to use Shape objects, it should have its own
> * render method.
> *
> * _at_param g Graphics2D context to render into.
> */
> public void draw(Graphics g) {
> if (isRenderable()) {
> ((Graphics2D)g).draw(shape);
> }
> }
>
> /**
> * Return the shortest distance from the edge of a graphic to an
> * XY-point. <p>
> *
> * _at_param x X coordinate of the point.
> * _at_param y Y coordinate of the point.
> * _at_return float distance, in pixels, from graphic to the point.
> * Returns Float.POSITIVE_INFINITY if the graphic isn't ready
> * (ungenerated).
> */
> public float distanceToEdge(int x, int y) {
> float temp, distance = Float.POSITIVE_INFINITY;
>
> if (getNeedToRegenerate() || shape == null) {
> return distance;
> }
>
> PathIterator pi2 = shape.getPathIterator(null);
> FlatteningPathIterator pi = new FlatteningPathIterator(pi2,
> .25);
> double[] coords = new double[6];
> int count = 0;
> double startPntX = 0;
> double startPntY = 0;
> double endPntX = 0;
> double endPntY = 0;
>
> while (!pi.isDone()) {
> int type = pi.currentSegment(coords);
> float dist;
>
> if (type == PathIterator.SEG_LINETO) {
> startPntX = endPntX;
> startPntY = endPntY;
> endPntX = coords[0];
> endPntY = coords[1];
>
> dist = (float) Line2D.ptSegDist(startPntX, startPntY,
> endPntX, endPntY, (double)x, (double)y);
>
> if (dist < distance) {
> distance = dist;
> }
>
> if (Debug.debugging("omgraphicdetail")) {
> Debug.output("Type: " + type + "(" +
> (count++) + "), " +
> startPntX + ", " + startPntY + ", " +
> endPntX + ", " + endPntY + ", " +
> x + ", " + y +
> ", distance: " + distance);
> }
>
> } else {
>
> // This should be the first and last
> // condition, SEG_MOVETO and SEG_CLOSE
> startPntX = coords[0];
> startPntY = coords[1];
> endPntX = coords[0];
> endPntY = coords[1];
> }
>
> pi.next();
> }
>
> return distance;
> }
>
> /**
> * Return the shortest distance from the graphic to an
> * XY-point. Checks to see of the point is contained within the
> * OMGraphic, which may, or may not be the right thing for clear
> * OMGraphics or lines.<p>
> *
> * This method used to be abstract, but with the conversion of
> * OMGeometrys to internally represent themselves as java.awt.Shape
> * objects, it's a more generic method. If the OMGeometry hasn't
> * been updated to use Shape objects, it should have its own
> * distance method.<p>
> *
> * Calls _distance(x, y);
> *
> * _at_param x X coordinate of the point.
> * _at_param y Y coordinate of the point.
> * _at_return float distance, in pixels, from graphic to the point.
> * Returns Float.POSITIVE_INFINITY if the graphic isn't ready
> * (ungenerated).
> */
> public float distance(int x, int y) {
> return _distance(x, y);
> }
>
> /**
> * Return the shortest distance from the graphic to an
> * XY-point. Checks to see of the point is contained within the
> * OMGraphic, which may, or may not be the right thing for clear
> * OMGraphics or lines.<p>
> *
> * _distance was added so subclasses could make this call if their
> * geometries/attributes require this action (when fill color
> * doesn't matter).
> *
> * _at_param x X coordinate of the point.
> * _at_param y Y coordinate of the point.
> * _at_return float distance, in pixels, from graphic to the point.
> * Returns Float.POSITIVE_INFINITY if the graphic isn't ready
> * (ungenerated).
> */
> protected float _distance(int x, int y) {
> float temp, distance = Float.POSITIVE_INFINITY;
>
> if (getNeedToRegenerate() || shape == null) {
> return distance;
> }
>
> if (shape.contains((double)x, (double)y)) {
> // if (Debug.debugging("omgraphicdetail")) {
> // Debug.output(" contains " + x + ", " + y);
> // }
> return 0f;
> } else {
> return distanceToEdge(x, y);
> }
> }
>
> /**
> * Answsers the question whether or not the OMGeometry contains
> * the given pixel point.
> * <P>
> * This method used to be abstract, but with the conversion of
> * OMGeometrys to internally represent themselves as java.awt.Shape
> * objects, it's a more generic method. If the OMGeometry hasn't
> * been updated to use Shape objects, it should have its own
> * contains method.
> * <P>
> * This method duplicates a java.awt.Shape method, with some
> * protection wrapped around it. If you have other queries for
> * the internal Shape object, just ask for it and then ask it
> * directly. This method is provided because it is the most
> * useful, used when determining if a mouse event is occuring over
> * an object on the map.
> *
> * _at_param x X pixel coordinate of the point.
> * _at_param y Y pixel coordinate of the point.
> * _at_return getShape().contains(x, y), false if the OMGraphic
> * hasn't been generated yet.
> */
> public boolean contains(int x, int y) {
> Shape shape = getShape();
> boolean ret = false;
>
> if (shape != null) {
> ret = shape.contains((double)x, (double)y);
> }
>
> return ret;
> }
>
> /**
> * Invoke this to regenerate a "dirty" graphic.
> * This method is a wrapper around the <code>generate()</code>
> * method. It invokes <code>generate()</code> only if
> * </code>needToRegenerate()</code> on the graphic returns true.
> * To force a graphic to be generated, call
> * <code>generate()</code> directly.
> *
> * _at_param proj the Projection
> * _at_return true if generated, false if didn't do it (maybe a
> * problem).
> * _at_see #generate
> */
> public boolean regenerate(Projection proj) {
> if (proj == null) {
> return false;
> }
>
> if (getNeedToRegenerate()) {
> return generate(proj);
> }
>
> return false;
> }
>
> /**
> * Get the java.awt.Shape object that represents the projected
> * graphic. The array will one Shape object even if the object
> * wraps around the earth and needs to show up in more than one
> * place on the map. In conditions like that, the Shape will have
> * multiple parts.<p>
> *
> * The java.awt.Shape object gives you the ability to do a little
> * spatial analysis on the graphics.
> *
> * _at_return java.awt.geom.GeneralPath (a java.awt.Shape object), or
> * null if the graphic needs to be generated with the current map
> * projection, or null if the OMGeometry hasn't been updated to
> * use Shape objects for its internal representation.
> */
> public GeneralPath getShape() {
> return shape;
> }
>
> /**
> * Set the java.awt.Shape object that represents the projected
> * graphic. This Shape object should be internally generated, but
> * this method is provided to clear out the object to save memory,
> * or to allow a little customization if your requirements
> * dictate.<p>
> *
> * The java.awt.Shape object gives you the ability to do a little
> * spatial analysis on the graphics.
> *
> * _at_param gp java.awt.geom.GeneralPath, or null if the graphic
> * needs to be generated with the current map projection or to
> * clear out the object being held by the OMGeometry.
> */
> public void setShape(GeneralPath gp) {
> shape = gp;
> }
>
> /**
> * Create a Shape object given an array of x points and y points.
> * The x points a y points should be projected. This method is
> * used by subclasses that get projected coordinates out of the
> * projection classes, and they need to build a Shape object from
> * those coordinates.
> * _at_param xpoints projected x coordinates
> * _at_param ypoints projected y coordinates
> * _at_param isPolygon whether the points make up a polygon, or a
> * polyline. If it's true, the Shape object returned is a
> * Polygon. If false, the Shape returned is a GeneralPath object.
> * _at_return The Shape object for the points.
> */
> public static GeneralPath createShape(int xpoints[], int
> ypoints[], boolean isPolygon) {
> return createShape(xpoints, ypoints, 0, xpoints.length,
> isPolygon);
> }
>
> /**
> * Create a Shape object given an array of x points and y points.
> * The x points a y points should be projected. This method is
> * used by subclasses that get projected coordinates out of the
> * projection classes, and they need to build a Shape object from
> * those coordinates.
> * _at_param xpoints projected x coordinates
> * _at_param ypoints projected y coordinates
> * _at_param startIndex the starting coordinate index in the array.
> * _at_param length the number of points to use from the array for
> the shape.
> * _at_param isPolygon whether the points make up a polygon, or a
> * polyline. If it's true, the Shape object returned is a
> * Polygon. If false, the Shape returned is a GeneralPath object.
> * _at_return The Shape object for the points.
> */
> public static GeneralPath createShape(int xpoints[], int ypoints[],
> int startIndex, int length,
> boolean isPolygon) {
> // used to return a Shape
>
> if (xpoints == null || ypoints == null) {
> return null;
> }
>
> if (startIndex < 0) {
> startIndex = 0;
> }
>
> if (length > xpoints.length - startIndex) {
> // Do as much as you can...
> length = xpoints.length - startIndex - 1;
> }
>
> GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD,
> length);
>
> if (length > startIndex) {
> path.moveTo(xpoints[startIndex], ypoints[startIndex]);
> for (int j = startIndex + 1; j < length; j++) {
> path.lineTo(xpoints[j], ypoints[j]);
> }
>
> if (isPolygon) {
> path.closePath();
> }
> }
>
> return path;
> }
>
> /**
> * Utility method that iterates over a Shape object and prints out
> the points.
> */
> public static void describeShapeDetail(Shape shape) {
> describeShapeDetail(shape, .25);
> }
>
> /**
> * Utility method that iterates over a Shape object and prints out
> * the points. The flattening is used for a
> * FlatteningPathIterator, controlling the scope of the path
> * traversal.
> */
> public static void describeShapeDetail(Shape shape, double
> flattening) {
> PathIterator pi2 = shape.getPathIterator(null);
> FlatteningPathIterator pi = new FlatteningPathIterator(pi2,
> flattening);
> double[] coords = new double[6];
> int pointCount = 0;
>
> Debug.output(" -- start describeShapeDetail with flattening["
> + flattening + "]");
> while (!pi.isDone()) {
> int type = pi.currentSegment(coords);
> Debug.output(" Shape point [" + type + "] (" +
> (pointCount++) + ") " +
> coords[0] + ", " + coords[1]);
> pi.next();
> }
>
> Debug.output(" -- end (" + pointCount + ")");
> }
>
> /**
> * Convenience method to add the coordinates to the given
> * GeneralPath. You need to close the path yourself if you want
> * it to be a polygon.
> * _at_param toShape the GeneralPath Shape object to add the
> coordinates to.
> * _at_param xpoints horizontal pixel coordiantes.
> * _at_param ypoints vertical pixel coordiantes.
> * _at_return toShape, with coordinates appended.
> */
> public static GeneralPath appendShapeEdge(GeneralPath toShape,
> int xpoints[], int
> ypoints[]) {
> return appendShapeEdge(toShape, xpoints, ypoints, 0,
> xpoints.length);
> }
>
> /**
> * Convenience method to add the coordinates to the given
> * GeneralPath. You need to close the path yourself if you want
> * it to be a polygon.
> * _at_param toShape the GeneralPath Shape object to add the
> coordinates to.
> * _at_param xpoints horizontal pixel coordiantes.
> * _at_param ypoints vertical pixel coordiantes.
> * _at_param startIndex the index into pixel coordinate array to
> start reading from.
> * _at_param length the number of coordinates to add.
> * _at_return toShape, with coordinates appended.
> */
> public static GeneralPath appendShapeEdge(GeneralPath toShape,
> int xpoints[], int
> ypoints[],
> int startIndex, int
> length) {
> return appendShapeEdge(toShape, createShape(xpoints, ypoints,
> startIndex, length, false));
> }
>
> /**
> * Convenience method to append the edge of a GeneralPath Shape to
> * another GeneralPath Shape. A PathIterator is used to figure
> * out the points to use to add to the toShape. You need to close
> * the path yourself if you want it to be a polygon.
> * _at_param toShape the GeneralPath Shape object to add the edge to.
> * _at_param addShape the GeneralPath Shape to add to the toShape.
> * _at_return toShape, with coordinates appended. Returns addShape
> * if toShape was null.
> */
> public static GeneralPath appendShapeEdge(GeneralPath toShape,
> GeneralPath addShape) {
>
> boolean DEBUG = Debug.debugging("arealist");
> int pointCount = 0;
> boolean firstPoint = true; //as: was false;
> // See
> ESRIPolygonRecord.addOMGraphics
>
> // If both null, return null.
> if (addShape == null) {
> return toShape;
> }
>
> if (toShape == null) {
> return addShape;
> }
>
> PathIterator pi2 = addShape.getPathIterator(null);
> FlatteningPathIterator pi = new FlatteningPathIterator(pi2,
> .25);
> double[] coords = new double[6];
>
> while (!pi.isDone()) {
> int type = pi.currentSegment(coords);
> if (firstPoint) {
> if (DEBUG) {
> Debug.output("Creating new shape, first point " +
> (float)coords[0] + ", " +
> (float)coords[1]);
> }
> toShape.moveTo((float)coords[0], (float)coords[1]);
> firstPoint = false;
> } else {
> if (DEBUG) {
> Debug.output(" adding point [" + type + "] (" +
> (pointCount++) + ") " +
> (float)coords[0] + ", " +
> (float)coords[1]);
> }
> toShape.lineTo((float)coords[0], (float)coords[1]);
> }
> pi.next();
> }
>
> if (DEBUG) {
> Debug.output(" -- end point (" + pointCount + ")");
> }
>
> return toShape;
> }
>
>
> /**
> * Create a general path from a point plus a height and width;
> */
> public static GeneralPath createBoxShape(int x, int y, int width,
> int height) {
> int[] xs = new int[4];
> int[] ys = new int[4];
>
> xs[0] = x;
> ys[0] = y;
> xs[1] = x + width;
> ys[1] = y;
> xs[2] = x + width;
> ys[2] = y + height;
> xs[3] = x;
> ys[3] = y + height;
>
> return createShape(xs, ys, true);
> }
> }
>


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Don Dietrick, dietrick_at_bbn.com
BBN Technologies, Cambridge, MA
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

--
[To unsubscribe to this list send an email to "majdart_at_bbn.com"
with the following text in the BODY of the message "unsubscribe openmap-users"]
Received on Tue Jul 27 2004 - 14:40:30 EDT

This archive was generated by hypermail 2.3.0 : Tue Mar 28 2017 - 23:25:05 EDT