[SCM] PostGIS branch master updated. 3.7.0beta1-222-gb8320b5e3d
git at osgeo.org
git at osgeo.org
Tue Aug 4 08:39:40 PDT 2026
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "PostGIS".
The branch, master has been updated
via b8320b5e3d58da416a098306ba573857bdf61e14 (commit)
via 253bfa1ab219c774c37c025759a20a3ea977d578 (commit)
via 097d0d11d02d4d1ad5f30e2dd84e54c4a9dc2d64 (commit)
from b2e9f1729fd4ffae2b0b60a5012c7f8fc6d27900 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit b8320b5e3d58da416a098306ba573857bdf61e14
Author: Regina Obe <lr at pcorp.us>
Date: Tue Aug 4 11:39:34 2026 -0400
Update pot
diff --git a/doc/po/templates/postgis-manual.pot b/doc/po/templates/postgis-manual.pot
index d0ecd16c2d..702dab7ae6 100644
--- a/doc/po/templates/postgis-manual.pot
+++ b/doc/po/templates/postgis-manual.pot
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
-"POT-Creation-Date: 2026-07-20 08:06+0000\n"
+"POT-Creation-Date: 2026-08-04 15:37+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
"Language-Team: LANGUAGE <kde-i18n-doc at kde.org>\n"
@@ -368,9 +368,9 @@ msgstr ""
#. Tag: para
#, no-c-format
msgid ""
-"Supplementary instructions for windows users are available at <link xlink:"
-"href=\"http://trac.osgeo.org/postgis/wiki/UsersWikiWinUpgrade\">Windows Hard "
-"upgrade</link>."
+"Supplementary instructions for windows users are available in the <link "
+"xlink:href=\"https://postgis.net/documentation/getting_started/"
+"install_windows/\">PostGIS Windows installation guide</link>."
msgstr ""
#. Tag: para
@@ -938,6 +938,52 @@ msgid ""
"linkend=\"postgis_installation\"/>"
msgstr ""
+#. Tag: title
+#, no-c-format
+msgid "Building Faces from Linework"
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"This example builds topology faces from noded linework and associates point "
+"attributes with the resulting faces. It uses <xref "
+"linkend=\"ST_CreateTopoGeo\"/> to populate an empty topology, <xref "
+"linkend=\"ValidateTopology\"/> to check the result, <xref "
+"linkend=\"GetFaceByPoint\"/> to locate each attribute point, and <xref "
+"linkend=\"ST_GetFaceGeometry\"/> to materialize ordinary polygon geometries."
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"Create an empty topology and populate it with linework that encloses two "
+"faces:"
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"An empty result from <function>ValidateTopology</function> means that no "
+"topology errors were found. Attribute points can now be matched to faces. A "
+"face identifier of <literal>0</literal> denotes the universal face, so the "
+"point is outside every bounded face."
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"Before copying attributes into a face table, check both for points outside "
+"bounded faces and for multiple source points assigned to the same face:"
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"Use <xref linkend=\"DropTopology\"/> to remove the example topology when it "
+"is no longer needed:"
+msgstr ""
+
#. Tag: title
#, no-c-format
msgid "Topology Primitive Tables"
@@ -4502,20 +4548,20 @@ msgstr ""
#. Tag: para
#, no-c-format
msgid ""
-"This section includes general compilation instructions, if you are compiling "
-"for Windows etc or another OS, you may find additional more detailed help at "
-"<link xlink:href=\"https://trac.osgeo.org/postgis/wiki/"
-"UsersWikiInstall\">PostGIS User contributed compile guides</link> and <link "
-"xlink:href=\"http://trac.osgeo.org/postgis/wiki/DevWikiMain\">PostGIS Dev "
-"Wiki</link>."
+"This section includes general compilation instructions. If you are compiling "
+"for Windows or another operating system, see the <link xlink:href=\"https://"
+"postgis.net/documentation/getting_started/\">PostGIS getting started guides</"
+"link> and the <link xlink:href=\"https://gitea.osgeo.org/postgis/postgis/src/"
+"branch/master/doc/development/environment/\">development environment guides</"
+"link>."
msgstr ""
#. Tag: para
#, no-c-format
msgid ""
-"Pre-Built Packages for various OS are listed in <link xlink:href=\"https://"
-"trac.osgeo.org/postgis/wiki/UsersWikiPackages\">PostGIS Pre-built Packages</"
-"link>"
+"Pre-Built Packages for various OS are listed in the <link xlink:"
+"href=\"https://postgis.net/documentation/getting_started/\">PostGIS getting "
+"started guides</link>"
msgstr ""
#. Tag: para
@@ -4968,8 +5014,8 @@ msgstr ""
#. Tag: para
#, no-c-format
msgid ""
-"If you obtained PostGIS from the <link xlink:href=\"https://trac.osgeo.org/"
-"postgis/wiki/CodeRepository\"> code repository </link> , the first step is "
+"If you obtained PostGIS from the <link xlink:href=\"https://postgis.net/"
+"development/source_code/\"> code repository </link> , the first step is "
"really to run the script"
msgstr ""
@@ -5648,6 +5694,24 @@ msgid ""
"common performance problems."
msgstr ""
+#. Tag: term
+#, no-c-format
+msgid "If you are connecting a desktop GIS"
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"Use the client's current database documentation for its user interface and "
+"supported geometry types. For example, the <link xlink:href=\"https://docs."
+"qgis.org/latest/en/docs/training_manual/databases/db_manager.html\">QGIS DB "
+"Manager lesson</link> covers PostgreSQL connections, table layers, SQL query "
+"layers, import, and export. A query layer should return a stable unique "
+"identifier and a geometry column with a known SRID and geometry type. "
+"Expensive read-only queries can be exposed through a view or materialized "
+"view and indexed as appropriate."
+msgstr ""
+
#. Tag: term
#, no-c-format
msgid "If you need a particular function"
@@ -14063,7 +14127,18 @@ msgstr ""
#. Tag: para
#, no-c-format
-msgid ", <xref linkend=\"ST_AsBinary\"/>, <xref linkend=\"ST_GeomFromEWKB\"/>"
+msgid ""
+"The PostgreSQL <link xlink:href=\"https://www.postgresql.org/docs/current/"
+"functions-binarystring.html\"><function>decode</function></link> function "
+"converts a hexadecimal WKB string to the <type>bytea</type> value accepted "
+"by <function>ST_GeomFromWKB</function>."
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+", <xref linkend=\"ST_AsBinary\"/>, <xref linkend=\"ST_AsEWKT\"/>, <xref "
+"linkend=\"ST_GeomFromEWKB\"/>"
msgstr ""
#. Tag: refpurpose
@@ -14820,8 +14895,9 @@ msgstr ""
#. Tag: para
#, no-c-format
msgid ""
-", <xref linkend=\"ST_DWithin\"/>, <xref linkend=\"ST_Length2D\"/>, <xref "
-"linkend=\"ST_LineInterpolatePoint\"/>, <xref linkend=\"ST_LineSubstring\"/>"
+", <xref linkend=\"ST_ClosestPoint\"/>, <xref linkend=\"ST_DWithin\"/>, <xref "
+"linkend=\"ST_Length2D\"/>, <xref linkend=\"ST_LineInterpolatePoint\"/>, "
+"<xref linkend=\"ST_LineSubstring\"/>"
msgstr ""
#. Tag: refpurpose
@@ -18026,6 +18102,16 @@ msgstr ""
msgid "&sqlmm_compliant; SQL-MM 3: 5.1.37"
msgstr ""
+#. Tag: para
+#, no-c-format
+msgid ""
+"Use the PostgreSQL <link xlink:href=\"https://www.postgresql.org/docs/"
+"current/functions-binarystring.html\"><function>encode</function></link> "
+"function to produce a hexadecimal WKB string without the <literal>\\x</"
+"literal> prefix used for <type>bytea</type> display. Specifying "
+"<literal>NDR</literal> makes the byte order deterministic."
+msgstr ""
+
#. Tag: para
#, no-c-format
msgid ""
@@ -18650,6 +18736,13 @@ msgstr ""
msgid "Decimal degrees."
msgstr ""
+#. Tag: para
+#, no-c-format
+msgid ""
+"To convert parsed degree, minute, and second components to decimal degrees, "
+"apply the cardinal-direction sign explicitly."
+msgstr ""
+
#. Tag: para
#, no-c-format
msgid "Excessively large values are normalized."
@@ -19194,7 +19287,7 @@ msgstr ""
#. Tag: entry
#, no-c-format
-msgid "not yet implemented - will be PolyLine2D"
+msgid "LineSet (coordinates are promoted to 3D)"
msgstr ""
#. Tag: entry
@@ -19207,6 +19300,11 @@ msgstr ""
msgid "MULTILINESTRING"
msgstr ""
+#. Tag: entry
+#, no-c-format
+msgid "IndexedLineSet (coordinates are promoted to 3D)"
+msgstr ""
+
#. Tag: entry
#, no-c-format
msgid "IndexedLineSet"
@@ -19244,7 +19342,9 @@ msgstr ""
#. Tag: entry
#, no-c-format
-msgid "Invalid X3D markup"
+msgid ""
+"IndexedFaceSet (coordinates are promoted to 3D; inner rings currently output "
+"as another faceset)"
msgstr ""
#. Tag: entry
@@ -19267,6 +19367,26 @@ msgstr ""
msgid "IndexedTriangleSet"
msgstr ""
+#. Tag: entry
+#, no-c-format
+msgid "CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON, MULTICURVE, MULTISURFACE"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "linearized, then emitted with the linear geometry mapping"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "NURBSCURVE"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "NurbsCurve with controlPoint, knot, weight, and order fields"
+msgstr ""
+
#. Tag: para
#, no-c-format
msgid ""
@@ -19274,6 +19394,11 @@ msgid ""
"separate polygons. We are working on these."
msgstr ""
+#. Tag: para
+#, no-c-format
+msgid "M coordinates are not represented in X3D output."
+msgstr ""
+
#. Tag: para
#, no-c-format
msgid ""
@@ -19312,6 +19437,13 @@ msgid ""
"flipping. Look at options for details."
msgstr ""
+#. Tag: para
+#, no-c-format
+msgid ""
+"Enhanced: 3.7.0: Support for curve geometries. NURBSCURVE is emitted as X3D "
+"NurbsCurve; other curve types are linearized before output."
+msgstr ""
+
#. Tag: para
#, no-c-format
msgid ""
@@ -21288,6 +21420,13 @@ msgid ""
"obstacle determining the circle radius."
msgstr ""
+#. Tag: para
+#, no-c-format
+msgid ""
+"If a boundary is supplied it must be a valid polygonal geometry with "
+"positive area."
+msgstr ""
+
#. Tag: para
#, no-c-format
msgid ""
@@ -34717,7 +34856,17 @@ msgstr ""
#. Tag: para
#, no-c-format
-msgid ", <xref linkend=\"ST_TransScale\"/>"
+msgid ""
+"This example constructs an ellipse centered at (10, 20), with X radius 8, Y "
+"radius 4, and rotated 30 degrees counter-clockwise. Scaling and rotation use "
+"the center as their origin, so the ellipse keeps both its position and SRID."
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+", <xref linkend=\"ST_Buffer\"/>, <xref linkend=\"ST_MakePoint\"/>, <xref "
+"linkend=\"ST_Rotate\"/>, <xref linkend=\"ST_TransScale\"/>"
msgstr ""
#. Tag: refpurpose
@@ -40435,6 +40584,253 @@ msgid ""
"it needs to read pixel data."
msgstr ""
+#. Tag: title
+#, no-c-format
+msgid "Raster Coverages and Raster/Vector Analysis"
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"A coverage is a way to interpret spatial data as a mapping from a "
+"<emphasis>domain</emphasis> of locations to a <emphasis>range</emphasis> of "
+"values. Evaluating a coverage means asking for the value at a location; an "
+"inverse query asks for the locations whose values match a condition. PostGIS "
+"does not expose a generic ISO 19123 coverage SQL type, but geometry tables "
+"and raster values provide the building blocks for both directions."
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"A coverage can be continuous, with values calculated or interpolated for "
+"arbitrary locations, or discrete, with values supplied by stored spatial "
+"objects or grid cells. The coverage model describes how locations map to "
+"values, independently of whether those values come from an equation, a "
+"table, or one or more raster values. A time column or function argument can "
+"add a temporal dimension without changing that distinction."
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"In a vector coverage, a geometry column supplies the domain and other "
+"columns supply attributes in the range. A vector range can contain text, "
+"numbers, or another spatial value, provided its meaning and type are "
+"consistent wherever the coverage is defined. In a raster, each cell location "
+"is part of the domain and the band values at that cell form the range. Other "
+"columns in the same table row describe the raster value or tile as a whole; "
+"they do not attach separate values to individual cells."
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"A logical raster coverage may be stored as one raster value or as many tiles "
+"in a table. Tiles that belong to one coverage should have compatible SRIDs, "
+"scales, alignment, band counts, pixel types, and NODATA conventions. The "
+"constraints exposed by <xref linkend=\"RT_Raster_Columns\"/> record and "
+"validate those properties."
+msgstr ""
+
+#. Tag: title
+#, no-c-format
+msgid "Raster table arrangements."
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"A raster column can represent an image warehouse, a tiled coverage, or a "
+"collection of rasterized objects. These arrangements have different "
+"assumptions about tile size, alignment, gaps, and overlaps. PostGIS does not "
+"infer the intended arrangement from the column type alone."
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "Arrangement"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "Row relationship"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "Typical use"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "Image warehouse"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid ""
+"Independent rasters; sizes, alignment, and extents may differ or overlap"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "Cataloging unrelated source images"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "Irregular tiled coverage"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid ""
+"One logical coverage with gaps allowed, variable tile sizes, and normally no "
+"overlap"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "Incremental or source-driven mosaics"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid ""
+"One alignment and tile size with no overlap; gaps may remain at the boundary "
+"or inside the coverage"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "Regular tiled coverage"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "Indexed analysis over a large coverage"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "Equal aligned tiles fill a rectangular extent without gaps or overlaps"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "Rectangular regular coverage"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "Predictable block processing and overview generation"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid ""
+"Each image is internally regular but stored in its own table or partition"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "Separate tiled images"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "Keeping source-image identity and lifecycle separate"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid ""
+"Each row is a rasterized feature; extents and sizes may differ and overlap"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "Raster-object coverage"
+msgstr ""
+
+#. Tag: entry
+#, no-c-format
+msgid "Object models where cells replace or supplement vector geometry"
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"Use <xref linkend=\"RT_AddRasterConstraints\"/> to record common SRID, "
+"scale, alignment, band, extent, and regular-blocking properties, and use "
+"spatial indexes and application constraints for relationships that the "
+"catalog cannot express."
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid "Queries against a tiled coverage normally have two stages:"
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"Use the raster spatial index and <xref linkend=\"RT_ST_Intersects\"/> to "
+"select candidate tiles."
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"Read, clip, aggregate, or vectorize values inside those tiles with functions "
+"such as <xref linkend=\"RT_ST_Value\"/>, <xref linkend=\"RT_ST_Clip\"/>, "
+"<xref linkend=\"RT_ST_SummaryStatsAgg\"/>, and <xref "
+"linkend=\"RT_ST_DumpAsPolygons\"/>."
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"returns <xref linkend=\"geomval\"/> rows that pair geometries with cell "
+"values. This allows ordinary geometry overlay and filtering to answer "
+"inverse coverage queries, but polygonizing many cells is usually more "
+"expensive than keeping the operation in raster space."
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"Choose boundary semantics deliberately. Clipping and aggregating raster "
+"pixels is usually the shorter and faster path. If a partial boundary cell "
+"must contribute in proportion to its covered area, intersect the geometry "
+"with the <xref linkend=\"geomval\"/> rows and calculate an area-weighted "
+"result in an appropriate projected coordinate system. That exact partial-"
+"cell method costs more because it vectorizes the selected cells."
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"Tile size affects both sides of the indexed query: smaller tiles reduce the "
+"amount of raster data inspected for each candidate row, while increasing row "
+"and index overhead. Benchmark representative queries instead of treating one "
+"tile size as a universal default."
+msgstr ""
+
+#. Tag: para
+#, no-c-format
+msgid ""
+"For a current end-to-end raster/vector pattern, load and tile data with "
+"<xref linkend=\"RT_Raster_Loader\"/>, then see the building example in <xref "
+"linkend=\"RT_ST_SummaryStats\"/>. It selects intersecting tiles, clips them "
+"to each polygon, and combines per-tile statistics into one result per "
+"feature. For point observations such as animal locations, first buffer the "
+"points in an appropriate projected coordinate system, then apply the same "
+"tile selection, clipping, and aggregation pattern to an elevation or other "
+"continuous raster coverage."
+msgstr ""
+
#. Tag: title
#, no-c-format
msgid "Raster Catalogs"
commit 253bfa1ab219c774c37c025759a20a3ea977d578
Author: Darafei Praliaskouski <me at komzpa.net>
Date: Sat Jul 18 20:29:26 2026 +0400
Fix X3D NURBSCURVE serialization
Emit NURBSCURVE as a native X3D NurbsCurve with explicit order, knot, weight, and controlPoint fields instead of relying on linearized curve output.
Add focused CUnit and regression checks that the NURBS path is not serialized as LineSet while other curve behavior remains linearized.
diff --git a/NEWS b/NEWS
index c6b7f0f04b..76ac9141e2 100644
--- a/NEWS
+++ b/NEWS
@@ -182,7 +182,8 @@ To take advantage of all postgis_sfcgal extension features SFCGAL 2.3+ is needed
prefer requested CXX over pg_config --cc for internal C++ checks, and
make fuzzer linking/runtime dependency packaging portable
(Darafei Praliaskouski)
- - #1416, ST_AsX3D outputs curve geometries
+ - #1416, ST_AsX3D outputs NURBSCURVE as X3D NurbsCurve
+ and linearizes other curve geometries
(Darafei Praliaskouski)
* Enhancements *
diff --git a/doc/reference_output.xml b/doc/reference_output.xml
index 62504da39c..7153047460 100644
--- a/doc/reference_output.xml
+++ b/doc/reference_output.xml
@@ -1658,9 +1658,9 @@ M 7 -8 L 10 -10 6 -14 4 -11 Z</screen>
<entry>linearized, then emitted with the linear geometry mapping</entry>
</row>
<row>
- <entry>NURBSCurve</entry>
- <entry>NurbsCurve</entry>
- <entry>NurbsCurve</entry>
+ <entry>NURBSCURVE</entry>
+ <entry>NurbsCurve with controlPoint, knot, weight, and order fields</entry>
+ <entry>NurbsCurve with controlPoint, knot, weight, and order fields</entry>
</row>
</tbody>
</tgroup>
@@ -1672,7 +1672,7 @@ M 7 -8 L 10 -10 6 -14 4 -11 Z</screen>
<para>Also check out <link xlink:href="https://git.osgeo.org/gitea/robe/postgis_x3d_viewer">PostGIS minimalist X3D viewer</link> that utilizes this function and <link xlink:href="http://www.x3dom.org/">x3dDom html/js open source toolkit</link>.</para>
<para role="availability" conformance="2.0.0">Availability: 2.0.0: ISO-IEC-19776-1.2-X3DEncodings-XML</para>
<para role="enhanced" conformance="2.2.0">Enhanced: 2.2.0: Support for GeoCoordinates and axis (x/y, long/lat) flipping. Look at options for details.</para>
- <para role="enhanced" conformance="3.7.0">Enhanced: 3.7.0: Support for curve geometries. NURBSCurve is emitted as NurbsCurve; other curve types are linearized before output.</para>
+ <para role="enhanced" conformance="3.7.0">Enhanced: 3.7.0: Support for curve geometries. NURBSCURVE is emitted as X3D NurbsCurve; other curve types are linearized before output.</para>
<!-- Optionally mention 3d support -->
<para>&Z_support;</para>
<!-- Optionally mention supports Polyhedral Surface -->
diff --git a/liblwgeom/cunit/cu_out_x3d.c b/liblwgeom/cunit/cu_out_x3d.c
index 1a12790718..2773870b05 100644
--- a/liblwgeom/cunit/cu_out_x3d.c
+++ b/liblwgeom/cunit/cu_out_x3d.c
@@ -262,7 +262,7 @@ out_x3d3_test_geoms(void)
do_x3d3_test(
"NURBSCURVE(2, (0 0, 1 1, 2 0))",
- "<NurbsCurve order='3'><Coordinate containerField='controlPoint' point='0 0 0 1 1 0 2 0 0' /></NurbsCurve>",
+ "<NurbsCurve order='3' knot='0 0 0 1 1 1' weight='1 1 1'><Coordinate containerField='controlPoint' point='0 0 0 1 1 0 2 0 0' /></NurbsCurve>",
0,
0);
@@ -272,6 +272,8 @@ out_x3d3_test_geoms(void)
0,
0);
+ do_x3d3_not_contains("NURBSCURVE(2, (0 0, 1 1, 2 0))", "<LineSet", 0, 0);
+
do_x3d3_contains("GEOMETRYCOLLECTION(NURBSCURVE(2, (0 0, 1 1, 2 0)))", "<Shape><NurbsCurve", 0, 0);
do_x3d3_contains("SRID=4326;NURBSCURVE(2, (0 0, 1 1, 2 0))",
diff --git a/liblwgeom/lwout_x3d.c b/liblwgeom/lwout_x3d.c
index c640c71ced..87c6f9717f 100644
--- a/liblwgeom/lwout_x3d.c
+++ b/liblwgeom/lwout_x3d.c
@@ -497,35 +497,38 @@ static int
asx3d3_nurbscurve_sb(const LWNURBSCURVE *curve, int precision, int opts, const char *defid, stringbuffer_t *sb)
{
uint32_t i;
+ uint32_t nknots = 0;
+ double *knots;
if (!curve->points || curve->points->npoints == 0)
return LW_SUCCESS;
- stringbuffer_aprintf(sb, "<NurbsCurve %s order='%u'", defid, curve->degree + 1);
-
- if (curve->knots && curve->nknots > 0)
+ knots = lwnurbscurve_get_or_generate_knots(curve, &nknots);
+ if (!knots || nknots == 0)
{
- stringbuffer_append_len(sb, " knot='", strlen(" knot='"));
- for (i = 0; i < curve->nknots; i++)
- {
- if (i)
- stringbuffer_append_len(sb, " ", 1);
- stringbuffer_append_double(sb, curve->knots[i], precision);
- }
- stringbuffer_append_len(sb, "'", 1);
+ if (knots)
+ lwfree(knots);
+ return LW_FAILURE;
}
- if (curve->weights && curve->nweights > 0)
+ stringbuffer_aprintf(sb, "<NurbsCurve %s order='%u' knot='", defid, curve->degree + 1);
+ for (i = 0; i < nknots; i++)
{
- stringbuffer_append_len(sb, " weight='", strlen(" weight='"));
- for (i = 0; i < curve->nweights; i++)
- {
- if (i)
- stringbuffer_append_len(sb, " ", 1);
- stringbuffer_append_double(sb, curve->weights[i], precision);
- }
- stringbuffer_append_len(sb, "'", 1);
+ if (i)
+ stringbuffer_append_len(sb, " ", 1);
+ stringbuffer_append_double(sb, knots[i], precision);
}
+ stringbuffer_append_len(sb, "' weight='", strlen("' weight='"));
+ lwfree(knots);
+
+ for (i = 0; i < curve->points->npoints; i++)
+ {
+ double weight = (curve->weights && i < curve->nweights) ? curve->weights[i] : 1.0;
+ if (i)
+ stringbuffer_append_len(sb, " ", 1);
+ stringbuffer_append_double(sb, weight, precision);
+ }
+ stringbuffer_append_len(sb, "'", 1);
if (X3D_USE_GEOCOORDS(opts))
stringbuffer_aprintf(
diff --git a/regress/core/tickets.sql b/regress/core/tickets.sql
index 2525950a27..fb5dd44d3d 100644
--- a/regress/core/tickets.sql
+++ b/regress/core/tickets.sql
@@ -1418,7 +1418,8 @@ SELECT '#1416f', ST_AsX3D('GEOMETRYCOLLECTION(CIRCULARSTRING(0 0 1,1 1 1,2 0 1))
SELECT '#1416g', ST_AsX3D('GEOMETRYCOLLECTION(COMPOUNDCURVE(CIRCULARSTRING(0 0 1,1 1 1,1 0 1),(1 0 1,0 1 1)))'::geometry, 0) LIKE '<Shape><LineSet%';
SELECT '#1416h', ST_AsX3D('GEOMETRYCOLLECTION(CURVEPOLYGON(CIRCULARSTRING(0 0 1,1 1 1,2 0 1,1 -1 1,0 0 1)))'::geometry, 0) LIKE '<Shape><IndexedFaceSet%';
SELECT '#1416i', ST_AsX3D('CIRCULARSTRING(0 0 1,1 1 1,2 0 1,1 -1 1,0 0 1)'::geometry, 0) LIKE '<LineSet vertexCount=''129''><Coordinate point=%';
-SELECT '#1416j', ST_AsX3D('NURBSCURVE(2, (0 0, 1 1, 2 0))'::geometry, 0) LIKE '%<Coordinate containerField=''controlPoint'' point=%';
+SELECT '#1416j', ST_AsX3D('NURBSCURVE(2, (0 0, 1 1, 2 0))'::geometry, 0);
+SELECT '#1416k', ST_AsX3D('NURBSCURVE(2, (0 0, 1 1, 2 0))'::geometry, 0) NOT LIKE '%<LineSet%';
SELECT '#4670-0', ST_AsEWKT(ST_AddPoint('LINESTRING(0 0, 1 1, 3 3, 4 4)'::geometry, 'POINT(2 2)'::geometry, 0));
SELECT '#4670-1', ST_AsEWKT(ST_AddPoint('LINESTRING(0 0, 1 1, 3 3, 4 4)'::geometry, 'POINT(2 2)'::geometry, 1));
diff --git a/regress/core/tickets_expected b/regress/core/tickets_expected
index 56515cc343..9f864b1077 100644
--- a/regress/core/tickets_expected
+++ b/regress/core/tickets_expected
@@ -449,7 +449,8 @@ ERROR: BOX2D_construct: args can not be empty points
#1416g|t
#1416h|t
#1416i|t
-#1416j|t
+#1416j|<NurbsCurve order='3' knot='0 0 0 1 1 1' weight='1 1 1'><Coordinate containerField='controlPoint' point='0 0 0 1 1 0 2 0 0' /></NurbsCurve>
+#1416k|t
#4670-0|LINESTRING(2 2,0 0,1 1,3 3,4 4)
#4670-1|LINESTRING(0 0,2 2,1 1,3 3,4 4)
#4670-2|LINESTRING(0 0,1 1,2 2,3 3,4 4)
commit 097d0d11d02d4d1ad5f30e2dd84e54c4a9dc2d64
Author: Darafei Praliaskouski <me at komzpa.net>
Date: Sat Jun 20 23:57:10 2026 +0400
Add ST_AsX3D support for curves
References #1416
References #3705
diff --git a/NEWS b/NEWS
index 11dd7c99e9..c6b7f0f04b 100644
--- a/NEWS
+++ b/NEWS
@@ -65,6 +65,8 @@ These are only changes since 3.7.0alpha1.
preprocessed with AltiVec vector macros (Darafei Praliaskouski)
- #6094, [upgrade] Avoid legacy string literal warnings in downgrade checks
with standard_conforming_strings off (Darafei Praliaskouski)
+ - #3705, ST_AsX3D outputs valid coordinate nodes for 2D multi-geometries
+ and GeometryCollections (Darafei Praliaskouski)
- #5423, Use a DTD-free expanded DocBook input for EPUB builds so
out-of-tree builds do not need dbtoepub entity path support
(Darafei Praliaskouski)
@@ -180,7 +182,8 @@ To take advantage of all postgis_sfcgal extension features SFCGAL 2.3+ is needed
prefer requested CXX over pg_config --cc for internal C++ checks, and
make fuzzer linking/runtime dependency packaging portable
(Darafei Praliaskouski)
-
+ - #1416, ST_AsX3D outputs curve geometries
+ (Darafei Praliaskouski)
* Enhancements *
- #2045, pgsql2shp query dumps no longer require temporary table
diff --git a/doc/reference_output.xml b/doc/reference_output.xml
index c88d7ecd3f..62504da39c 100644
--- a/doc/reference_output.xml
+++ b/doc/reference_output.xml
@@ -1624,12 +1624,12 @@ M 7 -8 L 10 -10 6 -14 4 -11 Z</screen>
<tbody>
<row>
<entry>LINESTRING</entry>
- <entry>not yet implemented - will be PolyLine2D</entry>
+ <entry>LineSet (coordinates are promoted to 3D)</entry>
<entry>LineSet</entry>
</row>
<row>
<entry>MULTILINESTRING</entry>
- <entry>not yet implemented - will be PolyLine2D</entry>
+ <entry>IndexedLineSet (coordinates are promoted to 3D)</entry>
<entry>IndexedLineSet</entry>
</row>
<row>
@@ -1644,7 +1644,7 @@ M 7 -8 L 10 -10 6 -14 4 -11 Z</screen>
</row>
<row>
<entry>(MULTI) POLYGON, POLYHEDRALSURFACE</entry>
- <entry>Invalid X3D markup</entry>
+ <entry>IndexedFaceSet (coordinates are promoted to 3D; inner rings currently output as another faceset)</entry>
<entry>IndexedFaceSet (inner rings currently output as another faceset)</entry>
</row>
<row>
@@ -1652,15 +1652,27 @@ M 7 -8 L 10 -10 6 -14 4 -11 Z</screen>
<entry>TriangleSet2D (Not Yet Implemented)</entry>
<entry>IndexedTriangleSet</entry>
</row>
+ <row>
+ <entry>CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON, MULTICURVE, MULTISURFACE</entry>
+ <entry>linearized, then emitted with the linear geometry mapping</entry>
+ <entry>linearized, then emitted with the linear geometry mapping</entry>
+ </row>
+ <row>
+ <entry>NURBSCurve</entry>
+ <entry>NurbsCurve</entry>
+ <entry>NurbsCurve</entry>
+ </row>
</tbody>
</tgroup>
</informaltable>
<note><para>2D geometry support not yet complete. Inner rings currently just drawn as separate polygons. We are working on these.</para></note>
+ <para>M coordinates are not represented in X3D output.</para>
<para>Lots of advancements happening in 3D space particularly with <link xlink:href="https://www.web3d.org/wiki/index.php/X3D_and_HTML5">X3D Integration with HTML5</link></para>
<para>There is also a nice open source X3D viewer you can use to view rendered geometries. Free Wrl <link xlink:href="http://freewrl.sourceforge.net/">http://freewrl.sourceforge.net/</link> binaries available for Mac, Linux, and Windows. Use the FreeWRL_Launcher packaged to view the geometries.</para>
<para>Also check out <link xlink:href="https://git.osgeo.org/gitea/robe/postgis_x3d_viewer">PostGIS minimalist X3D viewer</link> that utilizes this function and <link xlink:href="http://www.x3dom.org/">x3dDom html/js open source toolkit</link>.</para>
<para role="availability" conformance="2.0.0">Availability: 2.0.0: ISO-IEC-19776-1.2-X3DEncodings-XML</para>
<para role="enhanced" conformance="2.2.0">Enhanced: 2.2.0: Support for GeoCoordinates and axis (x/y, long/lat) flipping. Look at options for details.</para>
+ <para role="enhanced" conformance="3.7.0">Enhanced: 3.7.0: Support for curve geometries. NURBSCurve is emitted as NurbsCurve; other curve types are linearized before output.</para>
<!-- Optionally mention 3d support -->
<para>&Z_support;</para>
<!-- Optionally mention supports Polyhedral Surface -->
diff --git a/liblwgeom/cunit/cu_out_x3d.c b/liblwgeom/cunit/cu_out_x3d.c
index 2418e31c2d..1a12790718 100644
--- a/liblwgeom/cunit/cu_out_x3d.c
+++ b/liblwgeom/cunit/cu_out_x3d.c
@@ -18,9 +18,11 @@
#include "liblwgeom_internal.h"
#include "cu_tester.h"
-static void do_x3d3_test(char * in, char * out, int precision, int option)
+static void
+do_x3d3_test(char *in, char *out, int precision, int option)
{
LWGEOM *g = lwgeom_from_wkt(in, LW_PARSER_CHECK_NONE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g);
lwvarlena_t *v = lwgeom_to_x3d3(g, precision, option, "");
ASSERT_VARLENA_EQUAL(v, out);
@@ -29,52 +31,89 @@ static void do_x3d3_test(char * in, char * out, int precision, int option)
lwfree(v);
}
-
-static void do_x3d3_unsupported(char * in, char * out)
+static void
+do_x3d3_contains(char *in, char *needle, int precision, int option)
{
LWGEOM *g = lwgeom_from_wkt(in, LW_PARSER_CHECK_NONE);
- lwvarlena_t *v = lwgeom_to_x3d3(g, 0, 0, "");
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g);
+ lwvarlena_t *v = lwgeom_to_x3d3(g, precision, option, "");
+ size_t len;
+ char *out;
- ASSERT_STRING_EQUAL(out, cu_error_msg);
- cu_error_msg_reset();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(v);
+ len = LWSIZE_GET(v->size) - LWVARHDRSZ;
+ out = lwalloc(len + 1);
+ memcpy(out, v->data, len);
+ out[len] = '\0';
+
+ if (!strstr(out, needle))
+ {
+ fprintf(stderr, "[%s:%d]\n Expected substring: %s\n Obtained: %s\n", __FILE__, __LINE__, needle, out);
+ CU_FAIL();
+ }
+ else
+ CU_PASS();
+
+ lwfree(out);
lwfree(v);
lwgeom_free(g);
}
+static void
+do_x3d3_not_contains(char *in, char *needle, int precision, int option)
+{
+ LWGEOM *g = lwgeom_from_wkt(in, LW_PARSER_CHECK_NONE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g);
+ lwvarlena_t *v = lwgeom_to_x3d3(g, precision, option, "");
+ size_t len;
+ char *out;
-static void out_x3d3_test_precision(void)
+ CU_ASSERT_PTR_NOT_NULL_FATAL(v);
+ len = LWSIZE_GET(v->size) - LWVARHDRSZ;
+ out = lwalloc(len + 1);
+
+ memcpy(out, v->data, len);
+ out[len] = '\0';
+
+ if (strstr(out, needle))
+ {
+ fprintf(stderr, "[%s:%d]\n Unexpected substring: %s\n Obtained: %s\n", __FILE__, __LINE__, needle, out);
+ CU_FAIL();
+ }
+ else
+ CU_PASS();
+
+ lwfree(out);
+ lwfree(v);
+ lwgeom_free(g);
+}
+
+static void
+out_x3d3_test_precision(void)
{
/* 0 precision, i.e a round */
- do_x3d3_test(
- "POINT(1.1111111111111 1.1111111111111 2.11111111111111)",
- "1 1 2",
- 0, 0);
+ do_x3d3_test("POINT(1.1111111111111 1.1111111111111 2.11111111111111)", "1 1 2", 0, 0);
/* 3 digits precision */
- do_x3d3_test(
- "POINT(1.1111111111111 1.1111111111111 2.11111111111111)",
- "1.111 1.111 2.111",
- 3, 0);
+ do_x3d3_test("POINT(1.1111111111111 1.1111111111111 2.11111111111111)", "1.111 1.111 2.111", 3, 0);
/* 9 digits precision */
do_x3d3_test(
- "POINT(1.2345678901234 1.2345678901234 4.123456789001)",
- "1.23456789 1.23456789 4.123456789",
- 9, 0);
+ "POINT(1.2345678901234 1.2345678901234 4.123456789001)", "1.23456789 1.23456789 4.123456789", 9, 0);
/* huge data */
do_x3d3_test("POINT(1E300 -105E-153 4E300)", "1e+300 -1e-151 4e+300", 0, 0);
}
-
-static void out_x3d3_test_geoms(void)
+static void
+out_x3d3_test_geoms(void)
{
/* Linestring */
- do_x3d3_test(
- "LINESTRING(0 1 5,2 3 6,4 5 7)",
- "<LineSet vertexCount='3'><Coordinate point='0 1 5 2 3 6 4 5 7' /></LineSet>",
- 0, 0);
+ do_x3d3_test("LINESTRING(0 1 5,2 3 6,4 5 7)",
+ "<LineSet vertexCount='3'><Coordinate point='0 1 5 2 3 6 4 5 7' /></LineSet>",
+ 0,
+ 0);
/* 2D Linestring */
do_x3d3_test("LINESTRING(0 1,2 3,4 5)",
@@ -82,11 +121,19 @@ static void out_x3d3_test_geoms(void)
0,
0);
+ /* Closed lines keep their closing coordinate because LineSet has no index
+ * node to point back to the first vertex. */
+ do_x3d3_test("LINESTRING(0 0,1 0,0 0)",
+ "<LineSet vertexCount='3'><Coordinate point='0 0 0 1 0 0 0 0 0' /></LineSet>",
+ 0,
+ 0);
+
/* Polygon **/
do_x3d3_test(
"POLYGON((15 10 3,13.536 6.464 3,10 5 3,6.464 6.464 3,5 10 3,6.464 13.536 3,10 15 3,13.536 13.536 3,15 10 3))",
"<IndexedFaceSet convex='false' coordIndex='0 1 2 3 4 5 6 7'><Coordinate point='15 10 3 13.536 6.464 3 10 5 3 6.464 6.464 3 5 10 3 6.464 13.536 3 10 15 3 13.536 13.536 3 ' /></IndexedFaceSet>",
- 3, 0);
+ 3,
+ 0);
/* 2D Polygon */
do_x3d3_test(
@@ -98,25 +145,26 @@ static void out_x3d3_test_geoms(void)
/* TODO: Polygon - with internal ring - the answer is clearly wrong */
/** do_x3d3_test(
"POLYGON((0 1 3,2 3 3,4 5 3,0 1 3),(6 7 3,8 9 3,10 11 3,6 7 3))",
- "",
+ "",
NULL, 0); **/
/* 2D MultiPoint */
- do_x3d3_test(
- "MULTIPOINT(0 1,2 3,4 5)",
- "<Polypoint2D point='0 1 2 3 4 5 ' />",
- 0, 0);
+ do_x3d3_test("MULTIPOINT(0 1,2 3,4 5)", "<Polypoint2D point='0 1 2 3 4 5 ' />", 0, 0);
/* 3D MultiPoint */
do_x3d3_test(
- "MULTIPOINT Z(0 1 1,2 3 4,4 5 5)",
- "<PointSet ><Coordinate point='0 1 1 2 3 4 4 5 5 ' /></PointSet>",
- 0, 0);
+ "MULTIPOINT Z(0 1 1,2 3 4,4 5 5)", "<PointSet ><Coordinate point='0 1 1 2 3 4 4 5 5 ' /></PointSet>", 0, 0);
/* 3D Multiline */
do_x3d3_test(
"MULTILINESTRING Z((0 1 1,2 3 4,4 5 5),(6 7 5,8 9 8,10 11 5))",
"<IndexedLineSet coordIndex='0 1 2 -1 3 4 5'><Coordinate point='0 1 1 2 3 4 4 5 5 6 7 5 8 9 8 10 11 5 ' /></IndexedLineSet>",
- 0, 0);
+ 0,
+ 0);
+ /* 2D Multiline */
+ do_x3d3_test("MULTILINESTRING((2 3,4 5))",
+ "<IndexedLineSet coordIndex='0 1'><Coordinate point='2 3 0 4 5 0 ' /></IndexedLineSet>",
+ 0,
+ 0);
/* 2D Multiline */
do_x3d3_test(
@@ -129,7 +177,14 @@ static void out_x3d3_test_geoms(void)
do_x3d3_test(
"MULTIPOLYGON(((0 1 1,2 3 1,4 5 1,0 1 1)),((6 7 1,8 9 1,10 11 1,6 7 1)))",
"<IndexedFaceSet convex='false' coordIndex='0 1 2 -1 3 4 5'><Coordinate point='0 1 1 2 3 1 4 5 1 6 7 1 8 9 1 10 11 1 ' /></IndexedFaceSet>",
- 0, 0);
+ 0,
+ 0);
+ /* 2D MultiPolygon */
+ do_x3d3_test(
+ "MULTIPOLYGON(((0 0,1 0,0 1,0 0)))",
+ "<IndexedFaceSet convex='false' coordIndex='0 1 2'><Coordinate point='0 0 0 1 0 0 0 1 0 ' /></IndexedFaceSet>",
+ 0,
+ 0);
/* 2D MultiPolygon */
do_x3d3_test(
@@ -142,7 +197,8 @@ static void out_x3d3_test_geoms(void)
do_x3d3_test(
"POLYHEDRALSURFACE( ((0 0 0, 0 0 1, 0 1 1, 0 1 0, 0 0 0)), ((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)), ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)), ((1 1 0, 1 1 1, 1 0 1, 1 0 0, 1 1 0)), ((0 1 0, 0 1 1, 1 1 1, 1 1 0, 0 1 0)), ((0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1)) )",
"<IndexedFaceSet convex='false' coordIndex='0 1 2 3 -1 4 5 6 7 -1 8 9 10 11 -1 12 13 14 15 -1 16 17 18 19 -1 20 21 22 23'><Coordinate point='0 0 0 0 0 1 0 1 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0 0 0 0 0 1 0 0 1 0 1 0 0 1 1 1 0 1 1 1 1 0 1 1 0 0 0 1 0 0 1 1 1 1 1 1 1 0 0 0 1 1 0 1 1 1 1 0 1 1' /></IndexedFaceSet>",
- 0, 0);
+ 0,
+ 0);
/* GeometryCollection with 2D Polygon */
do_x3d3_test(
@@ -151,51 +207,111 @@ static void out_x3d3_test_geoms(void)
0,
0);
+ do_x3d3_test(
+ "GEOMETRYCOLLECTION(POINT(0 1 3),LINESTRING(2 3 3,4 5 3))",
+ "<Shape><PointSet ><Coordinate point='0 1 3 ' /></PointSet></Shape><Shape><LineSet vertexCount='2'><Coordinate point='2 3 3 4 5 3' /></LineSet></Shape>",
+ 0,
+ 0);
+
+ do_x3d3_test("GEOMETRYCOLLECTION(POINT(1 2),POINT(3 4))",
+ "<Shape><Polypoint2D point='1 2 ' /></Shape><Shape><Polypoint2D point='3 4 ' /></Shape>",
+ 0,
+ 0);
+
+ do_x3d3_contains("GEOMETRYCOLLECTION(TRIANGLE((0 0,1 1,0 1,0 0)))", "<Shape><IndexedTriangleSet", 0, 0);
+
+ do_x3d3_not_contains(
+ "GEOMETRYCOLLECTION(GEOMETRYCOLLECTION(POINT(0 0),LINESTRING(0 0,1 1)))", "<Shape><Shape>", 0, 0);
+
/* TODO: Implement Empty GeometryCollection correctly or throw a not-implemented */
/** do_x3d3_test(
"GEOMETRYCOLLECTION EMPTY",
"",
NULL, 0); **/
- /* CircularString */
- do_x3d3_unsupported(
- "CIRCULARSTRING(-2 0 1,0 2 1,2 0 1,0 2 1,2 4 1)",
- "lwgeom_to_x3d3: 'CircularString' geometry type not supported");
+ /* Non-NURBS curves are linearized before output */
+ do_x3d3_contains("CIRCULARSTRING(-2 0 1,0 2 1,2 0 1,0 2 1,2 4 1)",
+ "<LineSet vertexCount='129'><Coordinate point='-2 0 1",
+ 0,
+ 0);
- /* CompoundCurve */
- do_x3d3_unsupported(
- "COMPOUNDCURVE(CIRCULARSTRING(0 0 1,1 1 1,1 0 1),(1 0 1,0 1 1))",
- "lwgeom_to_x3d3: 'CompoundCurve' geometry type not supported");
+ do_x3d3_contains("CIRCULARSTRING(0 0 1,1 1 1,2 0 1,1 -1 1,0 0 1)",
+ "<LineSet vertexCount='129'><Coordinate point='0 0 1",
+ 0,
+ 0);
+ do_x3d3_contains("COMPOUNDCURVE(CIRCULARSTRING(0 0 1,1 1 1,1 0 1),(1 0 1,0 1 1))",
+ "<LineSet vertexCount='98'><Coordinate point='0 0 1",
+ 0,
+ 0);
+
+ do_x3d3_contains(
+ "CURVEPOLYGON(CIRCULARSTRING(0 0 1,1 1 1,2 0 1,1 -1 1,0 0 1))", "<IndexedFaceSet convex='false'", 0, 0);
+
+ do_x3d3_contains("GEOMETRYCOLLECTION(CIRCULARSTRING(0 0 1,1 1 1,2 0 1))", "<Shape><LineSet", 0, 0);
+
+ do_x3d3_contains("GEOMETRYCOLLECTION(COMPOUNDCURVE(CIRCULARSTRING(0 0 1,1 1 1,1 0 1),(1 0 1,0 1 1)))",
+ "<Shape><LineSet",
+ 0,
+ 0);
+
+ do_x3d3_contains("GEOMETRYCOLLECTION(CURVEPOLYGON(CIRCULARSTRING(0 0 1,1 1 1,2 0 1,1 -1 1,0 0 1)))",
+ "<Shape><IndexedFaceSet",
+ 0,
+ 0);
+
+ do_x3d3_test(
+ "NURBSCURVE(2, (0 0, 1 1, 2 0))",
+ "<NurbsCurve order='3'><Coordinate containerField='controlPoint' point='0 0 0 1 1 0 2 0 0' /></NurbsCurve>",
+ 0,
+ 0);
+
+ do_x3d3_test(
+ "NURBSCURVE(2, (0 0, 1 1, 2 0), (1, 2, 1), (0, 0, 0, 1, 1, 1))",
+ "<NurbsCurve order='3' knot='0 0 0 1 1 1' weight='1 2 1'><Coordinate containerField='controlPoint' point='0 0 0 1 1 0 2 0 0' /></NurbsCurve>",
+ 0,
+ 0);
+
+ do_x3d3_contains("GEOMETRYCOLLECTION(NURBSCURVE(2, (0 0, 1 1, 2 0)))", "<Shape><NurbsCurve", 0, 0);
+
+ do_x3d3_contains("SRID=4326;NURBSCURVE(2, (0 0, 1 1, 2 0))",
+ "><GeoCoordinate containerField='controlPoint' geoSystem='\"GD\" \"WE\" \"longitude_first\"'",
+ 0,
+ 2);
+
+ do_x3d3_contains("GEOMETRYCOLLECTION(CIRCULARSTRING(0 0,1 1,2 0),TIN(((0 0,1 1,0 1,0 0))))",
+ "</IndexedTriangleSet></Shape>",
+ 0,
+ 0);
}
-static void out_x3d3_test_option(void)
+static void
+out_x3d3_test_option(void)
{
/* 0 precision, flip coordinates*/
- do_x3d3_test(
- "POINT(3.1111111111111 1.1111111111111 2.11111111111111)",
- "1 3 2",
- 0, 1);
+ do_x3d3_test("POINT(3.1111111111111 1.1111111111111 2.11111111111111)", "1 3 2", 0, 1);
/* geocoordinate long,lat*/
do_x3d3_test(
"SRID=4326;POLYGON((15 10 3,13.536 6.464 3,10 5 3,6.464 6.464 3,5 10 3,6.464 13.536 3,10 15 3,13.536 13.536 3,15 10 3))",
"<IndexedFaceSet convex='false' coordIndex='0 1 2 3 4 5 6 7'><GeoCoordinate geoSystem='\"GD\" \"WE\" \"longitude_first\"' point='15 10 3 13.536 6.464 3 10 5 3 6.464 6.464 3 5 10 3 6.464 13.536 3 10 15 3 13.536 13.536 3 ' /></IndexedFaceSet>",
- 3, 2);
+ 3,
+ 2);
/* geocoordinate lat long*/
do_x3d3_test(
"SRID=4326;POLYGON((15 10 3,13.536 6.464 3,10 5 3,6.464 6.464 3,5 10 3,6.464 13.536 3,10 15 3,13.536 13.536 3,15 10 3))",
"<IndexedFaceSet convex='false' coordIndex='0 1 2 3 4 5 6 7'><GeoCoordinate geoSystem='\"GD\" \"WE\" \"latitude_first\"' point='10 15 3 6.464 13.536 3 5 10 3 6.464 6.464 3 10 5 3 13.536 6.464 3 15 10 3 13.536 13.536 3 ' /></IndexedFaceSet>",
- 3, 3);
+ 3,
+ 3);
}
-
/*
** Used by test harness to register the tests in this file.
*/
void out_x3d_suite_setup(void);
-void out_x3d_suite_setup(void)
+void
+out_x3d_suite_setup(void)
{
CU_pSuite suite = CU_add_suite("x3d_output", NULL, NULL);
PG_ADD_TEST(suite, out_x3d3_test_precision);
diff --git a/liblwgeom/lwout_x3d.c b/liblwgeom/lwout_x3d.c
index 4737fd09ed..c640c71ced 100644
--- a/liblwgeom/lwout_x3d.c
+++ b/liblwgeom/lwout_x3d.c
@@ -24,9 +24,9 @@
**********************************************************************/
/**
-* @file X3D output routines.
-*
-**********************************************************************/
+ * @file X3D output routines.
+ *
+ **********************************************************************/
#include "lwout_x3d.h"
@@ -41,7 +41,7 @@ lwgeom_to_x3d3(const LWGEOM *geom, int precision, int opts, const char *defid)
int rv;
/* Empty varlena for empties */
- if( lwgeom_is_empty(geom) )
+ if (lwgeom_is_empty(geom))
{
lwvarlena_t *v = lwalloc(LWVARHDRSZ);
LWSIZE_SET(v->size, LWVARHDRSZ);
@@ -51,7 +51,7 @@ lwgeom_to_x3d3(const LWGEOM *geom, int precision, int opts, const char *defid)
sb = stringbuffer_create();
rv = lwgeom_to_x3d3_sb(geom, precision, opts, defid, sb);
- if ( rv == LW_FAILURE )
+ if (rv == LW_FAILURE)
{
stringbuffer_destroy(sb);
return NULL;
@@ -76,15 +76,17 @@ lwgeom_to_x3d3_sb(const LWGEOM *geom, int precision, int opts, const char *defid
case LINETYPE:
return asx3d3_line_sb((LWLINE *)geom, precision, opts, defid, sb);
- case POLYGONTYPE:
- {
+ case POLYGONTYPE: {
/** We might change this later, but putting a polygon in an indexed face set
- * seems like the simplest way to go so treat just like a mulitpolygon
- */
- LWCOLLECTION *tmp = (LWCOLLECTION*)lwgeom_as_multi(geom);
- asx3d3_multi_sb(tmp, precision, opts, defid, sb);
+ * seems like the simplest way to go so treat just like a mulitpolygon
+ */
+ LWCOLLECTION *tmp = (LWCOLLECTION *)lwgeom_as_multi(geom);
+ int rv;
+ if (!tmp)
+ return LW_FAILURE;
+ rv = asx3d3_multi_sb(tmp, precision, opts, defid, sb);
lwcollection_free(tmp);
- return LW_SUCCESS;
+ return rv;
}
case TRIANGLETYPE:
@@ -104,6 +106,24 @@ lwgeom_to_x3d3_sb(const LWGEOM *geom, int precision, int opts, const char *defid
case COLLECTIONTYPE:
return asx3d3_collection_sb((LWCOLLECTION *)geom, precision, opts, defid, sb);
+ case NURBSCURVETYPE:
+ /* X3D has a native NurbsCurve node; do not linearize it. */
+ return asx3d3_nurbscurve_sb((const LWNURBSCURVE *)geom, precision, opts, defid, sb);
+
+ case CIRCSTRINGTYPE:
+ case COMPOUNDTYPE:
+ case CURVEPOLYTYPE:
+ case MULTICURVETYPE:
+ case MULTISURFACETYPE: {
+ LWGEOM *linear = lwgeom_stroke(geom, 32);
+ int rv;
+ if (!linear)
+ return LW_FAILURE;
+ rv = lwgeom_to_x3d3_sb(linear, precision, opts, defid, sb);
+ lwgeom_free(linear);
+ return rv;
+ }
+
default:
lwerror("lwgeom_to_x3d3: '%s' geometry type not supported", lwtype_name(type));
return LW_FAILURE;
@@ -138,32 +158,32 @@ asx3d3_mline_coordindex_sb(const LWMLINE *mgeom, stringbuffer_t *sb)
uint32_t np;
j = 0;
- for (i=0; i < mgeom->ngeoms; i++)
+ for (i = 0; i < mgeom->ngeoms; i++)
{
- geom = (LWLINE *) mgeom->geoms[i];
+ geom = (LWLINE *)mgeom->geoms[i];
pa = geom->points;
np = pa->npoints;
- si = j; /* start index of first point of linestring */
- for (k=0; k < np ; k++)
+ si = j; /* start index of first point of linestring */
+ for (k = 0; k < np; k++)
{
if (k)
{
stringbuffer_aprintf(sb, " ");
}
/** if the linestring is closed, we put the start point index
- * for the last vertex to denote use first point
- * and don't increment the index **/
- if (!lwline_is_closed(geom) || k < (np -1) )
+ * for the last vertex to denote use first point
+ * and don't increment the index **/
+ if (!lwline_is_closed(geom) || k < (np - 1))
{
stringbuffer_aprintf(sb, "%u", j);
j += 1;
}
else
{
- stringbuffer_aprintf(sb,"%u", si);
+ stringbuffer_aprintf(sb, "%u", si);
}
}
- if (i < (mgeom->ngeoms - 1) )
+ if (i < (mgeom->ngeoms - 1))
{
stringbuffer_aprintf(sb, " -1 "); /* separator for each linestring */
}
@@ -181,37 +201,40 @@ asx3d3_mpoly_coordindex_sb(const LWMPOLY *psur, stringbuffer_t *sb)
uint32_t i, j, k, l;
uint32_t np;
j = 0;
- for (i=0; i<psur->ngeoms; i++)
+ for (i = 0; i < psur->ngeoms; i++)
{
- patch = (LWPOLY *) psur->geoms[i];
- for (l=0; l < patch->nrings; l++)
+ patch = (LWPOLY *)psur->geoms[i];
+ for (l = 0; l < patch->nrings; l++)
{
np = patch->rings[l]->npoints - 1;
- for (k=0; k < np ; k++)
+ for (k = 0; k < np; k++)
{
if (k)
{
- stringbuffer_aprintf(sb, " ");
+ stringbuffer_aprintf(sb, " ");
}
- stringbuffer_aprintf(sb, "%d", (j + k));
+ stringbuffer_aprintf(sb, "%d", (j + k));
}
j += k;
- if (l < (patch->nrings - 1) )
+ if (l < (patch->nrings - 1))
{
/** @todo TODO: Decide the best way to render holes
- * Evidently according to my X3D expert the X3D consortium doesn't really
- * support holes and it's an issue of argument among many that feel it should. He thinks CAD x3d extensions to spec might.
- * What he has done and others developing X3D exports to simulate a hole is to cut around it.
- * So if you have a donut, you would cut it into half and have 2 solid polygons. Not really sure the best way to handle this.
- * For now will leave it as polygons stacked on top of each other -- which is what we are doing here and perhaps an option
- * to color differently. It's not ideal but the alternative sounds complicated.
- **/
- stringbuffer_aprintf(sb, " -1 "); /* separator for each inner ring. Ideally we should probably triangulate and cut around as others do */
+ * Evidently according to my X3D expert the X3D consortium doesn't really
+ * support holes and it's an issue of argument among many that feel it should. He
+ * thinks CAD x3d extensions to spec might. What he has done and others developing X3D
+ * exports to simulate a hole is to cut around it. So if you have a donut, you would cut
+ * it into half and have 2 solid polygons. Not really sure the best way to handle this.
+ * For now will leave it as polygons stacked on top of each other -- which is what we
+ * are doing here and perhaps an option to color differently. It's not ideal but the
+ * alternative sounds complicated.
+ **/
+ stringbuffer_aprintf(sb, " -1 "); /* separator for each inner ring. Ideally we should
+ probably triangulate and cut around as others do */
}
}
- if (i < (psur->ngeoms - 1) )
+ if (i < (psur->ngeoms - 1))
{
- stringbuffer_aprintf(sb, " -1 "); /* separator for each subgeom */
+ stringbuffer_aprintf(sb, " -1 "); /* separator for each subgeom */
}
}
return LW_SUCCESS;
@@ -225,21 +248,22 @@ asx3d3_line_sb(const LWLINE *line,
__attribute__((__unused__)) const char *defid,
stringbuffer_t *sb)
{
-
- /* int dimension=2; */
POINTARRAY *pa;
-
-
- /* if (FLAGS_GET_Z(line->flags)) dimension = 3; */
+ uint32_t npoints;
pa = line->points;
- stringbuffer_aprintf(sb, "<LineSet %s vertexCount='%d'>", defid, pa->npoints);
+ npoints = pa->npoints;
- if ( X3D_USE_GEOCOORDS(opts) ) stringbuffer_aprintf(sb, "<GeoCoordinate geoSystem='\"GD\" \"WE\" \"%s\"' point='", ( (opts & LW_X3D_FLIP_XY) ? "latitude_first" : "longitude_first") );
+ stringbuffer_aprintf(sb, "<LineSet %s vertexCount='%u'>", defid, npoints);
+
+ if (X3D_USE_GEOCOORDS(opts))
+ stringbuffer_aprintf(sb,
+ "<GeoCoordinate geoSystem='\"GD\" \"WE\" \"%s\"' point='",
+ ((opts & LW_X3D_FLIP_XY) ? "latitude_first" : "longitude_first"));
else
stringbuffer_aprintf(sb, "<Coordinate point='");
- ptarray_to_x3d3_sb(line->points, precision, opts, lwline_is_closed((LWLINE *)line), LW_TRUE, sb);
+ ptarray_to_x3d3_sb(line->points, precision, opts, LW_FALSE, LW_TRUE, sb);
stringbuffer_aprintf(sb, "' />");
@@ -256,9 +280,10 @@ asx3d3_poly_sb(const LWPOLY *poly,
stringbuffer_t *sb)
{
uint32_t i;
- for (i=0; i<poly->nrings; i++)
+ for (i = 0; i < poly->nrings; i++)
{
- if (i) stringbuffer_aprintf(sb, " "); /* inner ring points start */
+ if (i)
+ stringbuffer_aprintf(sb, " "); /* inner ring points start */
ptarray_to_x3d3_sb(poly->rings[i], precision, opts, 1, LW_TRUE, sb);
}
return LW_SUCCESS;
@@ -274,7 +299,6 @@ asx3d3_triangle_sb(const LWTRIANGLE *triangle,
return ptarray_to_x3d3_sb(triangle->points, precision, opts, 1, LW_TRUE, sb);
}
-
/*
* Don't call this with single-geoms inspected!
*/
@@ -283,13 +307,13 @@ asx3d3_multi_sb(const LWCOLLECTION *col, int precision, int opts, const char *de
{
char *x3dtype;
uint32_t i;
- int dimension=2;
+ int dimension = 2;
int has_coordinate_node = LW_FALSE;
- if (FLAGS_GET_Z(col->flags)) dimension = 3;
+ if (FLAGS_GET_Z(col->flags))
+ dimension = 3;
LWGEOM *subgeom;
- x3dtype="";
-
+ x3dtype = "";
switch (col->type)
{
@@ -337,7 +361,7 @@ asx3d3_multi_sb(const LWCOLLECTION *col, int precision, int opts, const char *de
stringbuffer_aprintf(sb, "<Coordinate point='");
}
- for (i=0; i<col->ngeoms; i++)
+ for (i = 0; i < col->ngeoms; i++)
{
subgeom = col->geoms[i];
if (subgeom->type == POINTTYPE)
@@ -362,9 +386,11 @@ asx3d3_multi_sb(const LWCOLLECTION *col, int precision, int opts, const char *de
{
stringbuffer_aprintf(sb, "' /></%s>", x3dtype);
}
- else { stringbuffer_aprintf(sb, "' />"); }
+ else
+ {
+ stringbuffer_aprintf(sb, "' />");
+ }
return LW_SUCCESS;
-
}
/*
@@ -380,39 +406,42 @@ asx3d3_psurface_sb(const LWPSURFACE *psur, int precision, int opts, const char *
LWPOLY *patch;
/* Open outmost tag */
- stringbuffer_aprintf(sb, "<IndexedFaceSet convex='false' %s coordIndex='",defid);
+ stringbuffer_aprintf(sb, "<IndexedFaceSet convex='false' %s coordIndex='", defid);
j = 0;
- for (i=0; i<psur->ngeoms; i++)
+ for (i = 0; i < psur->ngeoms; i++)
{
- patch = (LWPOLY *) psur->geoms[i];
+ patch = (LWPOLY *)psur->geoms[i];
np = patch->rings[0]->npoints - 1;
- for (k=0; k < np ; k++)
+ for (k = 0; k < np; k++)
{
if (k)
{
stringbuffer_aprintf(sb, " ");
}
- stringbuffer_aprintf(sb,"%d", (j + k));
+ stringbuffer_aprintf(sb, "%d", (j + k));
}
- if (i < (psur->ngeoms - 1) )
+ if (i < (psur->ngeoms - 1))
{
stringbuffer_aprintf(sb, " -1 "); /* separator for each subgeom */
}
j += k;
}
- if ( X3D_USE_GEOCOORDS(opts) )
- stringbuffer_aprintf(sb, "'><GeoCoordinate geoSystem='\"GD\" \"WE\" \"%s\"' point='",
- ( (opts & LW_X3D_FLIP_XY) ? "latitude_first" : "longitude_first") );
- else stringbuffer_aprintf(sb, "'><Coordinate point='");
+ if (X3D_USE_GEOCOORDS(opts))
+ stringbuffer_aprintf(sb,
+ "'><GeoCoordinate geoSystem='\"GD\" \"WE\" \"%s\"' point='",
+ ((opts & LW_X3D_FLIP_XY) ? "latitude_first" : "longitude_first"));
+ else
+ stringbuffer_aprintf(sb, "'><Coordinate point='");
- for (i=0; i<psur->ngeoms; i++)
+ for (i = 0; i < psur->ngeoms; i++)
{
asx3d3_poly_sb(psur->geoms[i], precision, opts, 1, defid, sb);
- if (i < (psur->ngeoms - 1) )
+ if (i < (psur->ngeoms - 1))
{
- stringbuffer_aprintf(sb, " "); /* only add a trailing space if its not the last polygon in the set */
+ stringbuffer_aprintf(
+ sb, " "); /* only add a trailing space if its not the last polygon in the set */
}
}
@@ -430,26 +459,30 @@ asx3d3_tin_sb(const LWTIN *tin, int precision, int opts, const char *defid, stri
uint32_t k;
/* int dimension=2; */
- stringbuffer_aprintf(sb,"<IndexedTriangleSet %s index='",defid);
+ stringbuffer_aprintf(sb, "<IndexedTriangleSet %s index='", defid);
k = 0;
/** Fill in triangle index **/
- for (i=0; i<tin->ngeoms; i++)
+ for (i = 0; i < tin->ngeoms; i++)
{
- stringbuffer_aprintf(sb, "%d %d %d", k, (k+1), (k+2));
- if (i < (tin->ngeoms - 1) )
+ stringbuffer_aprintf(sb, "%d %d %d", k, (k + 1), (k + 2));
+ if (i < (tin->ngeoms - 1))
{
stringbuffer_aprintf(sb, " ");
}
k += 3;
}
- if ( X3D_USE_GEOCOORDS(opts) ) stringbuffer_aprintf(sb, "'><GeoCoordinate geoSystem='\"GD\" \"WE\" \"%s\"' point='", ( (opts & LW_X3D_FLIP_XY) ? "latitude_first" : "longitude_first") );
- else stringbuffer_aprintf(sb, "'><Coordinate point='");
+ if (X3D_USE_GEOCOORDS(opts))
+ stringbuffer_aprintf(sb,
+ "'><GeoCoordinate geoSystem='\"GD\" \"WE\" \"%s\"' point='",
+ ((opts & LW_X3D_FLIP_XY) ? "latitude_first" : "longitude_first"));
+ else
+ stringbuffer_aprintf(sb, "'><Coordinate point='");
- for (i=0; i<tin->ngeoms; i++)
+ for (i = 0; i < tin->ngeoms; i++)
{
asx3d3_triangle_sb(tin->geoms[i], precision, opts, defid, sb);
- if (i < (tin->ngeoms - 1) )
+ if (i < (tin->ngeoms - 1))
{
stringbuffer_aprintf(sb, " ");
}
@@ -460,6 +493,53 @@ asx3d3_tin_sb(const LWTIN *tin, int precision, int opts, const char *defid, stri
return stringbuffer_aprintf(sb, "'/></IndexedTriangleSet>");
}
+static int
+asx3d3_nurbscurve_sb(const LWNURBSCURVE *curve, int precision, int opts, const char *defid, stringbuffer_t *sb)
+{
+ uint32_t i;
+
+ if (!curve->points || curve->points->npoints == 0)
+ return LW_SUCCESS;
+
+ stringbuffer_aprintf(sb, "<NurbsCurve %s order='%u'", defid, curve->degree + 1);
+
+ if (curve->knots && curve->nknots > 0)
+ {
+ stringbuffer_append_len(sb, " knot='", strlen(" knot='"));
+ for (i = 0; i < curve->nknots; i++)
+ {
+ if (i)
+ stringbuffer_append_len(sb, " ", 1);
+ stringbuffer_append_double(sb, curve->knots[i], precision);
+ }
+ stringbuffer_append_len(sb, "'", 1);
+ }
+
+ if (curve->weights && curve->nweights > 0)
+ {
+ stringbuffer_append_len(sb, " weight='", strlen(" weight='"));
+ for (i = 0; i < curve->nweights; i++)
+ {
+ if (i)
+ stringbuffer_append_len(sb, " ", 1);
+ stringbuffer_append_double(sb, curve->weights[i], precision);
+ }
+ stringbuffer_append_len(sb, "'", 1);
+ }
+
+ if (X3D_USE_GEOCOORDS(opts))
+ stringbuffer_aprintf(
+ sb,
+ "><GeoCoordinate containerField='controlPoint' geoSystem='\"GD\" \"WE\" \"%s\"' point='",
+ ((opts & LW_X3D_FLIP_XY) ? "latitude_first" : "longitude_first"));
+ else
+ stringbuffer_append_len(sb,
+ "><Coordinate containerField='controlPoint' point='",
+ strlen("><Coordinate containerField='controlPoint' point='"));
+ ptarray_to_x3d3_sb(curve->points, precision, opts, LW_FALSE, LW_TRUE, sb);
+ return stringbuffer_aprintf(sb, "' /></NurbsCurve>");
+}
+
static int
asx3d3_collection_sb(const LWCOLLECTION *col, int precision, int opts, const char *defid, stringbuffer_t *sb)
{
@@ -473,48 +553,49 @@ asx3d3_collection_sb(const LWCOLLECTION *col, int precision, int opts, const cha
stringbuffer_aprintf(sb, "<%sGroup>", defid);
#endif
- for (i=0; i<col->ngeoms; i++)
+ for (i = 0; i < col->ngeoms; i++)
{
subgeom = col->geoms[i];
+
+ if (subgeom->type == COLLECTIONTYPE)
+ {
+ if (asx3d3_collection_sb((LWCOLLECTION *)subgeom, precision, opts, defid, sb) == LW_FAILURE)
+ return LW_FAILURE;
+ continue;
+ }
+
stringbuffer_aprintf(sb, "<Shape%s>", defid);
- if ( subgeom->type == POINTTYPE )
- {
- asx3d3_point_sb((LWPOINT *)subgeom, precision, opts, defid, sb);
- }
- else if ( subgeom->type == LINETYPE )
- {
- asx3d3_line_sb((LWLINE *)subgeom, precision, opts, defid, sb);
- }
- else if ( subgeom->type == POLYGONTYPE )
+ if (subgeom->type == POINTTYPE)
{
LWCOLLECTION *tmp = (LWCOLLECTION *)lwgeom_as_multi(subgeom);
- asx3d3_multi_sb(tmp, precision, opts, defid, sb);
+ int rv;
+ if (!tmp)
+ return LW_FAILURE;
+ rv = asx3d3_multi_sb(tmp, precision, opts, defid, sb);
lwcollection_free(tmp);
+ if (rv == LW_FAILURE)
+ return LW_FAILURE;
}
- else if ( subgeom->type == TINTYPE )
+ else if (subgeom->type == TRIANGLETYPE)
{
- asx3d3_tin_sb((LWTIN *)subgeom, precision, opts, defid, sb);
+ LWTIN *tmp = (LWTIN *)lwgeom_as_multi(subgeom);
+ int rv;
+ if (!tmp)
+ return LW_FAILURE;
+ rv = asx3d3_tin_sb(tmp, precision, opts, defid, sb);
+ lwcollection_free((LWCOLLECTION *)tmp);
+ if (rv == LW_FAILURE)
+ return LW_FAILURE;
}
- else if ( subgeom->type == POLYHEDRALSURFACETYPE )
- {
- asx3d3_psurface_sb((LWPSURFACE *)subgeom, precision, opts, defid, sb);
- }
- else if ( lwgeom_is_collection(subgeom) )
- {
- if ( subgeom->type == COLLECTIONTYPE )
- asx3d3_collection_sb((LWCOLLECTION *)subgeom, precision, opts, defid, sb);
- else
- asx3d3_multi_sb((LWCOLLECTION *)subgeom, precision, opts, defid, sb);
- }
- else
- lwerror("asx3d3_collection_buf: unknown geometry type");
+ else if (lwgeom_to_x3d3_sb(subgeom, precision, opts, defid, sb) == LW_FAILURE)
+ return LW_FAILURE;
stringbuffer_aprintf(sb, "</Shape>");
}
/* Close outmost tag */
#ifdef PGIS_X3D_OUTERMOST_TAGS
- stringbuffer_aprintf(sb, "</%sGroup>", defid);
+ stringbuffer_aprintf(sb, "</%sGroup>", defid);
#endif
return LW_SUCCESS;
@@ -532,10 +613,11 @@ ptarray_to_x3d3_sb(POINTARRAY *pa, int precision, int opts, int is_closed, int f
if (!FLAGS_GET_Z(pa->flags) && !force_3d)
{
- for (i=0; i<pa->npoints; i++)
+ for (i = 0; i < pa->npoints; i++)
{
- /** Only output the point if it is not the last point of a closed object or it is a non-closed type **/
- if ( !is_closed || i < (pa->npoints - 1) )
+ /** Only output the point if it is not the last point of a closed object or it is a non-closed
+ * type **/
+ if (!is_closed || i < (pa->npoints - 1))
{
POINT2D pt;
getPoint2d_p(pa, i, &pt);
@@ -543,9 +625,10 @@ ptarray_to_x3d3_sb(POINTARRAY *pa, int precision, int opts, int is_closed, int f
lwprint_double(pt.x, precision, x);
lwprint_double(pt.y, precision, y);
- if ( i ) stringbuffer_append_len(sb," ",1);
+ if (i)
+ stringbuffer_append_len(sb, " ", 1);
- if ( ( opts & LW_X3D_FLIP_XY) )
+ if ((opts & LW_X3D_FLIP_XY))
stringbuffer_aprintf(sb, "%s %s", y, x);
else
stringbuffer_aprintf(sb, "%s %s", x, y);
@@ -578,10 +661,11 @@ ptarray_to_x3d3_sb(POINTARRAY *pa, int precision, int opts, int is_closed, int f
}
else
{
- for (i=0; i<pa->npoints; i++)
+ for (i = 0; i < pa->npoints; i++)
{
- /** Only output the point if it is not the last point of a closed object or it is a non-closed type **/
- if ( !is_closed || i < (pa->npoints - 1) )
+ /** Only output the point if it is not the last point of a closed object or it is a non-closed
+ * type **/
+ if (!is_closed || i < (pa->npoints - 1))
{
POINT4D pt;
getPoint4d_p(pa, i, &pt);
@@ -590,9 +674,10 @@ ptarray_to_x3d3_sb(POINTARRAY *pa, int precision, int opts, int is_closed, int f
lwprint_double(pt.y, precision, y);
lwprint_double(pt.z, precision, z);
- if ( i ) stringbuffer_append_len(sb," ",1);
+ if (i)
+ stringbuffer_append_len(sb, " ", 1);
- if ( ( opts & LW_X3D_FLIP_XY) )
+ if ((opts & LW_X3D_FLIP_XY))
stringbuffer_aprintf(sb, "%s %s %s", y, x, z);
else
stringbuffer_aprintf(sb, "%s %s %s", x, y, z);
diff --git a/liblwgeom/lwout_x3d.h b/liblwgeom/lwout_x3d.h
index 0efb67d957..ebc4523fb1 100644
--- a/liblwgeom/lwout_x3d.h
+++ b/liblwgeom/lwout_x3d.h
@@ -24,9 +24,9 @@
**********************************************************************/
/**
-* @file X3D output routines.
-*
-**********************************************************************/
+ * @file X3D output routines.
+ *
+ **********************************************************************/
#include <string.h>
#include "liblwgeom_internal.h"
#include "stringbuffer.h"
@@ -41,6 +41,8 @@ static int
asx3d3_triangle_sb(const LWTRIANGLE *triangle, int precision, int opts, const char *defid, stringbuffer_t *sb);
static int asx3d3_multi_sb(const LWCOLLECTION *col, int precision, int opts, const char *defid, stringbuffer_t *sb);
+static int
+asx3d3_nurbscurve_sb(const LWNURBSCURVE *curve, int precision, int opts, const char *defid, stringbuffer_t *sb);
static int asx3d3_psurface_sb(const LWPSURFACE *psur, int precision, int opts, const char *defid, stringbuffer_t *sb);
static int asx3d3_tin_sb(const LWTIN *tin, int precision, int opts, const char *defid, stringbuffer_t *sb);
diff --git a/regress/core/tickets.sql b/regress/core/tickets.sql
index 643d921ea6..2525950a27 100644
--- a/regress/core/tickets.sql
+++ b/regress/core/tickets.sql
@@ -1012,6 +1012,12 @@ SELECT '#3627b', ST_Equals(geom, ST_LineFromEncodedPolyline(ST_AsEncodedPolyline
-- #3704
SELECT '#3704', ST_AsX3D('LINESTRING EMPTY') = '';
+-- #3705
+SELECT '#3705a', ST_AsX3D('MULTILINESTRING((2 3,4 5))'::geometry, 0) LIKE '<IndexedLineSet%<Coordinate%';
+SELECT '#3705b', ST_AsX3D('MULTIPOLYGON(((0 0,1 0,0 1,0 0)))'::geometry, 0) LIKE '<IndexedFaceSet%<Coordinate%';
+SELECT '#3705c', ST_AsX3D('GEOMETRYCOLLECTION(POINT(1 2),POINT(3 4))'::geometry, 0) LIKE '<Shape><Polypoint2D%';
+SELECT '#3705d', ST_AsX3D('POINTM(1 2 3)'::geometry, 0);
+
-- #3709
select '#3709', ST_SnapToGrid(ST_Project('SRID=4326;POINT(1 1)'::geography, 100000, 20)::geometry, 0.0001) = ST_SnapToGrid(ST_Project('SRID=4326;POINT(1 1)'::geography, -100000, 20+pi())::geometry, 0.0001);
@@ -1403,6 +1409,17 @@ SELECT '#4599-1', ST_AsEWKT(ST_AddPoint(ST_GeomFromEWKT('LINESTRING(0 0 1, 1 1 1
SELECT '#4599-2', ST_AsEWKT(ST_AddPoint(ST_GeomFromEWKT('LINESTRING(0 0 1, 1 1 1)'), ST_MakePoint(1, 2, 3), 0));
SELECT '#4599-3', ST_AsEWKT(ST_AddPoint(ST_GeomFromEWKT('LINESTRING(0 0 1, 1 1 1)'), ST_MakePoint(1, 2, 3), -1));
+SELECT '#1416a', ST_AsX3D('CIRCULARSTRING(0 0 1,1 1 1,2 0 1)'::geometry, 0) LIKE '<LineSet%';
+SELECT '#1416b', ST_AsX3D('COMPOUNDCURVE(CIRCULARSTRING(0 0 1,1 1 1,2 0 1),(2 0 1,3 1 1))'::geometry, 0) LIKE '<LineSet%';
+SELECT '#1416c', ST_AsX3D('CURVEPOLYGON(CIRCULARSTRING(0 0 1,1 1 1,2 0 1,1 -1 1,0 0 1))'::geometry, 0) LIKE '<IndexedFaceSet%';
+SELECT '#1416d', ST_AsX3D('MULTICURVE(CIRCULARSTRING(0 0 1,1 1 1,2 0 1))'::geometry, 0) LIKE '<IndexedLineSet%';
+SELECT '#1416e', ST_AsX3D('MULTISURFACE(CURVEPOLYGON(CIRCULARSTRING(0 0 1,1 1 1,2 0 1,1 -1 1,0 0 1)))'::geometry, 0) LIKE '<IndexedFaceSet%';
+SELECT '#1416f', ST_AsX3D('GEOMETRYCOLLECTION(CIRCULARSTRING(0 0 1,1 1 1,2 0 1))'::geometry, 0) LIKE '<Shape><LineSet%';
+SELECT '#1416g', ST_AsX3D('GEOMETRYCOLLECTION(COMPOUNDCURVE(CIRCULARSTRING(0 0 1,1 1 1,1 0 1),(1 0 1,0 1 1)))'::geometry, 0) LIKE '<Shape><LineSet%';
+SELECT '#1416h', ST_AsX3D('GEOMETRYCOLLECTION(CURVEPOLYGON(CIRCULARSTRING(0 0 1,1 1 1,2 0 1,1 -1 1,0 0 1)))'::geometry, 0) LIKE '<Shape><IndexedFaceSet%';
+SELECT '#1416i', ST_AsX3D('CIRCULARSTRING(0 0 1,1 1 1,2 0 1,1 -1 1,0 0 1)'::geometry, 0) LIKE '<LineSet vertexCount=''129''><Coordinate point=%';
+SELECT '#1416j', ST_AsX3D('NURBSCURVE(2, (0 0, 1 1, 2 0))'::geometry, 0) LIKE '%<Coordinate containerField=''controlPoint'' point=%';
+
SELECT '#4670-0', ST_AsEWKT(ST_AddPoint('LINESTRING(0 0, 1 1, 3 3, 4 4)'::geometry, 'POINT(2 2)'::geometry, 0));
SELECT '#4670-1', ST_AsEWKT(ST_AddPoint('LINESTRING(0 0, 1 1, 3 3, 4 4)'::geometry, 'POINT(2 2)'::geometry, 1));
SELECT '#4670-2', ST_AsEWKT(ST_AddPoint('LINESTRING(0 0, 1 1, 3 3, 4 4)'::geometry, 'POINT(2 2)'::geometry, 2));
diff --git a/regress/core/tickets_expected b/regress/core/tickets_expected
index 9cc8aefd9a..56515cc343 100644
--- a/regress/core/tickets_expected
+++ b/regress/core/tickets_expected
@@ -306,6 +306,10 @@ ERROR: invalid KML representation
#3627a|o}~~|AdshNoSsBgd at eGoBlm@wKhj@~@?
#3627b|t
#3704|t
+#3705a|t
+#3705b|t
+#3705c|t
+#3705d|1 2
#3709|t
NOTICE: Hole lies outside shell at or near point 25495368.044100001 6671726.9312000005
#3719a|f
@@ -436,6 +440,16 @@ ERROR: BOX2D_construct: args can not be empty points
#4599-1|LINESTRING(0 0 1,1 1 1,1 2 3)
#4599-2|LINESTRING(1 2 3,0 0 1,1 1 1)
#4599-3|LINESTRING(0 0 1,1 1 1,1 2 3)
+#1416a|t
+#1416b|t
+#1416c|t
+#1416d|t
+#1416e|t
+#1416f|t
+#1416g|t
+#1416h|t
+#1416i|t
+#1416j|t
#4670-0|LINESTRING(2 2,0 0,1 1,3 3,4 4)
#4670-1|LINESTRING(0 0,2 2,1 1,3 3,4 4)
#4670-2|LINESTRING(0 0,1 1,2 2,3 3,4 4)
-----------------------------------------------------------------------
Summary of changes:
NEWS | 6 +-
doc/po/templates/postgis-manual.pot | 438 ++++++++++++++++++++++++++++++++++--
doc/reference_output.xml | 18 +-
liblwgeom/cunit/cu_out_x3d.c | 224 +++++++++++++-----
liblwgeom/lwout_x3d.c | 314 ++++++++++++++++----------
liblwgeom/lwout_x3d.h | 8 +-
regress/core/tickets.sql | 18 ++
regress/core/tickets_expected | 15 ++
8 files changed, 847 insertions(+), 194 deletions(-)
hooks/post-receive
--
PostGIS
More information about the postgis-tickets
mailing list