Library References  

Geometry

 

Library dependencies: System, SystemUI


The Geometry library provides vector representations for Points, MultiPoints, Polylines, Polygons, and Multipatches. Geometries are used by the geodatabase and graphic element systems to define the shapes of features and graphics. They supply operations that are used by the Editor and map symbology systems to define and symbolize features. Spatial References describe where these geometries are located on the earth. They also define the resolution and valid values for the coordinates used by these geometries. Almost every system in ArcObjects uses geometries and spatial references in some way.

To use geometries accurately, consistently and predictably, you need to understand how geometries and spatial references work together.

Introduction to geometry objects

In addition to the top level geometries (Points, Multipoints, Polylines and Polygons), Paths, Rings and Segments serve as building blocks for polylines and polygons. Polylines contain paths and polygons contain rings. Paths and Rings are sequences of vertices connected by segments. A Segment is a parametric function that defines the shape of the curve connecting its vertices. Segment types include CircularArc, Line, EllipticArc, and BezierCurve. In addition to the X and Y coordinates for each vertex in a geometry, additional vertex attributes can be defined: M (measure), Z (elevation) and ID (foreign key). Envelopes describe the spatial extent of other geometries, and GeometryBags provide operations on collections of geometries.

Geometry objects are not meant to be extended by developers.

Multipoint, polyline and polygon geometries have constraints on their shapes. For example, a polygon must have its interior clearly defined and separated from its exterior. When all constraints are satisfied, a geometry is said to be simple. When a constraint is violated, or it is not known if the constraint is met, then the geometry is said to be non-simple. The ITopologicalOperator, IPolygonN and IPolylineN interfaces provide operations for testing and enforcing simplicity. The SDK documentation for the Simplify method of the ITopologicalOperator describes these rules precisely.

Each vertex of a geometry, in addition to its X and Y coordinates, can optionally have additional attributes, called vertex attributes. The Z vertex attribute is a double precision value that can be used to represent heights or depths relative to a vertical coordinate system. The M vertex attribute, also called a measure, is a double precision value that can be used to establish a linear reference system on a geometry (usually a polyline), such as the exits along a highway. The ID vertex attribute, also called a point ID, is a signed integer that can be used as a foreign database key to associate additional information with each vertex such as survey measurements. Vertex attributes can be added to or removed from any geometry at any time and in any combination. For example, a polyline could start out with no vertex attributes, have Zs added to it, then have IDs added to it, then have its Zs removed. When a geometry is aware of its vertex attributes, those attributes will be persisted as part of the geometry, and will be included in the output of topological operations that involve that geometry. If a geometry is not aware of its attributes, then those attributes will be ignored when the geometry is persisted and the attributes will not appear in the output of a topological operation involving that geometry. The attribute awareness of a geometry is controlled by one of the interfaces IZAware, IMAware, and IPointIDAware. The attribute values are not actually removed from a geometry if its awareness is disabled. One of the polyline examples below illustrates use of the point ID attribute.

Geometries, especially the segment types, have a rich set of methods for defining their location. For examine, look at the IConstructCircularArc interface to see the different ways in which you can define a circular arc segment. Typically, interfaces or methods that include the word ‘Construct’ in their name use a set of input parameters (including other geometries) to completely define the target geometry. The inputs are not altered.

Top level geometries support the classical set-theoretic operations for generating new geometries including union, intersection, difference, and symmetric difference. These operations are exposed on the ITopologicalOperator interface and usually operate on a pair of geometries at a time. ITopologicalOperator::ConstructUnion can operate on more than two. New geometries are created to represent the results. Top level geometries also support the IRelationalOperator interface, which can perform a variety of tests on a pair of geometries such as disjoint, contains, and touches.

Both of these interfaces use the spatial reference associated with the input geometries when determining the answer. Two important properties of a spatial reference (as described next) are its coordinate grid and its XY tolerance. Different values for these properties can cause the relational and topological operators to produce different results.

Introduction to spatial references and coordinate grids

Geometries are georeferenced to the real world through a spatial reference. A spatial reference includes the coordinate system and several coordinate grids. A coordinate system includes such information as the unit of measure, the earth model used and, sometimes, how the data was projected. The coordinate grids define the XY, Z, and M resolution values, the corresponding domain extents and a set of tolerance values. A geometry’s coordinates (or vertex attributes) must fall within the domain extent and be rounded to the resolution. The tolerance values are used by geometric operations that relate coordinates or compute new ones.

XY values can be georeferenced with a geographic or projected coordinate system. A geographic coordinate system (GCS) is defined by a datum, an angular unit of measure usually either degrees or grads, and a prime meridian. A projected coordinate system (PCS) consists of a linear unit of measure usually meters or feet, a map projection, the specific parameters used by the map projection, and a geographic coordinate system. A PCS or GCS can have a vertical coordinate system as an optional property. A vertical coordinate system (VCS) georeferences Z values. A vertical coordinate system (VCS) includes either a geodetic or vertical datum, a linear unit of measure, an axis direction, and a vertical shift. M, or measure, values do not have a coordinate system.

A spatial reference that includes an unknown coordinate system (UCS) includes a grid (domain extent) and a tolerance only. It is not possible to georeference a geometry associated with a UCS. If at all possible, you should not use a UCS. When a GCS or PCS is used, appropriate default XY domain extent, resolution, and tolerance values can be calculated. All grid and tolerance information for coordinates and attributes is associated with the PCS, GCS or UCS. A VCS georeferences Z coordinates but does not have a well-defined default grid.

The resolution and domain extent values determine how a geometry is stored. Resolution values are in the same units as the associated coordinate system. For example, if a spatial reference is using a projected coordinate system with units of meters, the XY resolution value is defined in meters. For many applications, a resolution value of 0.0001 meters (1/10 mm) is appropriate. You should use a resolution value that is at least 10 times smaller than the accuracy of the data. If a coordinate system is unknown, or for Ms, you will have to set appropriate resolution values according to the data without knowing the unit of measure. In addition, the resolution and domain extent values defining the grid of a spatial reference may affect how a geometry is stored in an ESRI geodatabase, or an ESRI supported file data source. For example, coordinates in an ArcSDE hosted layer are snapped to the integer grid defined by the resolution and domain extent:

Persisted coordinate = IPart( (map coordinate – minimum domain extent) / resolution) (1)

Here, the left side of the equation is the integer value that gets stored in the layer. IPart is a function which extracts the integer portion of the real-valued result of the equation. The inverse of this operation is used to recreate map coordinates from those data sources that store coordinates as integer.

In addition to the integerization of map coordinates, ArcSDE and file geodatabases compress the resulting integer coordinates by removing leading zeroes among a few other things. Coordinates with large numeric values after integerization don’t compress as well as those with smaller numeric values, so coordinates clustered near the upper right corner of the domain extent will not compress as well as those near the lower left, or minimum domain extent.

Personal geodatabases persist their coordinates as double precision floating point values, but the values have had equation (1) applied to them and hence have been 'snapped'. Shapefiles also persist double precision values, but they are not snapped to the spatial reference grids.

If you've worked with geometries prior to version 9.2, the resolution values are the inverse of the precision values. The precision values are also known as the xyUnits, zUnits, and mUnits or the scale factor. At 9.2, the terms resolution and precision are often treated as synonyms. There may be parts of the API or documentation that still refer to precision or scale factor when it should use the term resolution instead. The word precision also is used to describe a spatial reference with an enhanced grid, as discussed next.

The coordinate values of a geometry must fall within the appropriate XY, Z, and M domain extents. The largest legal map coordinate value is the upper right of the domain extent. This coordinate value must also be representable as an integer according to (1) above. The domain extents and resolution values are connected. Together they form a grid mesh with the resolution defining the mesh separation, or cell size. Internally, the resolution and domain extents are used to define an integer grid.

At 9.1 and before, each integer coordinate was allotted 31 bits. At 9.2, 53 bits are provided for data sources created and managed by 9.2. Thus for a given resolution, the domain extent can be much larger, or alternatively, for a given domain extent, the resolution can be much smaller than is possible for data using a low precision spatial reference. A data source that stores 53 bit coordinates is always associated with a high precision spatial reference. Existing data sets have not had their spatial references upgraded are always associated with low precision spatial references.

As an example, if the minimum domain value is 0 and the resolution is 1, the maximum domain value for a high precision data source is 9007199254740990 or 253-2. If the resolution is 0.0001, the maximum domain value is 900719925474.0990. These values are for high precision spatial references which are new at version 9.2. Prior to this release, spatial references were what we now call low precision. A low precision spatial reference uses maximum domain values of 2147483647 or 231-1. If the same resolution value of 0.0001 and a minimum domain extent of zero are used, the largest value that a geometry could have is 214748.3647. That is too small for many projected coordinate systems, like UTM and State Plane. So, when you have to work with low precision spatial references, you have to carefully balance the tradeoff between domain extent and the resolution or precision values.

If possible you should always work with high precision spatial references. By default using version 9.2, a new spatial reference is high precision. If you are editing or creating data for a geodatabase (of any type) that has not had its spatial reference upgraded, you will have continue to work with low precision spatial references. New COM interfaces are available on the various spatial reference objects to determine whether one is low or high precision. A new interface is provided on the SpatialReferenceEnvironment object to convert between high and low precision spatial references using certain assumptions.

In addition to the resolution and domain extent properties of a spatial reference’s coordinate grid, the XY tolerance property is also extremely important. The XY tolerance is applied to X and Y coordinates during relational and topological operations. The XY tolerance property of a spatial reference describes the minimum allowable separation between points in a simple geometry. The minimum allowable tolerance value is twice the resolution (or 2.0/scale factor). The Z tolerance property is used when validating topologies in a geodatabase. Different tolerance values can produce different answers for relational and topological operations. For example, two geometries might be classified as disjoint (no points in common) with the minimum tolerance, but a larger tolerance might cause them to be classified as touching.

To achieve precise and predictable results using the geometry library, it is essential that the spatial reference of geometries within a workflow is well defined. When performing a spatial operation using two or more geometries, for example an intersection, the coordinate systems of the two geometries must be equal. If the coordinate systems of the geometries are different or undefined, the operation could produce unexpected results. Prior to version 9.2, the resolution (precision) determined the tolerance value used in a geometry operation. Now, a spatial reference should have explicitly defined tolerance values. In two-at-a-time geometry operations such as the methods found on the IRelationalOperator and ITopologicalOperator interfaces, the tolerance value of the left operand geometry is used. If the tolerance property of the spatial reference is undefined, or there is no spatial reference associated with a geometry, a default grid guaranteed to contain the operands, and the minimum allowable tolerance based on the grid, are used.

The spatial reference is not defined when creating a new instance of a geometry. It is the developer's responsibility to define a spatial reference that makes sense for the geometry and for the operation. If an existing geometry is coming from a feature class then usually the spatial reference is well defined and the geometry can be used directly or projected without problems.

Geometry and Spatial Reference objects

Descriptions and code samples for the majority of objects supplied by the geometry system are given below.

Geometry objects:

Spatial Reference objects:

GeometryEnvironment

GeometryEnviroment provides a way of creating geometries from different inputs and setting or getting global variables for controlling the behavior of geometry methods. It also provides Java friendly versions of methods originally defined on other geometry objects (see the IGeometryBridge and IGeometryBridge2 interfaces). The GeometryEnvironment object is a singleton object, so calling "new" several times doesn't create a new object each time. Instead, it returns a reference to the existing GeometryEnvironment.

Using the Geometry and Spatial Reference environments

This example uses the IGeometryBridge2 interface on the GeometryEnvironment singleton object to define a polyline from an array of WKSPoint structures. It also uses the SpatialReferenceEnvironment singleton object to create a predefined projected coordinate system. Some of the concepts discussed in the introduction above are used here.


public static void geometryEnvironmentTest() throws IOException {
	GeometryEnvironment geomEnvironment = new GeometryEnvironment();
	SpatialReferenceEnvironment spatialReferenceFactory = new SpatialReferenceEnvironment();
	Polyline polyline = new Polyline();

	// Create a projected coordinate system and define its domain, resolution and xy tolerance
	ProjectedCoordinateSystem coordinateSystem = (ProjectedCoordinateSystem)
		spatialReferenceFactory.createProjectedCoordinateSystem(esriSRProjCSType.esriSRProjCS_NAD1983UTM_11N);
	coordinateSystem.constructFromHorizon();
	coordinateSystem.setDefaultXYTolerance();

	polyline.setSpatialReferenceByRef(coordinateSystem);

	// Create an array of WKSPoint structures starting in the middle of the XY domain of the
	// projected coordinate system
	double[] xmin = new double[1];
	double[] xmax = new double[1];
	double[] ymin = new double[1];
	double[] ymax = new double[1];
	coordinateSystem.getDomain(xmin, xmax, ymin, ymax);
	_WKSPoint m = new _WKSPoint();
	m.x = (xmin[0] + xmax[0]) * 0.5;
	m.y = (ymin[0] + ymax[0]) * 0.5;

	_WKSPoint[] wksPoints = new _WKSPoint[10];
	for(int i = 0; i < wksPoints.length; i++) {
		wksPoints[i] = new _WKSPoint();
		wksPoints[i].x = m.x + i;
		wksPoints[i].y = m.y + i;
	}

	geomEnvironment.addWKSPoints(polyline, wksPoints);

}

Envelope

For many applications, the coordinates of a geometry are treated as existing in a planar (Cartesian) coordinate space. An Envelope is a rectangle with sides parallel to that space defining the spatial extent of a geometry. It can also describe the extent of the geometry’s Z, ID and M vertex attributes. You can obtain (copies of) envelopes of other geometries or create envelopes directly. In the first case, the spatial reference of the envelope is the spatial reference of its defining geometry.

Finding the combined extent of two geometries

This example determines the spatial extent of the union of two geometries.


Envelope env1 = (Envelope) feat1.getShape().getEnvelope();
System.out.println(env1.getXMin() + " " + env1.getYMin() + " " + env1.getXMax() + " " + env1.getYMax());

Envelope env2 = (Envelope) feat2.getShape().getEnvelope();
env1.union(env2);

System.out.println(env1.getXMin() + " " + env1.getYMin() + " " + env1.getXMax() + " " + env1.getYMax());

Return to list of objects.

Geometrybag

GeometryBag is a set of references to other geometry objects supporting the IGeometry interface. Objects of any level (Segments, Polylines, or Polygons) can be added to the GeometryBag via the IGeometryCollection interface. However, placing objects of different geometry types may not be suitable when using GeometryBag in some topological operations. For example, a GeometryBag must contain strictly polygons, strictly polylines or strictly envelopes when using it as a parameter to the ITopologicalOperator.constructUnion.

Like other geometries, a geometry bag has a spatial reference property. A geometry added to a bag will reference the same spatial reference as the bag. If the bag has no spatial reference, then neither will the added geometry after it is added to the bag. This is usually an error. Take care to define the spatial reference of the bag before adding geometries to it.

Creating the union of several polygons

This example constructs a polygon representing the topological union of several polygons. The source polygons come from a feature class. References to the polygons are inserted into a geometry bag. The geometry bag then is used as the input parameter to the ConstructUnion method. Note that the spatial reference of the geometry bag is defined before adding geometries to it.


//Set the properties of the spatial filter *here*
SpatialFilter qfilter = new SpatialFilter();
qfilter.setGeometryByRef(fclass.getExtent());

GeometryBag geometryBag = new GeometryBag();
geometryBag.setSpatialReferenceByRef(fclass.getSpatialReference());

FeatureCursor fcursor = new FeatureCursor(fclass.search(qfilter, false));

Feature feat = (Feature) fcursor.nextFeature();
while(feat != null) {
	geometryBag.addGeometry(feat.getShape(), null, null); //Add a reference to this feature’s geometry into the bag
	feat = (Feature) fcursor.nextFeature();
}

// Create the polygon that will be the union of the features returned from the search cursor.
// The spatial reference of this feature does not need to be set ahead of time – the
// ConstructUnion method defines the constructed polygon’s spatial reference to be the same as
// the input geometry bag.
Polygon polygon = new Polygon();
polygon.constructUnion(geometryBag);
System.out.println(polygon.getGeometryCount());

Return to list of objects.

Transformations

The transformation objects can be used to apply various linear coordinate transformations to top-level geometries (Points, Multipoints, Polylines, Polygons). Typically, you create a particular kind of transformation object, define its properties and then pass it to the geometry being transformed in order to perform the transform on that geometry. Only occasionally will you need to extract the points from the geometry and transform them directly, or transform arrays of WKSPoints directly.

AffineTransformation2D

AffineTransformation2D is a 3x3 matrix that implements conformal (angle preserving) affine and general affine transformations. A minimum of 2 pairs of points are required to exactly define a conformal affine transformation. A 2D conformal transformation is also called a Helmert transformation. A minimum of 3 pairs of points are required to define a general affine transformation. Additional points are required to determine RMS error information for the transformation. One use for an AffineTransformation2D is to register a paper map into a known coordinate system when digitizing.

ProjectiveTransformation2D

The projective transformation requires a minimum of four pairs of points to define the transformation. The projective transformation is only used to transform coordinates digitized directly off high altitude aerial photography or aerial photographs of relatively flat terrain assuming that there is no systematic distortion in the air photos. The projective transformation uses eight parameters.

AffineTransformation3D

The 3D version of AffineTransformation2D is a 4x4 matrix. Supports definition of general affine transformations from control points. It will not determine conformal affine transformations.

Using an affine transformation

This example first uses an affine transformation to transform a digitized geometry into ground (projected) coordinates. The same transformation is then applied to an array of double precision raw coordinate values. You may be interested in the latter approach when transforming large numbers of coordinates coming from a text file, binary file, or some other large source of raw coordinates. This avoids the processing overhead of creating COM Point objects for every coordinate.


public static void affineTransformation2DExample() throws IOException {

	ShapefileWorkspaceFactory shapefileWorkspaceFactory = new ShapefileWorkspaceFactory();
	Workspace wksp = new Workspace(shapefileWorkspaceFactory.openFromFile("C:/Program Files/ArcGIS/java/samples/data/usa", 0)); 
	FeatureClass fclass = new FeatureClass(wksp.openFeatureClass("states.shp")); 

	SpatialFilter qfilter = new SpatialFilter();
	qfilter.setGeometryByRef(fclass.getExtent());

	GeometryBag geometryBag = new GeometryBag();
	geometryBag.setSpatialReferenceByRef(fclass.getSpatialReference());

	FeatureCursor fcursor = new FeatureCursor(fclass.search(qfilter, false));

	Feature feat = (Feature) fcursor.nextFeature();
	Polygon poly = (Polygon) feat.getShape();

	//sample
	Point[] digitizerPoints = new Point[10]; //Get the digitizer control point values from somewhere
	Point[] groundPoints = new Point[10]; 	// Get the ground control coordinate values from somewhere
											// (aGroundPoints(i) is the ground point corresponding to aDigitizerPoints(i)

	//boilerplate
	int i = 0;
	for(i = 0; i < poly.getPointCount() && i < 10; i++) {
		groundPoints[i] = (Point) poly.getPoint(i);
	}
	for(i = 10; i < poly.getPointCount() && i < 20; i++) {
		digitizerPoints[i] = (Point) poly.getPoint(i);
	}


	//sample
	AffineTransformation2D transformation = new AffineTransformation2D();
	transformation.defineFromControlPoints(10, digitizerPoints[0], groundPoints[0]);

	double[] fromToRMS = new double[1];
	double[] toFromRMS = new double[1];

	transformation.getRMSError( fromToRMS, toFromRMS );

	if( fromToRMS[0] > 0.05 ) {
	  System.out.println("RMS error is too large; please redigitize control points");
	  return;
	}

	Polygon digitizedGeometry = poly; //Get geometry to be transformed from digitizer input
	//pTransformee.Transform esriTransformForward, pAff2D
	digitizedGeometry.transform(esriTransformDirection.esriTransformForward, transformation );

	//pDigitizedGeometry is now in the destination coordinate system and should be assigned a
	//spatial reference
	//
	//Now we will apply the same transformation directly to an array of double precision values
	//representing (x,y) points.
	//The x,y values are assumed to be interleaved in the array: aFromPoints(1) is the x coordinate
	//for the first point, aFromPoints(2) is the y coordinate, etc.

	double[] fromPoints = new double[50];
	double[][] toPoints = new double[1][50];

	//fromPoints - Read array of points from a file

	
	transformation.transformPointsFF(esriTransformDirection.esriTransformForward, fromPoints, toPoints );

}

Return to list of objects.

Points

A two dimensional point, optionally with measure (M), height (Z), and ID attributes.

Snapping a point to a coordinate grid

This example creates a point, associates it with the spatial reference of a feature class, positions it in the center of the domain of that spatial reference, then snaps its coordinates to the spatial reference’s coordinate grid.


	ISpatialReference spRef = fclass.getSpatialReference();

	double[] xmin = new double[1];
	double[] ymin = new double[1];
	double[] xmax = new double[1];
	double[] ymax = new double[1];

	spRef.getDomain(xmin, xmax, ymin, ymax);

	Point point = new Point();
	point.setSpatialReferenceByRef(spRef);
	point.setX((xmin[0] + xmax[0]) * 0.5);
	point.setY((ymin[0] + ymax[0]) * 0.5);

	System.out.println("Before snapping: " + point.getX() + point.getY());
	point.snapToSpatialReference();
	System.out.println("After snapping: " + point.getX() + point.getY());

Return to list of objects.

Multipoints

An ordered collection of points; optionally has measure (M), height (Z), and ID attributes.The IPointCollection interface implemented by a multipoint object provides direct access to its point elements. This is different than how IPointCollection behaves when that interface is used to provide access to the vertices of a polyline or polygon. In that case, you are working with copies of the points.

Creating a multipoint object from the vertices of a polyline

This example creates a multipoint with point elements being copies of the vertices of an existing polyline. It then offsets those elements 5 units to the right using one transformation method, then offsets them up another 5 units.


	//Set the properties of the spatial filter *here*
	SpatialFilter qfilter = new SpatialFilter();
	qfilter.setGeometryByRef(fclass.getExtent());
	qfilter.setSpatialRel(esriSpatialRelEnum.esriSpatialRelContains);
	qfilter.setGeometryField(fclass.getShapeFieldName());

	GeometryBag geometryBag = new GeometryBag();
	geometryBag.setSpatialReferenceByRef(fclass.getSpatialReference());

	FeatureCursor fcursor = new FeatureCursor(fclass.search(qfilter, false));

	Feature feat = new Feature(fcursor.nextFeature()); 

	Polyline polyline = (Polyline) feat.getShape();
	Multipoint multipoint = new Multipoint();
	multipoint.setSpatialReferenceByRef(polyline.getSpatialReference());
	multipoint.addPointCollection(polyline);

	AffineTransformation2D shiftBy5 = new AffineTransformation2D();
	shiftBy5.move(5, 0);

	multipoint.transform( esriTransformDirection.esriTransformForward , shiftBy5);

	multipoint.move(0, 5);

	multipoint.transform( esriTransformDirection.esriTransformForward , shiftBy5);

Return to list of objects.

Polylines

An ordered collection of paths; optionally has measure (M), height (Z), and ID attributes. The IPointCollection interface on a Polyline manipulates copies of its vertices. Use the IGeometryCollection interface to directly access its paths and the ISegmentCollection interface to directly access its segments. Note that IPointCollection and ISegmentCollection interfaces are also available on Path objects and are characterized the same way.

Polyline structure

Polyline Structure graphic

Creating a multipart polyline

This example uses an existing polyline to define a new, multipart polyline. In the image below, the input polyline is shown in the background and the new polyline is shown in dark green with its vertices marked. Each part of the new polyline is a single segment normal to a segment from the original polyline, incident at its midpoint, and 1/3 its length.

Polyline example

Creating a multiplart polyline graphic


	Polyline newPolyline = new Polyline();
	newPolyline.setSpatialReferenceByRef(polyline.getSpatialReference()); //Always associate new top level geometries with an appropriate spatial reference

	IEnumSegment polylineSegments = polyline.getEnumSegments();
	ISegment[] segment = new ISegment[1];
	int[] partIndex = new int[1];
	int[] segmentIndex = new int[1];
	// Iterate over existing polyline segments using a segment enumerator
	polylineSegments.next(segment, partIndex, segmentIndex);
	while(segment[0] != null) {
		Line line = new Line();

		// Geometry methods with _Query_ in their name expect to modify existing geometries.
		// In this case, the QueryNormal method modifies an existing line segment (line) to be the normal vector to
		// segment at the specified location along segment

		segment[0].queryNormal(esriSegmentExtension.esriNoExtension, 0.5, true, segment[0].getLength() / 3, line );

		Path path = new Path(); //Since each normal vector is not connected to others, create a new path for each one
		path.addSegment(line, null, null);
		newPolyline.addGeometry(path, null, null); //The spatial reference associated with pNewG will be assigned to all incoming paths and segments

		polylineSegments.next(segment, partIndex, segmentIndex);
	}
	// newPolyline now contains the new, multipart polyline

Adding point IDs to a polyline

This example shows how to make an existing polyline point ID aware, and efficiently define ID values for each of its vertices. The example assumes that an edit session exists on the workspace containing the feature class whose features are being iterated over.



	FeatureCursor fcursor = new FeatureCursor(fclass.search(null, false));
	IFeature ifeat = fcursor.nextFeature(); 
	while(ifeat != null) {
		Polyline polyline = (Polyline) ifeat.getShape();
		polyline.setPointIDAware(true); //The polyline is now point ID aware. It will persist its point IDs the next time it is saved.
		IEnumSegment segments = polyline.getEnumSegments();
		//ISegment[] segment = new ISegment[1];
		Line[] segment = new Line[1];
		int[] partIndex = new int[1];
		int[] segmentIndex = new int[1];
		segments.next(segment, partIndex, segmentIndex);
		while(segment[0] != null) {
			segment[0].setIDs(segmentIndex[0], segmentIndex[0] + 1);
			segments.next(segment, partIndex, segmentIndex);
		}
		ifeat.setShapeByRef(polyline);
		ifeat.store();
		ifeat = fcursor.nextFeature();
	}
	//featureWksp.stopEditOperation();
	featureWksp.stopEditing(true);

Return to list of objects.

Polygons

A collection of rings ordered by their containment relationship; optionally has measure (M), height (Z) and ID attributes. Each ring is a collection of segments. The IPointCollection interface on polygons and rings manipulates copies of vertices. Use the IGeometryCollection and ISegmentCollection interfaces to access rings and segments directly.

Polygon structure

Polygon Structure

Building a polygon using segments and points

This example builds two multipart polygons in two different ways. The first polygon is built segment by segment. The second is built by defining its vertices as an array of WKSPoint structures. The first approach gives you the most control if you are using advanced construction techniques or curved segments (circular arcs, bezier curves, etc). The second approach is recommended for efficiently building polygons from bulk coordinate data that have vertices connected with straight lines only. The techniques shown here can also be applied to polyline construction. The first polygon should look something like the following:


private static double PI = 3.14159265358979;


public static void constructPolygons() throws IOException {

	//boilerplate
	SdeWorkspaceFactory sdeWorkspaceFactory = new SdeWorkspaceFactory();
	PropertySet pset = new PropertySet();
	pset.setProperty("SERVER", "hemlock");
	pset.setProperty("INSTANCE", "9210");
	pset.setProperty("USER", "gdb");
	pset.setProperty("PASSWORD", "gdb");
	pset.setProperty("VERSION", "sde.DEFAULT");
	Workspace featureWksp = new Workspace(sdeWorkspaceFactory.open(pset, 0)); 
	featureWksp.startEditing(false);
	//featureWksp.startEditOperation();
	FeatureClass fclass = new FeatureClass(featureWksp.openFeatureClass("GDB.Roads"));
	System.out.println("FeatureClass name = " + fclass.getName());
	FeatureCursor fcursor = new FeatureCursor(fclass.search(null, false));
	IFeature ifeat = fcursor.nextFeature(); 
	ISpatialReference spref = ifeat.getShape().getSpatialReference();

	//sample
	Polygon polygon = new Polygon();
	polygon.setSpatialReferenceByRef(spref); //Always define the spatial reference of new top level geometries

	// Create the segments and rings – if this were a single part polygon we could add
	// segments directly to the polygon and it would create the ring internally.
	// You cannot re-use the same ring object; also, when rings are added to the polygon
	// it takes ownership of them. You cannot then reuse a ring for building another polygon.
	// These same restrictions also apply to segments.

	CircularArc arc = new CircularArc();
	BezierCurve curve = new BezierCurve();
	Ring ring1 = new Ring();
	Ring ring2 = new Ring();
	ring1.addSegment(arc, null, null);
	ring2.addSegment(curve, null, null);

	polygon.addGeometry(ring1, null, null);
	polygon.addGeometry(ring2, null, null);

	// At this point, we have constructed a _shell_ geometry. It consists of one
	// polygon containing two rings, each of which contains one segment.
	// However, the coordinates of those segments have not yet been defined.
	// Because we still have references to those segments, we can define their
	// coordinates now.

	Point point = new Point();
	point.setX(-10);
	point.setY(0);

	arc.putCoordsByAngle(point, 0, 2 * PI, 10);
	Point[] controlPoints = new Point[4];
	for(int i = 0; i < 4; i++) {
		controlPoints[i] = new Point();
	}
	controlPoints[0].setX(10);
	controlPoints[0].setY(0);
	controlPoints[1].setX(10);
	controlPoints[1].setY(10);
	controlPoints[2].setX(20);
	controlPoints[2].setY(10);
	controlPoints[3].setX(10);
	controlPoints[3].setY(0);
	
	curve.putCoords(controlPoints);

	// pPolygon has now been defined. When changing segment coordinates directly
	// like this, we need to be careful to let the top level geometry know that
	// things have changed underneath it, so that it can delete any cached properties
	// that it might be maintaining, such as envelope, length, area, etc.
	//
	// Note that when you use certain methods on the top-level geometry implementation
	// of IGeometryCollection interface, like AddGeometry, it will automatically
	// invalidate any cached properties.

	polygon.geometriesChanged();

	// Build another polygon from a bunch of points. As before, we will assume that
	// two parts (rings) need to be created. If the polygon was single part, we could
	// add the points directly to the polygon without first creating a ring.
	//
	// At 9.2, the recommended way to add arrays of points to a geometry is to use
	// the IGeometryBridge2 interface on the GeometryEnvironment singleton object.

	GeometryEnvironment gBridge = new GeometryEnvironment();

	Polygon pointPolygon = new Polygon();
	pointPolygon.setSpatialReferenceByRef(spref); //Define the spatial reference of the new polygon

	Ring r1 = new Ring();
	int cPoints1 = ring1.getPointCount(); //The number of points in the first part
	_WKSPoint[][] wksPointBuffer = new _WKSPoint[1][cPoints1];
	for (int i=0;i < cPoints1; i++)
		wksPointBuffer[0][i] = new _WKSPoint();

	gBridge.queryWKSPoints(ring1, cPoints1, wksPointBuffer ); //Read cPoints1 into the point buffer
	gBridge.setWKSPoints(r1, wksPointBuffer[0]);

	Ring r2 = new Ring();
	int cPoints2 = ring2.getPointCount(); //The number of points in the first part
	wksPointBuffer = new _WKSPoint[1][cPoints2];
	for(int i=0;i < cPoints2;i++)
		wksPointBuffer[0][i] = new _WKSPoint();

	gBridge.queryWKSPoints(ring2, cPoints2, wksPointBuffer ); //Read cPoints2 into the point buffer
	gBridge.setWKSPoints(r2, wksPointBuffer[0]);

	pointPolygon.addGeometry(r1, null, null);
	pointPolygon.addGeometry(r2, null, null);
	// pPointPoly has now been defined

}

Return to list of objects.

MultiPatch

The MultiPatch geometry type was initially developed to address the needs for a 3D Polygon geometry type–-unconstrained by 2D validity rules. Without eliminating the constraints that rule out 'vertical walls', for example, representing extruded 2D lines and footprint-polygons for 3D visualization would not be possible. Besides eliminating 2D constraints, MultiPatches provide better control over polygon face orientations, and a better definition of polygon face interiors.

MultiPatches have also been extended to provide advanced geometric representations for 3D features. These complex 3D objects can be part of a Synthetic Landscape Model, stored in a geodatabase. The target of these extensions is improved visualization quality.

One key capability is that of supporting per-vertex normals that improve the quality of shading under illumination. Another key capability is that of precise image/texture mapping on to the MultiPatch geometry, via explicit texture coordinates. The geometry of a MultiPatch is in-lined with a GeometryMaterialList, which contains one or more GeometryMaterials. These GeometryMaterials can be a color, a texture (image), or both. The new class for this functionality is GeneralMultiPatchCreator, which is used to construct a MultiPatch.

The relationship between objects used in MultiPatch construction is illustrated below:

MultiPatch OMD excerpt

Another key improvement to MultiPatches is the support of the new Triangles part. This extends the original MultiPatch parts (i.e., Triangle Strip, Triangle Fan, Outer Ring, Inner Ring, First Ring, and Ring). The Triangles part is introduced to complete the range of vertex-based part types and facilitate capturing the output results of different triangle-mesh tessellators or 3D object importers (e.g., from 3D Studio models), which contain non-connected triangles, into a MultiPatch geometry. Of course, developers can also make use of the Triangles part as a useful addition to the initial MultiPatch part types offered.

A single Triangles part represents a collection of triangular faces. Each consecutive triplet of vertices defines a new triangle. The size of a Triangles part must be a multiple of three (see the following diagram).

Triangles illustrationt

Regarding the use of rings, the following are important notes about MultiPatch geometries:

To construct a textured MultiPatch geometry, it is recommended that you use the new interface IGeneralMultiPatchCreator even though the old interface IEncode3DProperties that includes the PackTexture2D method still works. With the introduction of explicit vertex normals and texture coordinates, the usage of IEncode3DProperties is now deprecated.

The following shows a simple example of constructing a textured MultiPatch geometry using a TriangleStrip that resembles a vertical polygon, assuming it is 300 (width) x 100 (height) in size.

public static void testMultiPatch() throws IOException {


	//Prepare the geometry material list:
	GeometryMaterial texture = new GeometryMaterial();
	texture.setTextureImage("C:/Temp/MyImage.bmp");
	GeometryMaterialList materialList = new GeometryMaterialList();
	materialList.addMaterial(texture);

	//Create the multipatch:
	GeneralMultiPatchCreator mpCreator = new GeneralMultiPatchCreator();
	mpCreator.init( 4, 1, false, false, false, 4, materialList);

	// Set the texture coordinates for a panel:
	_WKSPoint txLL = new _WKSPoint();
	txLL.x = 0;
	txLL.y = 1;
	_WKSPoint txLR = new _WKSPoint();
	txLR.x = 1;
	txLR.y = 1;
	_WKSPoint txUR = new _WKSPoint();
	txUR.x = 1;
	txUR.y = 0;
	_WKSPoint txUL = new _WKSPoint();
	txUL.x = 0;
	txUL.y = 0;

	_WKSPointZ pUL = new  _WKSPointZ();
	pUL.x = 0; pUL.y = 0; pUL.z = 0;

	_WKSPointZ pUR = new  _WKSPointZ();
	pUR.x = 300; pUR.y = 0; pUR.z = 0;

	_WKSPointZ pLL = new  _WKSPointZ();
	pLL.x = 0; pLL.y = 0; pLL.z = -1 * 100;

	_WKSPointZ pLR = new  _WKSPointZ();
	pLR.x = 300; pLR.y = 0; pLR.z = -1 * 100;

	// Set up part:
	mpCreator.setPatchType(0, esriPatchType.esriPatchTypeTriangleStrip); //could also use a Ring or a TriangleFan
	mpCreator.setMaterialIndex(0, 0);
	mpCreator.setPatchPointIndex(0, 0);
	mpCreator.setPatchTexturePointIndex(0, 0);
	// Set real world points:
	mpCreator.setWKSPointZ(0, pUR);
	mpCreator.setWKSPointZ(1, pLR);
	mpCreator.setWKSPointZ(2, pUL);
	mpCreator.setWKSPointZ(3, pLL);

	//Set texture points:
	mpCreator.setTextureWKSPoint(0, txUR);
	mpCreator.setTextureWKSPoint(1, txLR);
	mpCreator.setTextureWKSPoint(2, txUL);
	mpCreator.setTextureWKSPoint(3, txLL);

	MultiPatch mpatch = new MultiPatch();
	mpatch = (MultiPatch) mpCreator.createMultiPatch();

}

Return to list of objects.

SpatialReferenceEnvironment

SpatialReferenceEnvironment is a singleton object used for creating, loading, and storing entire spatial references. Spatial References are often cloned and copied internally. Setting up the SpatialReferenceEnvironment as a singleton object conserves resources and makes it less likely that a spatial reference is deleted completely before it is no longer in use. The SpatialReferenceEnvironment can also create predefined components used for building spatial references (projections, datums, prime meridians, etc). Finally, you can use it to convert between low and high precision spatial references. The following examples demonstrate these capabilities.

Using ISpatialReferenceFactory to create a predefined spatial reference system

ArcObjects includes a vast array of predefined spatial reference systems and building blocks for spatial reference systems. Each predefined object is identified by a factory code. Factory codes are defined enumeration sets that begin with 'esriSR'. Use the enumeration macro rather than the integer value it represents. Occasionally, the code value for which an enumeration stands may change. This is because many of the values are from the EPSG database (see http://www.epsg.org) which is becoming an industry standard. This example shows how to create predefined spatial reference objects and use them as input to geodatabase operations.

SpatialReferenceEnvironment spfactory = new SpatialReferenceEnvironment();
ProjectedCoordinateSystem pcoordsys = new ProjectedCoordinateSystem();
spfactory.createProjectedCoordinateSystem(esriSRProjCSType.esriSRProjCS_WGS1984UTM_11N);

The ISpatialReferenceFactory interface provides methods that use the FactoryCode to generate predefined factory spatial reference objects. There are three types of functions on this interface: those that return single objects, those that return a set of objects of the same type, and those that are used to import and export SpatialReference objects to and from a PRJ file or a PRJ string representation. For example, the createGeographicCoordinateSystem function takes as its only parameter an integer that represents the FactoryCode of a predefined geographic coordinate system. The function returns a fully instantiated GCS object that can then be queried for its properties and classes.

Thousands of coordinate system related objects are available through macros. The enumerations all begin with "esriSR". Use the enumeration macro rather than the integer value it represents. Many of the FactoryCode values are based on an external standard and the values may change.

The next type of function on the ISpatialReferenceFactory interface returns a complete Set of objects. For example, the following code shows how the createPredefinedProjections function returns a set that contains all the available Projection objects. The set is iterated through, and the name of each Projection with the set is obtained. These type of functions are useful for developers who may wish to populate a pulldown selection list of available SpatialReference objects.

SpatialReferenceEnvironment spfactory1 = new SpatialReferenceEnvironment();
Set projectionSet = (Set) spfactory1.createPredefinedProjections();
System.out.println(projectionSet.getCount());
projectionSet.reset();
for(int i = 0; i < projectionSet.getCount(); i++) {
	IProjection projection = (IProjection) projectionSet.next();
	System.out.println(projection.getName());
}

The third type of function supported by ISpatialReferenceFactory deals with PRJ files and strings. createESRISpatialReferenceFromPRJFile takes an old or new style PRJ file and creates either a geographic or projected coordinate system from it, depending on the file contents. The old style PRJ is used with coverages, TINs, and GRIDs. createESRISpatialReferenceFromPRJ is used to create a SpatialReference based on the string buffer of an old style PRJ file. While createESRISpatialReference is similar, the string buffer must be in the format of a new PRJ file. This code sample shows how to create a SpatialReference coordinate system directly from a PRJ file (both old and new style files are supported):

SpatialReferenceEnvironment spfactory2 = new SpatialReferenceEnvironment();
ProjectedCoordinateSystem projCoordSys = (ProjectedCoordinateSystem) spfactory2.createESRISpatialReferenceFromPRJFile("C:/ArcGIS/java/samples/data/linrear_ref/county.prj");

Using ISpatialReferenceFactory to import and export a spatial reference system

This example creates a predefined projected coordinate system, defines its coordinate grid and tolerance values, then exports and imports it 2 different ways. First it exports the coordinate system to a .prj file, then uses the contents of the .prj file to create a second projected coordinate system.

At the end of this process you might expect that both projected coordinate systems will be identical, but they aren’t. PRJ files do not store coordinate grid information, so recreating a spatial reference from a PRJ file will lose any coordinate grid and tolerance information that might have been defined for it.

Complete equality of spatial references (equal coordinate systems and equal coordinate grids) cannot be checked with one method. IClone.isEqual compares coordinate systems but not coordinate grids. You need to use other methods to do the latter.

  static void importExportSR_Example() throws Exception {
	//Instantiate a predefined spatial reference and set its coordinate grid information
	SpatialReferenceEnvironment srFactory = new SpatialReferenceEnvironment();
	ProjectedCoordinateSystem pcSystem = (ProjectedCoordinateSystem) srFactory.createProjectedCoordinateSystem(esriSRProjCSType.esriSRProjCS_WGS1984UTM_10N);
	pcSystem.constructFromHorizon();
	pcSystem.setDefaultXYTolerance();

	//Export the pcs to a prj file
	String fileName = "C:/Program Files/ArcGIS/java/samples/data/linrear_ref/county.prj";
	srFactory.exportESRISpatialReferenceToPRJFile(fileName, pcSystem);

	//Re-hydrate it as a new spatial reference object
	ProjectedCoordinateSystem pcSystem1 = (ProjectedCoordinateSystem) srFactory.createESRISpatialReferenceFromPRJFile(fileName);

	//See if they’re equal
	System.out.println(pcSystem.isEqual(pcSystem1)); //Should be true, but we haven’t checked coordinate grid information yet!
	System.out.println(pcSystem.isXYPrecisionEqual(pcSystem1));//Should be false, PRJ files do not persist coordinate grid information


}
The ISpatialReferenceFactory3 interface has two methods that are useful when working with low and high precision spatial references. ConstructHighPrecisionSpatialReference creates a high precision spatial reference from a low precision spatial reference. Using this method will ensure that coordinate values will fit exactly into the new, denser grid mesh. Each intersection of the original grid is an intersection of the new grid. ConstructLowPrecisionSpatialReference will create a low precision spatial reference from an existing high precision one. You can require that the new resolution value be maintained, possibly at the expense of the XY domain extent.

Constructing a high or low precision spatial reference

SpatialReferenceEnvironment srFactory = new SpatialReferenceEnvironment();
GeographicCoordinateSystem gcSystem = (GeographicCoordinateSystem) srFactory.createESRISpatialReferenceFromPRJFile("C:/ArcGIS/Coordinate Systems/Geographic Coordinate Systems/World/WGS 1984.prj");
gcSystem.setIsHighPrecision(true); //Flip this to false if low precision
gcSystem.constructFromHorizon();//this is the KEY, Construct Horizon THEN
gcSystem.setDefaultXYResolution(); //Set Default
double[] xMin = new double[1];
double[] xMax = new double[1];
double[] yMin = new double[1];
double[] yMax = new double[1];
gcSystem.getDomain(xMin, xMax, yMin, yMax);
System.out.println("Domain " + xMin[0] + " " + xMax[0] + " " + yMin[0] + " " + yMax[0] + " ");

Converting between low and high precision spatial references

This example illustrates one way to convert a pre 9.1 low precision spatial reference to a 9.2 high precision spatial reference. The original spatial reference is not modified. A copy is made and altered.

In general, you can construct a high precision spatial reference in any way that makes sense for your 9.1 data. The approach shown here is used for compatibility with ArcSDE dual-resolution data layers, which have a constraint on the relationship between the low and high precision scale factors.

FeatureClass fclass = new FeatureClass(featureWksp.openFeatureClass("VTEST.Counties2"));
ISpatialReference spRef91 = fclass.getSpatialReference();

double[] fx = new double[1];
double[] fy = new double[1];
double[] s = new double[1];
spRef91.getFalseOriginAndUnits(fx, fy, s);
System.out.println("low precision coordinate grid definition:");
System.out.println("false x: " + fx[0] + ", false y: " + fy[0] + ", scale factor: " + s[0]);

SpatialReferenceEnvironment spFactory = new SpatialReferenceEnvironment();
ISpatialReference spRef92 = spFactory.constructHighPrecisionSpatialReference(spRef91, -1, -1, -1); 



spRef92.getFalseOriginAndUnits(fx, fy, s);
System.out.println("high precision coordinate grid definition:");
System.out.println("false x: " + fx[0] + ", false y: " + fy[0] + ", scale factor: " + s[0]);

Return to list of objects.

GeographicCoordinateSystem

A geographic coordinate system includes a name, angular unit of measure, datum (which includes a spheroid), and a prime meridian. It is a model of the earth in a three dimensional coordinate system. Latitude-longitude, or lat/lon, data is in a geographic coordinate system. You can access the majority of the properties and methods through IGeographicCoordinateSystem interface with a few more properties are available in IGeographicCoordinateSystem2. Although most developers will not need to create a custom geographic coordinate system, the IGeographicCoordinateSystemEdit contains the define and defineEx methods.

The following code demonstrates how to use the define method to create a user-defined geographic coordinate system. The ISpatialReferenceFactory allows you to create the Datum, PrimeMeridian, and AngularUnit component parts. These components can also be created using a similar define method available on their classes.

SpatialReferenceEnvironment srFactory = new SpatialReferenceEnvironment();
Datum datum = (Datum) srFactory.createDatum(esriSRDatumType.esriSRDatum_OSGB1936);
PrimeMeridian meridian = (PrimeMeridian) srFactory.createPrimeMeridian(esriSRPrimeMType.esriSRPrimeM_Greenwich);
AngularUnit angularUnit = (AngularUnit) srFactory.createUnit(esriSRUnitType.esriSRUnit_Degree);
GeographicCoordinateSystem gcSystem = new GeographicCoordinateSystem();
gcSystem.define("UserDefined Geographic Coordinate System", "UserDefined GCS", "UserDefined", "User Defined Geographic Coordinate System based on OSGB1936", "Suitable for the UK", datum, meridian, angularUnit);
System.out.println(gcSystem.hasXYPrecision());

The following code shows how the defineEx method can be used. It uses a SpatialReferenceFactory to create the Datum, PrimeMeridian, and Unit components.

  // Smart pointer variables used
  IDatumPtr ipDatum;
  IPrimeMeridianPtr ipPrimeMeridian;
  IUnitPtr ipUnit;
  IAngularUnitPtr ipAngularUnit;

  // Create the factory and the component parts
  ISpatialReferenceFactoryPtr
  ipFactory(CLSID_SpatialReferenceEnvironment);
  ipFactory->CreateDatum(esriSRDatum_OSGB1936,&ipDatum);
  ipFactory->CreatePrimeMeridian(esriSRPrimeM_Greenwich,&ipPrimeMeridian);
  ipFactory->CreateUnit(esriSRUnit_Degree,&ipUnit);
  IGeographicCoordinateSystemEditPtr
  ipGeoCSEdit(CLSID_GeographicCoordinateSystem);
  IGeographicCoordinateSystemPtr ipGCS;

  // QI for the AngularUnit from the Unit
  // - this is achieved by the SmartPointers
  ipAngularUnit = ipUnit;

  // Make the string descriptions
  CComBSTR name(_T("User Defined Geographic Coordinate System"));
  CComBSTR alias(_T("UserDefined"));
  CComBSTR abbreviation(_T("User"));
  CComBSTR remarks(_T("User Define GCS based on OSGB1936"));
  CComBSTR useage(_T("Suitable for the UK"));

  // Make the call 
HRESULT hr; hr = ipGeoCSEdit->DefineEx(name, alias, abbreviation, remarks, useage, ipDatum, ipPrimeMeridian, ipAngularUnit); // QI for the result ipGCS = ipGeoCSEdit;

To access the hundreds of predefined geographic coordinate systems, ISpatialReferenceFactory has the CreateGeographicCoordinateSystem method. The predefined geographic coordinate systems are listed in the esriSRGeoCSType, esriSRGeoCS2Type, and esriSRGeoCS3Type enumerations. The parts of a geographic coordinate system, such as the datum, angular unit, and prime meridian, are objects as well. All support ISpatialReference2 and ISpatialReferenceFactory. Make use of the predefined objects available in the various esriSR* enumerations.

The IGeographicCoordinateSystem2 interface supplies the AngularConversionFactor method, which will return a value that converts the units of measure between two geographic coordinate systems.

The ExtentHint, LeftLongitude, and RightLongitude properties are interrelated. Usually, data in a geographic coordinate system has longitude values between -180 and 180 if the unit of measure is degrees. Some datasets are designed to use a minimum longitude value of 0 or -360. The LeftLongitude property controls whether the data is considered as -360 to 0, -180 to 180, or 0 to 360. You only need to worry about this is you’re inverse projecting projected coordinates for storage in a GCS-based feature class that has a non-standard longitude range. The ArcObjects framework usually deals with this detail for you. Note that the left longitude property is not considered when comparing two GCS for equality.

GetHorizon returns a WKSEnvelope describing the extent of a geographic coordinate system based on its unit of measure and the LeftLongitude. This method can be used to define a standard coordinate grid for the GCS. It is used internally by the ISpatialReferenceResolution.constructFromHorizon method.

 

Return to list of objects.

ProjectedCoordinateSystem

A projected coordinate system includes a name, linear unit of measure, geographic coordinate system, map projection, and any parameters required by map projection. Using the term 'projection' for a coordinate system is imprecise. The term 'projection' should be used for the actual mathematical function. Transverse Mercator and Lambert Conformal Conic are map projections. UTM and State Plane are projected coordinate systems that are based on particular map projections. Each projected coordinate system must include a geographic coordinate system. Map projection parameters can be linear, angular, or unitless. A unitless parameter includes scale factor and option. Angular parameters are the central meridian, standard parallels and latitude of origin. Linear parameters are false easting and false northing. Use the getDefaultParameters method on the IProjection interface to determine which parameters a particular map projection expects.

The parts of a projected coordinate system, such as the projection, linear unit, and geographic coordinate system, are objects as well. All support ISpatialReference2 and ISpatialReferenceFactory. When defining a custom projected coordinate system, make use of the predefined objects available in the various esriSR* enumerations.

You can access the majority of the properties and methods through IProjectedCoordinateSystem2 interface although a few more properties are available in IProjectedCoordinateSystem3 and IProjectedCoordinateSystem4. The IProjectedCoordinateSystemEdit contains the define method which allows you to define a custom projected coordinate system. To access the hundreds of predefined projected coordinate systems, ISpatialReferenceFactory has the CreateProjectedCoordinateSystem method. The predefined projected coordinate systems are listed in the esriSRProjCSType, esriSRProjCS2Type, esriSRProjCS3Type, and esriSRProjCS4Type enumerations.

The IProjectedCoordinateSystemEdit interface provides you with the define method to create your own PCS object based on parameters such as Name, GeographicCoordinateSystem, projectedUnit, Projection and, if necessary, projection Parameters.

//Create a factory
SpatialReferenceEnvironment srFactory = new SpatialReferenceEnvironment();

//Create a projection, gcs and unit using the factory
Projection projection =  (Projection) srFactory.createProjection(esriSRProjectionType.esriSRProjection_Sinusoidal);
GeographicCoordinateSystem gcSystem = (GeographicCoordinateSystem) srFactory.createGeographicCoordinateSystem(esriSRGeoCSType.esriSRGeoCS_WGS1984);
LinearUnit linearUnit = (LinearUnit) srFactory.createUnit(esriSRUnitType.esriSRUnit_Meter);


//Get the default parameters from the Projection
IParameter[] params = new IParameter[16];

//Create a projected coordinate system using the Define method
ProjectedCoordinateSystem pcSystem = new ProjectedCoordinateSystem();
pcSystem.define("Newfoundland", "NF_LAB", "NF", "Most Eastern Province in Canada",
		"When making maps of Newfoundland", gcSystem, linearUnit, projection, params);

System.out.println(pcSystem.hasXYPrecision());

Return to list of objects.

Parameter

Parameters are required by both projected coordinate systems and geographic transformations. For example, to define a Lambert Azimuthal Equal Area projected coordinate system, only the central meridian and latitude of origin parameters are required by the mathematical algorithm that actually performs the projection.

The IParameter interface has an Index and a Value. The value is self-explanatory and refers to the internal array that holds the parameters for a projected coordinate system or a geographic transformation. The ISpatialReferenceFactory can be used to create new parameters. Here is an example of how to use the createParameter method and the esriSR_ParameterType enumeration. The SpatialReferenceFactory provides default values for each type of parameter. The values can easily be changed.

SpatialReferenceEnvironment srFactory = new SpatialReferenceEnvironment();
IParameter parameter = srFactory.createParameter(esriSRParameterType.esriSRParameter_LatitudeOfOrigin);
System.out.println(parameter.getName());
System.out.println(parameter.getIndex());
System.out.println(parameter.getValue());
parameter.setValue(45);
System.out.println(parameter.getValue());

The following code demonstrates how to get the parameters from a projected coordinate system. It assumes that the projected coordinate is already defined. These parameters are passed to the client by reference; it is then possible to modify the value of the parameters directly. If this is done, then the isChanged method on the ProjectedCoordinateSystem must be called.

// pPcs is the projected coordinate system object
ProjectedCoordinateSystem pCS = new ProjectedCoordinateSystem();
// Create an array of IParameters with 16 elements
IParameter[][] params = new IParameter[1][16];
pCS.getParameters(params);
for(int i = 0; i < 16; i++) {
	IParameter param = params[0][i];
	if(param != null) {
		System.out.println(param.getName() + " " + param.getIndex() + " " + param.getValue());
	}
}

The following code shows how to change a Parameter using the same variables.

// Get the Central Meridian Parameter
IParameter param = params[0][2];

if (param != null) {
	// Set the new value
	param.setValue(123);
	// Tell the projected coordinate system that it has changed
	pCS.changed();
}

The following example uses the getDefaultParameters method on the IProjection interface to retrieve a set of the required parameters for a projection. Next, set some values and create a new projected coordinate system using these parameters, then make a call to IProjectedCoordinateSystem.getParameters to verify that the parameters have been set.

SpatialReferenceEnvironment srFactory1 = new SpatialReferenceEnvironment();
// Create a projection, gcs and unit using the factory
Projection projection =  (Projection) srFactory.createProjection(esriSRProjectionType.esriSRProjection_Sinusoidal);
GeographicCoordinateSystem gcSystem = (GeographicCoordinateSystem) srFactory.createGeographicCoordinateSystem(esriSRGeoCSType.esriSRGeoCS_WGS1984);
LinearUnit linearUnit = (LinearUnit) srFactory.createUnit(esriSRUnitType.esriSRUnit_Meter);

// Get the default parameters from the Projection
IParameter[] params1 = projection.getDefaultParameters();

// Iterate through the Parameters and print out their name and value
for(int i = 0; i < params1.length; i++) {
	IParameter param1 = params1[i];
	if(param1 == null) {
		System.out.println(param1.getName() + " " + param1.getIndex() + " " + param1.getValue());
	}
}

// Reset one of the parameter values
params1[2].setValue(45);

//generate default params for the projection to be created
IParameter params_v[] = new IParameter[16];

//Create a projection, gcs and unit using the factory
Projection projection1 =  (Projection) srFactory.createProjection(esriSRProjectionType.esriSRProjection_Sinusoidal);
GeographicCoordinateSystem gcSystem1 = (GeographicCoordinateSystem) srFactory.createGeographicCoordinateSystem(esriSRGeoCSType.esriSRGeoCS_WGS1984);
LinearUnit linearUnit1 = (LinearUnit) srFactory.createUnit(esriSRUnitType.esriSRUnit_Meter);


//Create a projected coordinate system using the Define method
ProjectedCoordinateSystem pcSystem = new ProjectedCoordinateSystem();
pcSystem.define("Newfoundland", "NF_LAB", "NF", "Most Eastern Province in Canada",
		"When making maps of Newfoundland", gcSystem1, linearUnit1, projection1, params_v[0]);

// Get the parameters from your new projected coordinate system and verify
// that the parameter value was changed.
IParameter[][] params2 = new IParameter[1][16];
pcSystem.getParameters(params2);

// Iterate through the Parameters and print their name and value
for(int i = 0; i < 16; i++) {
	IParameter param2 = params[0][i];
	if(param != null) {
		System.out.println(param2.getName() + " " + param2.getIndex() + " " + param2.getValue());
	}
}

Return to list of objects.

 

VerticalCoordinateSystem

A vertical coordinate system includes a name, linear unit of measure, a vertical or geodetic (horizontal) datum, a direction, and optionally, a vertical shift. A vertical coordinate system defines the origin of Z coordinate values. A common application is for Z values to represent elevations or depths. If Z values increase 'up' (against the direction of gravity) or 'down' (in the direction of gravity). You can access the majority of the properties and methods through IVerticalCoordinateSystem interface. Although most developers will not need to create a custom vertical coordinate system, the IVerticalCoordinateSystemEdit contains the define method.

The following two code samples demonstrate how to use the define method to create user-defined vertical coordinate systems. The ISpatialReferenceFactory interface allows you to create the Datum, VerticalDatum, and LinearUnit component parts. These components can also be created using a similar define method available on their classes. The first sample creates a gravity-related vertical coordinate system that uses a vertical datum, while the second one creates an ellipsoid-based vertical coordinate system.

// Creates a gravity-related vertical coordinate system.
VerticalCoordinateSystem vcSystem = new VerticalCoordinateSystem();
SpatialReferenceEnvironment srFactory = new SpatialReferenceEnvironment();
VerticalDatum verticalDatum = (VerticalDatum) srFactory.createVerticalDatum(esriSRVerticalDatumType.esriSRVertDatum_Taranaki);

// Because a VCS can be based upon Datum or VerticalDatum, IHVDatum is used
// when defining a vertical coordinate system.
LinearUnit unit = (LinearUnit) srFactory.createUnit(esriSRUnitType.esriSRUnit_Meter);

// The Direction is set to -1 and the VerticalShift is set to 40.
vcSystem.define("New VCoordinateSystem", "VCoordinateSystem alias", "abbr",
		"Test for options", "New Zealand", verticalDatum, unit, new Integer(40), new Integer(-1)); 

//	Creates an ellipsoid-based vertical coordinate system
VerticalCoordinateSystem vcSystem1 = new VerticalCoordinateSystem();
SpatialReferenceEnvironment srFactory1 = new SpatialReferenceEnvironment();
Datum verticalDatum1 = (Datum) srFactory.createDatum( esriSRDatumType.esriSRDatum_WGS1984);

// Because a VCS can be based upon Datum or VerticalDatum, IHVDatum is used
// when defining a vertical coordinate system.
LinearUnit unit1 = (LinearUnit) srFactory.createUnit(esriSRUnitType.esriSRUnit_Foot);

// The Direction is set to -1 and the VerticalShift is set to 40.
vcSystem.define("WGS84 vcs", "WGS84 ellipsoid", "w84 3d", "WGS84 ell-based vcs",
		"everywhere!", verticalDatum1, unit1, new Double(0.4839), new Double(-1));

To access the predefined vertical coordinate systems and datums, ISpatialReferenceFactory3 has the createVerticalCoordinateSystem and createVerticalDatum methods. The predefined vertical datums and coordinate systems are listed in the esriSRVerticalDatumType and esriSRVerticalCSType enumerations.

Return to list of objects.

 

GeoTransformations

Moving your data between projected coordinate systems may also involve transforming between geographic coordinate systems. Because the geographic coordinate systems contain datums that are based on spheroids, a geographic transformation also changes the underlying spheroid. Other frequently used terms for a geographic transformation include datum shift and geodetic transformation.

A geographic transformation is a mathematical operation that takes the coordinates of a point in one geographic coordinate system and returns the coordinates of the same point in another geographic coordinate system. There is also an inverse transformation to allow coordinates to be put back to the first coordinate system from the second. There are many different types of mathematical operations used to achieve this task.

To illustrate the above, consider the following scenario. You have a known geographic position in the State of Kansas: 97.32 W, 37.68 N. This same location, when displayed with UTM Grid Zone 14N for Kansas (based on the NAD1927 geographic coordinate system), will have planar coordinates of 648147.22m E, 4171434.25m N. The exact same geographic location when using the UTM Grid Zone 14N (based on the NAD1983 geographic coordinate system) will have planar coordinates of 648115.09m E, 4171640.19m N. This is a difference of -12.13 meters in the Eastings and 204.86 meters in the Northings.

Thus, if you have two datasets that are projected, they may be on different projected coordinate systems, and their respective coordinate systems may be based on different geographic coordinate systems. It might not be enough to simply change the parameters of the projected coordinate system. You may experience a misalignment between the two datasets even when both are displayed using a common projected coordinate system. The magnitude of the error will vary depending on the geographic coordinate systems used and the relative accuracy of the data. A geographic transformation should minimize these inaccuracies.

Older geographic coordinate systems are usually 'local' systems. They are designed for a particular region or country. As measuring techniques and data became available, new geographic coordinate systems have often been defined for the same area. A geographic transformation converts data between two geographic coordinate systems. A GeoTransformation includes a name, two geographic coordinate systems (from and to), a method or type, and any parameters required for the method. Each method or type is a class. The available transformation types are:

The interfaces on these classes allow you to set any parameters needed by the transformation type. The HARN, NADCON, and NTv2 transformations are grid-based. They use a file on-disk to calculate the data shifts. Use the IGridTransformation interface to access the on-disk information.

Use the IGeoTransformation interfaces to set the from and to spatial references. A GeoTransformation is always defined in a particular direction--FROM geographic coordinate system 1 TO geographic coordinate system 2. The projectEx method on IGeometry2 has a direction parameter. Set it to esriTransformForward if you want apply the GeoTransformation in the order that it is defined. If you want to apply it in the opposite direction, use esriTransformReverse.

To access the hundreds of predefined geographic transformations, ISpatialReferenceFactory has the createGeoTransformation method. The predefined geographic transformations are listed in the esriSRGeoTransformationType, esriSRGeoTransformation2Type, and esriSRGeoTransformation3Type enumerations. The following code illustrates creating a geotransformation using the spatial reference factory and the getSpatialReferences methods.

SpatialReferenceEnvironment srFactory = new SpatialReferenceEnvironment();
IGeoTransformation geoTransformation = (IGeoTransformation) srFactory.createGeoTransformation(esriSRGeoTransformationType.esriSRGeoTransformation_OSGB1936_To_WGS1984_1);
ISpatialReference[] fromSR = new ISpatialReference[1];
ISpatialReference[] toSR = new ISpatialReference[1];
geoTransformation.getSpatialReferences(fromSR, toSR);
System.out.println(fromSR[0].getName() + toSR[0].getName());

A CompositeGeoTransformation is a multistep geotransformation. Use the ICompositeGeoTransformation interface when you need to define a geographic transformation operation that requires the use of an intermediate geographic coordinate system. For example, no direct path from one geographic coordinate system to another exists, but you can use a third geographic coordinate system to get from one to another. For example, Cameroon uses both the Adindan and Minna geographic coordinate systems. While it is not possible to convert directly between Adindan and Minna, you can convert both to WGS 1984. So, you can go from Adindan to WGS 1984, and then from WGS 1984 to Minna. The transformation method actually goes from Minna to WGS 1984, so you need to state that you want to go in the reverse direction (WGS 1984 to Minna). To specify the direction of a transformation, use the esriTransformDirection enumerations (esriTransformForward and esriTransformReverse).

The forward direction through this composite geotransformation takes you from Adindan to Minna. The reverse direction through this geotransformation takes you from Minna to Adindan. This composite geotransformation has two components. That is, it has two (direction, geotransformation) pairs:

Going forward through the composite geotransformation is the same as going forward through the first component geotransformation and backward through the second component geotransformation. Going reverse through the composite geotransformation is the same as going forward through the second geotransformation and backward through the first geotransformation.

In other words, you need the component directions and component ordering to define what forward and reverse mean at the composite level. The order is important. Once you build the composite transformation, it acts just like a regular transformation.