Skip to content

Commit c7aeb77

Browse files
author
Hugo Mercier
committedOct 21, 2015
PostGIS: Allow to load TIN, PS and Triangle layers
The postgres provider is modified so that layers with TIN, PolyhedralSurface and Triangle geometries can be loaded. Geometries are converted to MultiPolygons (and Polygons for Triangles). The postgres test is completed to cover the loading of different types of layers
1 parent c45fc09 commit c7aeb77

File tree

5 files changed

+196
-51
lines changed

5 files changed

+196
-51
lines changed
 

‎src/core/geometry/qgsabstractgeometryv2.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,12 @@ QgsRectangle QgsAbstractGeometryV2::boundingBox() const
5555

5656
bool QgsAbstractGeometryV2::is3D() const
5757
{
58-
return(( mWkbType >= 1001 && mWkbType <= 1012 ) || ( mWkbType > 3000 || mWkbType & 0x80000000 ) );
Code has comments. Press enter to view.
58+
return(( mWkbType >= 1001 && mWkbType <= 1017 ) || ( mWkbType > 3000 || mWkbType & 0x80000000 ) );
5959
}
6060

6161
bool QgsAbstractGeometryV2::isMeasure() const
6262
{
63-
return ( mWkbType >= 2001 && mWkbType <= 3012 );
63+
return ( mWkbType >= 2001 && mWkbType <= 3017 );
6464
}
6565

6666
#if 0

‎src/providers/postgres/qgspostgresconn.cpp

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,15 +1527,32 @@ int QgsPostgresConn::postgisWkbTypeDim( QGis::WkbType wkbType )
15271527

15281528
QGis::WkbType QgsPostgresConn::wkbTypeFromPostgis( QString type )
15291529
{
1530+
// Polyhedral surfaces and TIN are stored in PostGIS as geometry collections
1531+
// of Polygons and Triangles.
1532+
// So, since QGIS does not natively support PS and TIN, but we would like to open them if possible,
1533+
// we consider them as multipolygons. WKB will be converted by the feature iterator
1534+
if (( type == "POLYHEDRALSURFACE" ) || ( type == "TIN" ) )
1535+
{
1536+
return QGis::WKBMultiPolygon;
1537+
}
1538+
else if ( type == "TRIANGLE" )
1539+
{
1540+
return QGis::WKBPolygon;
1541+
}
15301542
return ( QGis::WkbType )QgsWKBTypes::parseType( type );
15311543
}
15321544

15331545
QgsWKBTypes::Type QgsPostgresConn::wkbTypeFromOgcWkbType( unsigned int wkbType )
15341546
{
1535-
// polyhedralsurface / TIN / triangle => MultiPolygon
1536-
if ( wkbType % 100 >= 15 )
1537-
wkbType = wkbType / 1000 * 1000 + QGis::WKBMultiPolygon;
1538-
1547+
// PolyhedralSurface => MultiPolygon
1548+
if ( wkbType % 1000 == 15 )
1549+
return ( QgsWKBTypes::Type )( wkbType / 1000 * 1000 + QgsWKBTypes::MultiPolygon );
1550+
// TIN => MultiPolygon
1551+
if ( wkbType % 1000 == 16 )
1552+
return ( QgsWKBTypes::Type )( wkbType / 1000 * 1000 + QgsWKBTypes::MultiPolygon );
1553+
// Triangle => Polygon
1554+
if ( wkbType % 1000 == 17 )
1555+
return ( QgsWKBTypes::Type )( wkbType / 1000 * 1000 + QgsWKBTypes::Polygon );
15391556
return ( QgsWKBTypes::Type ) wkbType;
15401557
}
15411558

‎src/providers/postgres/qgspostgresfeatureiterator.cpp

Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -451,57 +451,42 @@ bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int
451451
memcpy( featureGeom, PQgetvalue( queryResult.result(), row, col ), returnedLength );
452452
memset( featureGeom + returnedLength, 0, 1 );
453453

454-
// modify 2.5D WKB types to make them compliant with OGR
455454
unsigned int wkbType;
456455
memcpy( &wkbType, featureGeom + 1, sizeof( wkbType ) );
457-
wkbType = QgsPostgresConn::wkbTypeFromOgcWkbType( wkbType );
458-
memcpy( featureGeom + 1, &wkbType, sizeof( wkbType ) );
456+
QgsWKBTypes::Type newType = QgsPostgresConn::wkbTypeFromOgcWkbType( wkbType );
459457

460-
// change wkb type of inner geometries
461-
if ( wkbType == QGis::WKBMultiPoint25D ||
462-
wkbType == QGis::WKBMultiLineString25D ||
463-
wkbType == QGis::WKBMultiPolygon25D )
458+
if (( unsigned int )newType != wkbType )
459+
{
460+
// overwrite type
461+
unsigned int n = newType;
462+
memcpy( featureGeom + 1, &n, sizeof( n ) );
463+
}
464+
465+
// PostGIS stores TIN as a collection of Triangles.
466+
// Since Triangles are not supported, they have to be converted to Polygons
467+
const int nDims = 2 + ( QgsWKBTypes::hasZ( newType ) ? 1 : 0 ) + ( QgsWKBTypes::hasM( newType ) ? 1 : 0 );
468+
if ( wkbType % 1000 == 16 )
464469
{
465470
unsigned int numGeoms;
466471
memcpy( &numGeoms, featureGeom + 5, sizeof( unsigned int ) );
467472
unsigned char *wkb = featureGeom + 9;
468473
for ( unsigned int i = 0; i < numGeoms; ++i )
469474
{
470-
unsigned int localType;
471-
memcpy( &localType, wkb + 1, sizeof( localType ) );
472-
localType = QgsPostgresConn::wkbTypeFromOgcWkbType( localType );
475+
const unsigned int localType = QgsWKBTypes::singleType( newType ); // polygon(Z|M)
473476
memcpy( wkb + 1, &localType, sizeof( localType ) );
474477

475478
// skip endian and type info
476479
wkb += sizeof( unsigned int ) + 1;
477480

478481
// skip coordinates
479-
switch ( wkbType )
482+
unsigned int nRings;
483+
memcpy( &nRings, wkb, sizeof( int ) );
484+
wkb += sizeof( int );
485+
for ( unsigned int j = 0; j < nRings; ++j )
480486
{
481-
case QGis::WKBMultiPoint25D:
482-
wkb += sizeof( double ) * 3;
483-
break;
484-
case QGis::WKBMultiLineString25D:
485-
{
486-
unsigned int nPoints;
487-
memcpy( &nPoints, wkb, sizeof( int ) );
488-
wkb += sizeof( int ) + sizeof( double ) * 3 * nPoints;
489-
}
490-
break;
491-
default:
492-
case QGis::WKBMultiPolygon25D:
493-
{
494-
unsigned int nRings;
495-
memcpy( &nRings, wkb, sizeof( int ) );
496-
wkb += sizeof( int );
497-
for ( unsigned int j = 0; j < nRings; ++j )
498-
{
499-
unsigned int nPoints;
500-
memcpy( &nPoints, wkb, sizeof( int ) );
501-
wkb += sizeof( nPoints ) + sizeof( double ) * 3 * nPoints;
502-
}
503-
}
504-
break;
487+
unsigned int nPoints;
488+
memcpy( &nPoints, wkb, sizeof( int ) );
489+
wkb += sizeof( nPoints ) + sizeof( double ) * nDims * nPoints;
505490
}
506491
}
507492
}

‎tests/src/python/test_provider_postgres.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import qgis
1616
import os
17+
import sys
1718
from qgis.core import NULL
1819

1920
from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsFeature, QgsProviderRegistry
@@ -34,8 +35,11 @@ class TestPyQgsPostgresProvider(TestCase, ProviderTestCase):
3435
@classmethod
3536
def setUpClass(cls):
3637
"""Run before all tests"""
38+
cls.dbconn = u'dbname=\'qgis_test\' host=localhost port=5432 user=\'postgres\' password=\'postgres\''
39+
if os.environ.has_key('QGIS_PGTEST_DB'):
40+
cls.dbconn = os.environ['QGIS_PGTEST_DB']
3741
# Create test layer
38-
cls.vl = QgsVectorLayer(u'dbname=\'qgis_test\' host=localhost port=5432 user=\'postgres\' password=\'postgres\' sslmode=disable key=\'pk\' srid=4326 type=POINT table="qgis_test"."someData" (geom) sql=', 'test', 'postgres')
42+
cls.vl = QgsVectorLayer( cls.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POINT table="qgis_test"."someData" (geom) sql=', 'test', 'postgres')
3943
assert(cls.vl.isValid())
4044
cls.provider = cls.vl.dataProvider()
4145

@@ -55,5 +59,31 @@ def testDefaultValue(self):
5559
assert self.provider.defaultValue(1) == NULL
5660
assert self.provider.defaultValue(2) == '\'qgis\'::text'
5761

62+
def testWkbTypes(self):
63+
def test_table( dbconn, table_name, wkt ):
64+
vl = QgsVectorLayer( '%s srid=4326 table="qgis_test".%s (geom) sql=' % (dbconn, table_name), "testgeom", "postgres" )
65+
assert( vl.isValid() )
66+
for f in vl.getFeatures():
67+
print f.geometry().exportToWkt(), wkt
68+
assert f.geometry().exportToWkt() == wkt
69+
70+
test_table(self.dbconn, 'p2d', 'Polygon ((0 0, 1 0, 1 1, 0 1, 0 0))')
71+
test_table(self.dbconn, 'p3d', 'PolygonZ ((0 0 0, 1 0 0, 1 1 0, 0 1 0, 0 0 0))')
72+
test_table(self.dbconn, 'triangle2d', 'Polygon ((0 0, 1 0, 1 1, 0 0))')
73+
test_table(self.dbconn, 'triangle3d', 'PolygonZ ((0 0 0, 1 0 0, 1 1 0, 0 0 0))')
74+
test_table(self.dbconn, 'tin2d', 'MultiPolygon (((0 0, 1 0, 1 1, 0 0)),((0 0, 0 1, 1 1, 0 0)))')
75+
test_table(self.dbconn, 'tin3d', 'MultiPolygonZ (((0 0 0, 1 0 0, 1 1 0, 0 0 0)),((0 0 0, 0 1 0, 1 1 0, 0 0 0)))')
76+
test_table(self.dbconn, 'ps2d', 'MultiPolygon (((0 0, 1 0, 1 1, 0 1, 0 0)))')
77+
test_table(self.dbconn, 'ps3d', 'MultiPolygonZ (((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)),((0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1)),((0 0 0, 0 0 1, 0 1 1, 0 1 0, 0 0 0)),((0 1 0, 0 1 1, 1 1 1, 1 1 0, 0 1 0)),((1 1 0, 1 1 1, 1 0 1, 1 0 0, 1 1 0)),((1 0 0, 1 0 1, 0 0 1, 0 0 0, 1 0 0)))')
78+
test_table(self.dbconn, 'mp3d', 'MultiPolygonZ (((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)),((0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1)),((0 0 0, 0 0 1, 0 1 1, 0 1 0, 0 0 0)),((0 1 0, 0 1 1, 1 1 1, 1 1 0, 0 1 0)),((1 1 0, 1 1 1, 1 0 1, 1 0 0, 1 1 0)),((1 0 0, 1 0 1, 0 0 1, 0 0 0, 1 0 0)))')
79+
test_table(self.dbconn, 'pt2d', 'Point (0 0)')
80+
test_table(self.dbconn, 'pt3d', 'PointZ (0 0 0)')
81+
test_table(self.dbconn, 'ls2d', 'LineString (0 0, 1 1)')
82+
test_table(self.dbconn, 'ls3d', 'LineStringZ (0 0 0, 1 1 1)')
83+
test_table(self.dbconn, 'mpt2d', 'MultiPoint ((0 0),(1 1))')
84+
test_table(self.dbconn, 'mpt3d', 'MultiPointZ ((0 0 0),(1 1 1))')
85+
test_table(self.dbconn, 'mls2d', 'MultiLineString ((0 0, 1 1),(2 2, 3 3))')
86+
test_table(self.dbconn, 'mls3d', 'MultiLineStringZ ((0 0 0, 1 1 1),(2 2 2, 3 3 3))')
87+
5888
if __name__ == '__main__':
5989
unittest.main()

‎tests/testdata/provider/testdata.sql

Lines changed: 122 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@ SET client_min_messages = warning;
1818
-- Name: qgis_test; Type: SCHEMA; Schema: -; Owner: postgres
1919
--
2020

21-
CREATE SCHEMA qgis_test;
22-
21+
CREATE EXTENSION IF NOT EXISTS postgis;
2322

24-
ALTER SCHEMA qgis_test OWNER TO postgres;
23+
DROP SCHEMA IF EXISTS qgis_test CASCADE;
24+
CREATE SCHEMA qgis_test;
2525

26-
SET search_path = qgis_test, pg_catalog;
2726

2827
SET default_tablespace = '';
2928

@@ -34,23 +33,21 @@ SET default_with_oids = false;
3433
-- Name: someData; Type: TABLE; Schema: qgis_test; Owner: postgres; Tablespace:
3534
--
3635

37-
CREATE TABLE "someData" (
36+
CREATE TABLE qgis_test."someData" (
3837
pk SERIAL NOT NULL,
3938
cnt integer,
4039
name text DEFAULT 'qgis',
4140
geom public.geometry(Point,4326)
4241
);
4342

4443

45-
ALTER TABLE qgis_test."someData" OWNER TO postgres;
46-
4744
--
4845
-- TOC entry 4068 (class 0 OID 377761)
4946
-- Dependencies: 171
5047
-- Data for Name: someData; Type: TABLE DATA; Schema: qgis_test; Owner: postgres
5148
--
5249

53-
COPY "someData" (pk, cnt, name, geom) FROM stdin;
50+
COPY qgis_test."someData" (pk, cnt, name, geom) FROM stdin;
5451
5 -200 \N 0101000020E61000001D5A643BDFC751C01F85EB51B88E5340
5552
3 300 Pear \N
5653
1 100 Orange 0101000020E61000006891ED7C3F9551C085EB51B81E955040
@@ -64,7 +61,7 @@ COPY "someData" (pk, cnt, name, geom) FROM stdin;
6461
-- Name: someData_pkey; Type: CONSTRAINT; Schema: qgis_test; Owner: postgres; Tablespace:
6562
--
6663

67-
ALTER TABLE ONLY "someData"
64+
ALTER TABLE ONLY qgis_test."someData"
6865
ADD CONSTRAINT "someData_pkey" PRIMARY KEY (pk);
6966

7067

@@ -74,3 +71,119 @@ ALTER TABLE ONLY "someData"
7471
-- PostgreSQL database dump complete
7572
--
7673

74+
CREATE TABLE qgis_test.p2d(
75+
id int,
76+
geom Geometry(Polygon,4326)
77+
);
78+
INSERT INTO qgis_test.p2d values (1, 'srid=4326;Polygon((0 0,1 0,1 1,0 1,0 0))'::geometry);
79+
80+
CREATE TABLE qgis_test.p3d(
81+
id int,
82+
geom Geometry(PolygonZ,4326)
83+
);
84+
INSERT INTO qgis_test.p3d values (1, 'srid=4326;Polygon((0 0 0,1 0 0,1 1 0,0 1 0,0 0 0))'::geometry);
85+
86+
CREATE TABLE qgis_test.triangle2d(
87+
id int,
88+
geom Geometry(Triangle,4326)
89+
);
90+
91+
INSERT INTO qgis_test.triangle2d values (1, 'srid=4326;triangle((0 0,1 0,1 1,0 0))'::geometry);
92+
93+
CREATE TABLE qgis_test.triangle3d(
94+
id int,
95+
geom Geometry(TriangleZ,4326)
96+
);
97+
98+
INSERT INTO qgis_test.triangle3d values (1, 'srid=4326;triangle((0 0 0,1 0 0,1 1 0,0 0 0))'::geometry);
99+
100+
CREATE TABLE qgis_test.tin2d(
101+
id int,
102+
geom Geometry(TIN,4326)
103+
);
104+
105+
INSERT INTO qgis_test.tin2d values (1, 'srid=4326;TIN(((0 0,1 0,1 1,0 0)),((0 0,0 1,1 1,0 0)))'::geometry);
106+
107+
CREATE TABLE qgis_test.tin3d(
108+
id int,
109+
geom Geometry(TINZ,4326)
110+
);
111+
112+
INSERT INTO qgis_test.tin3d values (1, 'srid=4326;TIN(((0 0 0,1 0 0,1 1 0,0 0 0)),((0 0 0,0 1 0,1 1 0,0 0 0)))'::geometry);
113+
114+
CREATE TABLE qgis_test.ps2d(
115+
id int,
116+
geom Geometry(PolyhedralSurface,4326)
117+
);
118+
119+
INSERT INTO qgis_test.ps2d values (1, 'srid=4326;PolyhedralSurface(((0 0,1 0,1 1,0 1,0 0)))'::geometry);
120+
121+
CREATE TABLE qgis_test.ps3d(
122+
id int,
123+
geom Geometry(PolyhedralSurfaceZ,4326)
124+
);
125+
126+
INSERT INTO qgis_test.ps3d values (1, 'srid=4326;PolyhedralSurface Z(((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),((0 0 1,1 0 1,1 1 1,0 1 1,0 0 1)),((0 0 0,0 0 1,0 1 1,0 1 0,0 0 0)),((0 1 0,0 1 1,1 1 1,1 1 0,0 1 0)),((1 1 0,1 1 1,1 0 1,1 0 0,1 1 0)),((1 0 0,1 0 1,0 0 1,0 0 0,1 0 0)))'::geometry);
127+
128+
CREATE TABLE qgis_test.mp3d(
129+
id int,
130+
geom Geometry(MultipolygonZ,4326)
131+
);
132+
133+
INSERT INTO qgis_test.mp3d values (1, 'srid=4326;Multipolygon Z(((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),((0 0 1,1 0 1,1 1 1,0 1 1,0 0 1)),((0 0 0,0 0 1,0 1 1,0 1 0,0 0 0)),((0 1 0,0 1 1,1 1 1,1 1 0,0 1 0)),((1 1 0,1 1 1,1 0 1,1 0 0,1 1 0)),((1 0 0,1 0 1,0 0 1,0 0 0,1 0 0)))'::geometry);
134+
135+
CREATE TABLE qgis_test.pt2d(
136+
id int,
137+
geom Geometry(Point,4326)
138+
);
139+
140+
INSERT INTO qgis_test.pt2d values (1, 'srid=4326;Point(0 0)'::geometry);
141+
142+
CREATE TABLE qgis_test.pt3d(
143+
id int,
144+
geom Geometry(PointZ,4326)
145+
);
146+
147+
INSERT INTO qgis_test.pt3d values (1, 'srid=4326;PointZ(0 0 0)'::geometry);
148+
149+
CREATE TABLE qgis_test.ls2d(
150+
id int,
151+
geom Geometry(LineString,4326)
152+
);
153+
154+
INSERT INTO qgis_test.ls2d values (1, 'srid=4326;Linestring(0 0, 1 1)'::geometry);
155+
156+
CREATE TABLE qgis_test.ls3d(
157+
id int,
158+
geom Geometry(LineStringZ,4326)
159+
);
160+
161+
INSERT INTO qgis_test.ls3d values (1, 'srid=4326;Linestring(0 0 0, 1 1 1)'::geometry);
162+
163+
CREATE TABLE qgis_test.mpt2d(
164+
id int,
165+
geom Geometry(MultiPoint,4326)
166+
);
167+
168+
INSERT INTO qgis_test.mpt2d values (1, 'srid=4326;MultiPoint((0 0),(1 1))'::geometry);
169+
170+
CREATE TABLE qgis_test.mpt3d(
171+
id int,
172+
geom Geometry(MultiPointZ,4326)
173+
);
174+
175+
INSERT INTO qgis_test.mpt3d values (1, 'srid=4326;MultiPoint((0 0 0),(1 1 1))'::geometry);
176+
177+
CREATE TABLE qgis_test.mls2d(
178+
id int,
179+
geom Geometry(MultiLineString,4326)
180+
);
181+
182+
INSERT INTO qgis_test.mls2d values (1, 'srid=4326;MultiLineString((0 0, 1 1),(2 2, 3 3))'::geometry);
183+
184+
CREATE TABLE qgis_test.mls3d(
185+
id int,
186+
geom Geometry(MultiLineStringZ,4326)
187+
);
188+
189+
INSERT INTO qgis_test.mls3d values (1, 'srid=4326;MultiLineString((0 0 0, 1 1 1),(2 2 2, 3 3 3))'::geometry);

0 commit comments

Comments
 (0)
Please sign in to comment.