Hi, with this mail I'm trying to summarize a few findings of mine on how to deal with 3D vector data, and in particular with the 2D+1 subset of it, in Geotools.
With 2D+1 data I mean the usual JTS geometry types, whose coordinates do have a not null, nor NaN, Z component. Expanding a bit that might also include the usual measured geometries, so include the xyz, xym, xyzm cases that both shapefiles and postgis do handle. The main issues trying to deal with more dimension are dealing the the CRS representation, the transformations, and the envelopes. The cleanest path seemed to have a real 3D CRS and thus stick the information about the dimensionality in the CRS itself. CRS wise there are two kinds of 3D systems, 3d geographic, like 4327, and compounds, that do take a 2d crs and stick a vertical component onto it. Here are two examples of them: EPSG:4327, the 3d brother of 4326 (but unusable due to its DMS unit of measure): GEOGCS["WGS 84 (geographic 3D)", DATUM["World Geodetic System 1984", SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], AUTHORITY["EPSG","6326"]], PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], UNIT["DMS", 0.00000484813681109536], AXIS["Geodetic longitude", EAST], AXIS["Geodetic latitude", NORTH], AXIS["Ellipsoidal height", UP], AUTHORITY["EPSG","4327"]] EPSG:7401, a compound one: COMPD_CS["NTF (Paris) / France II + NGF Lallemand", PROJCS["NTF (Paris) / France II", GEOGCS["NTF (Paris)", DATUM["Nouvelle Triangulation Francaise (Paris)", SPHEROID["Clarke 1880 (IGN)", 6378249.2, 293.4660212936269, AUTHORITY["EPSG","7011"]], AUTHORITY["EPSG","6807"]], PRIMEM["Paris", 2.5969213, AUTHORITY["EPSG","8903"]], UNIT["grade", 0.015707963267948967], AXIS["Geodetic latitude", NORTH], AXIS["Geodetic longitude", EAST], AUTHORITY["EPSG","4807"]], PROJECTION["Lambert Conic Conformal (1SP)", AUTHORITY["EPSG","9801"]], PARAMETER["central_meridian", 0.0], PARAMETER["latitude_of_origin", 52.0], PARAMETER["scale_factor", 0.99987742], PARAMETER["false_easting", 600000.0], PARAMETER["false_northing", 2200000.0], UNIT["m", 1.0], AXIS["Easting", EAST], AXIS["Northing", NORTH], AUTHORITY["EPSG","27582"]], VERT_CS["NGF Lallemand", VERT_DATUM["Nivellement General de la France - Lallemand", 2005, AUTHORITY["EPSG","5118"]], UNIT["m", 1.0], AXIS["Gravity-related height", UP], AUTHORITY["EPSG","5719"]], AUTHORITY["EPSG","7401"]] So, the basic idea was to use a real 3D CRS and have everything else learn how to handle it. Unfortunately I've stumbled into a number of issues: - all of our code uses ReferencedEnvelope, which is limited to 2D data. Basically, every time there is code trying to build one we should take care of using CRS.getHorizontalCRS to extract only the 2D part of it before building or transforming a ReferencedEnvelope (this means, many, many places, not only datastores, but also wrappers, rendering, and in GeoServer, UI and configuration). Either that, or stop using ReferencedEnvelope altogheter and use a n-dimensional ready alternative instead - a few parts of our code try to invert MathTransform objects. This is usually related to computing the source bounds to be queries given the destination bounds. Too bad a 3d -> 2d transformation (the common case in rendering) is not invertible (due to information loss). - the referencing subsystem has issues of its own trying to build transformations that go from a compound CRS to another CRS that does not share the same horizontal component. For example, using the above compound srs, the following code will fail on the last statemetn asserting there is no transformation between f3d and wgs84: CoordinateReferenceSystem wgs84 = CRS.decode("EPSG:4326"); CoordinateReferenceSystem f2d = CRS.decode("EPSG:27582"); CoordinateReferenceSystem f3d = CRS.decode("EPSG:7401"); System.out.println(CRS.findMathTransform(f3d, f2d)); System.out.println(CRS.findMathTransform(f2d, wgs84)); System.out.println(CRS.findMathTransform(f3d, wgs84)); - the same goes if we try to compute the transformation between two system with a different vertical component (that consistently fails), that always consistently fails So, in the end I was left wondering, what are people already dealing with this use case doing? What are postgis, shapefiles, OGR, GML and CityGML doing? A cursory investigation shows that a number of them always reports a 2D srs and provides 3d coordinates, and has a way to tell the user what the number of dimensions are independent of the CRS itself. Examples: - I have a couple shapefiles provided by people interested in 2d+1 data support, and generated by ESRI tools. The .prj contains a 2D srs, the data in the shapefile is arcz or polyz - postgis geometry_columns has an explicit coord_dimension column, and the spatial_ref_sys table has no 3d reference systems - gml3 posList has both an explicit srsName and a srsDimension attribute, some CityGML 1.0 examples available online miss the srsName completely but do specify the srsDimension one (others do miss both of them). Mind that the feature collection envelope is specified in 3d as well. As an example, see: http://demo.snowflakesoftware.com:8080/hangar_terrain/GOPublisherWFS?service=wfs&version=1.1.0&request=GetFeature&maxfeatures=10&typename=Building (32617 is UTM 17N, a 2d CRS) - ogr2ogr is able to generate 3d kml or 3d gml out of a xyz shapefile. Given the following code: ogr2ogr -f kml -s_srs "EPSG:2907" -t_srs "EPSG:4326" 3dfloor.kml 3Dfloor.shp the output is a kml file with the z untouched, and the planar coordinates transformed. Same goes for GML2 output, that declared an srs of 4326 but uses coordinates with 3 dimensions - Oracle spatial started to have real 3d coordinate systems in 11G, before it did 2.5d with flat CRS too (and still does in 11G unless you explicitly ask for full 3D systems). So, it seems the rest of the world is doing 3D by declaring 2D srs and just using 3d coordinates... can't we do the same? ;-) How do we do the same, thought? A few ideas to store the dimensions: - JTS CoordinateSequence does have a getDimension method. We can use custom coordinate sequences that allow us to specify the dimensionality there - GeometryDescriptor could contain a "well known" COORDINATE_DIMENSION hint inside its userData section For what coordinate handling is concerned, we can finally make use of the FEATURE_2D hint we already have to make clients ask for flattened data when needed, and use 3d otherwise. As for geometry transformations are concerned, we should rewrite the transformation code so that it transforms the flat part but preserves the z and m if available (the current code will wipe them out instead). Opinions? Cheers Andrea -- Andrea Aime OpenGeo - http://opengeo.org Expert service straight from the developers. ------------------------------------------------------------------------------ Register Now & Save for Velocity, the Web Performance & Operations Conference from O'Reilly Media. Velocity features a full day of expert-led, hands-on workshops and two days of sessions from industry leaders in dedicated Performance & Operations tracks. Use code vel09scf and Save an extra 15% before 5/3. http://p.sf.net/sfu/velocityconf _______________________________________________ Geotools-devel mailing list Geotools-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/geotools-devel