Re: [OpenMap Users] Re: ESRIPolygonRecord with holes

From: Tore Halset <halset_at_pvv.ntnu.no>
Date: Wed, 28 Jul 2004 19:37:08 +0200

Hello.

Thanks! I am using this now, and it works very well. It would still be
very nice to have a OMPoly that had support for holes. How can I
control the linepaint of the hole polygons separatly from the exterior
polygon?

Perhaps the fix for BasicGeometry can be included in the standard
distribution?

Regards,
  - Tore.


On Jun 25, 2004, at 16:50, 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);
> }
> }

--
[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 Wed Jul 28 2004 - 14:00:48 EDT

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