Skip to content

Commit

Permalink
Merge pull request #2322 from mhugo/fix_tin
Browse files Browse the repository at this point in the history
Postgres provider: allow to load layers with polyhedral surfaces or TINs
  • Loading branch information
Hugo Mercier committed Oct 21, 2015
2 parents 55babc3 + c7aeb77 commit a460e47
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 51 deletions.
4 changes: 2 additions & 2 deletions src/core/geometry/qgsabstractgeometryv2.cpp
Expand Up @@ -55,12 +55,12 @@ QgsRectangle QgsAbstractGeometryV2::boundingBox() const

bool QgsAbstractGeometryV2::is3D() const
{
return(( mWkbType >= 1001 && mWkbType <= 1012 ) || ( mWkbType > 3000 || mWkbType & 0x80000000 ) );
return(( mWkbType >= 1001 && mWkbType <= 1017 ) || ( mWkbType > 3000 || mWkbType & 0x80000000 ) );
}

bool QgsAbstractGeometryV2::isMeasure() const
{
return ( mWkbType >= 2001 && mWkbType <= 3012 );
return ( mWkbType >= 2001 && mWkbType <= 3017 );
}

#if 0
Expand Down
25 changes: 21 additions & 4 deletions src/providers/postgres/qgspostgresconn.cpp
Expand Up @@ -1527,15 +1527,32 @@ int QgsPostgresConn::postgisWkbTypeDim( QGis::WkbType wkbType )

QGis::WkbType QgsPostgresConn::wkbTypeFromPostgis( QString type )
{
// Polyhedral surfaces and TIN are stored in PostGIS as geometry collections
// of Polygons and Triangles.
// So, since QGIS does not natively support PS and TIN, but we would like to open them if possible,
// we consider them as multipolygons. WKB will be converted by the feature iterator
if (( type == "POLYHEDRALSURFACE" ) || ( type == "TIN" ) )
{
return QGis::WKBMultiPolygon;
}
else if ( type == "TRIANGLE" )
{
return QGis::WKBPolygon;
}
return ( QGis::WkbType )QgsWKBTypes::parseType( type );
}

QgsWKBTypes::Type QgsPostgresConn::wkbTypeFromOgcWkbType( unsigned int wkbType )
{
// polyhedralsurface / TIN / triangle => MultiPolygon
if ( wkbType % 100 >= 15 )
wkbType = wkbType / 1000 * 1000 + QGis::WKBMultiPolygon;

// PolyhedralSurface => MultiPolygon
if ( wkbType % 1000 == 15 )
return ( QgsWKBTypes::Type )( wkbType / 1000 * 1000 + QgsWKBTypes::MultiPolygon );
// TIN => MultiPolygon
if ( wkbType % 1000 == 16 )
return ( QgsWKBTypes::Type )( wkbType / 1000 * 1000 + QgsWKBTypes::MultiPolygon );
// Triangle => Polygon
if ( wkbType % 1000 == 17 )
return ( QgsWKBTypes::Type )( wkbType / 1000 * 1000 + QgsWKBTypes::Polygon );
return ( QgsWKBTypes::Type ) wkbType;
}

Expand Down
55 changes: 20 additions & 35 deletions src/providers/postgres/qgspostgresfeatureiterator.cpp
Expand Up @@ -451,57 +451,42 @@ bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int
memcpy( featureGeom, PQgetvalue( queryResult.result(), row, col ), returnedLength );
memset( featureGeom + returnedLength, 0, 1 );

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

// change wkb type of inner geometries
if ( wkbType == QGis::WKBMultiPoint25D ||
wkbType == QGis::WKBMultiLineString25D ||
wkbType == QGis::WKBMultiPolygon25D )
if (( unsigned int )newType != wkbType )
{
// overwrite type
unsigned int n = newType;
memcpy( featureGeom + 1, &n, sizeof( n ) );
}

// PostGIS stores TIN as a collection of Triangles.
// Since Triangles are not supported, they have to be converted to Polygons
const int nDims = 2 + ( QgsWKBTypes::hasZ( newType ) ? 1 : 0 ) + ( QgsWKBTypes::hasM( newType ) ? 1 : 0 );
if ( wkbType % 1000 == 16 )
{
unsigned int numGeoms;
memcpy( &numGeoms, featureGeom + 5, sizeof( unsigned int ) );
unsigned char *wkb = featureGeom + 9;
for ( unsigned int i = 0; i < numGeoms; ++i )
{
unsigned int localType;
memcpy( &localType, wkb + 1, sizeof( localType ) );
localType = QgsPostgresConn::wkbTypeFromOgcWkbType( localType );
const unsigned int localType = QgsWKBTypes::singleType( newType ); // polygon(Z|M)
memcpy( wkb + 1, &localType, sizeof( localType ) );

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

// skip coordinates
switch ( wkbType )
unsigned int nRings;
memcpy( &nRings, wkb, sizeof( int ) );
wkb += sizeof( int );
for ( unsigned int j = 0; j < nRings; ++j )
{
case QGis::WKBMultiPoint25D:
wkb += sizeof( double ) * 3;
break;
case QGis::WKBMultiLineString25D:
{
unsigned int nPoints;
memcpy( &nPoints, wkb, sizeof( int ) );
wkb += sizeof( int ) + sizeof( double ) * 3 * nPoints;
}
break;
default:
case QGis::WKBMultiPolygon25D:
{
unsigned int nRings;
memcpy( &nRings, wkb, sizeof( int ) );
wkb += sizeof( int );
for ( unsigned int j = 0; j < nRings; ++j )
{
unsigned int nPoints;
memcpy( &nPoints, wkb, sizeof( int ) );
wkb += sizeof( nPoints ) + sizeof( double ) * 3 * nPoints;
}
}
break;
unsigned int nPoints;
memcpy( &nPoints, wkb, sizeof( int ) );
wkb += sizeof( nPoints ) + sizeof( double ) * nDims * nPoints;
}
}
}
Expand Down
32 changes: 31 additions & 1 deletion tests/src/python/test_provider_postgres.py
Expand Up @@ -14,6 +14,7 @@

import qgis
import os
import sys
from qgis.core import NULL

from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsFeature, QgsProviderRegistry
Expand All @@ -34,8 +35,11 @@ class TestPyQgsPostgresProvider(TestCase, ProviderTestCase):
@classmethod
def setUpClass(cls):
"""Run before all tests"""
cls.dbconn = u'dbname=\'qgis_test\' host=localhost port=5432 user=\'postgres\' password=\'postgres\''
if os.environ.has_key('QGIS_PGTEST_DB'):
cls.dbconn = os.environ['QGIS_PGTEST_DB']
# Create test layer
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')
cls.vl = QgsVectorLayer( cls.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POINT table="qgis_test"."someData" (geom) sql=', 'test', 'postgres')
assert(cls.vl.isValid())
cls.provider = cls.vl.dataProvider()

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

def testWkbTypes(self):
def test_table( dbconn, table_name, wkt ):
vl = QgsVectorLayer( '%s srid=4326 table="qgis_test".%s (geom) sql=' % (dbconn, table_name), "testgeom", "postgres" )
assert( vl.isValid() )
for f in vl.getFeatures():
print f.geometry().exportToWkt(), wkt
assert f.geometry().exportToWkt() == wkt

test_table(self.dbconn, 'p2d', 'Polygon ((0 0, 1 0, 1 1, 0 1, 0 0))')
test_table(self.dbconn, 'p3d', 'PolygonZ ((0 0 0, 1 0 0, 1 1 0, 0 1 0, 0 0 0))')
test_table(self.dbconn, 'triangle2d', 'Polygon ((0 0, 1 0, 1 1, 0 0))')
test_table(self.dbconn, 'triangle3d', 'PolygonZ ((0 0 0, 1 0 0, 1 1 0, 0 0 0))')
test_table(self.dbconn, 'tin2d', 'MultiPolygon (((0 0, 1 0, 1 1, 0 0)),((0 0, 0 1, 1 1, 0 0)))')
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)))')
test_table(self.dbconn, 'ps2d', 'MultiPolygon (((0 0, 1 0, 1 1, 0 1, 0 0)))')
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)))')
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)))')
test_table(self.dbconn, 'pt2d', 'Point (0 0)')
test_table(self.dbconn, 'pt3d', 'PointZ (0 0 0)')
test_table(self.dbconn, 'ls2d', 'LineString (0 0, 1 1)')
test_table(self.dbconn, 'ls3d', 'LineStringZ (0 0 0, 1 1 1)')
test_table(self.dbconn, 'mpt2d', 'MultiPoint ((0 0),(1 1))')
test_table(self.dbconn, 'mpt3d', 'MultiPointZ ((0 0 0),(1 1 1))')
test_table(self.dbconn, 'mls2d', 'MultiLineString ((0 0, 1 1),(2 2, 3 3))')
test_table(self.dbconn, 'mls3d', 'MultiLineStringZ ((0 0 0, 1 1 1),(2 2 2, 3 3 3))')

if __name__ == '__main__':
unittest.main()
131 changes: 122 additions & 9 deletions tests/testdata/provider/testdata.sql
Expand Up @@ -18,12 +18,11 @@ SET client_min_messages = warning;
-- Name: qgis_test; Type: SCHEMA; Schema: -; Owner: postgres
--

CREATE SCHEMA qgis_test;

CREATE EXTENSION IF NOT EXISTS postgis;

ALTER SCHEMA qgis_test OWNER TO postgres;
DROP SCHEMA IF EXISTS qgis_test CASCADE;
CREATE SCHEMA qgis_test;

SET search_path = qgis_test, pg_catalog;

SET default_tablespace = '';

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

CREATE TABLE "someData" (
CREATE TABLE qgis_test."someData" (
pk SERIAL NOT NULL,
cnt integer,
name text DEFAULT 'qgis',
geom public.geometry(Point,4326)
);


ALTER TABLE qgis_test."someData" OWNER TO postgres;

--
-- TOC entry 4068 (class 0 OID 377761)
-- Dependencies: 171
-- Data for Name: someData; Type: TABLE DATA; Schema: qgis_test; Owner: postgres
--

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

ALTER TABLE ONLY "someData"
ALTER TABLE ONLY qgis_test."someData"
ADD CONSTRAINT "someData_pkey" PRIMARY KEY (pk);


Expand All @@ -74,3 +71,119 @@ ALTER TABLE ONLY "someData"
-- PostgreSQL database dump complete
--

CREATE TABLE qgis_test.p2d(
id int,
geom Geometry(Polygon,4326)
);
INSERT INTO qgis_test.p2d values (1, 'srid=4326;Polygon((0 0,1 0,1 1,0 1,0 0))'::geometry);

CREATE TABLE qgis_test.p3d(
id int,
geom Geometry(PolygonZ,4326)
);
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);

CREATE TABLE qgis_test.triangle2d(
id int,
geom Geometry(Triangle,4326)
);

INSERT INTO qgis_test.triangle2d values (1, 'srid=4326;triangle((0 0,1 0,1 1,0 0))'::geometry);

CREATE TABLE qgis_test.triangle3d(
id int,
geom Geometry(TriangleZ,4326)
);

INSERT INTO qgis_test.triangle3d values (1, 'srid=4326;triangle((0 0 0,1 0 0,1 1 0,0 0 0))'::geometry);

CREATE TABLE qgis_test.tin2d(
id int,
geom Geometry(TIN,4326)
);

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);

CREATE TABLE qgis_test.tin3d(
id int,
geom Geometry(TINZ,4326)
);

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);

CREATE TABLE qgis_test.ps2d(
id int,
geom Geometry(PolyhedralSurface,4326)
);

INSERT INTO qgis_test.ps2d values (1, 'srid=4326;PolyhedralSurface(((0 0,1 0,1 1,0 1,0 0)))'::geometry);

CREATE TABLE qgis_test.ps3d(
id int,
geom Geometry(PolyhedralSurfaceZ,4326)
);

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);

CREATE TABLE qgis_test.mp3d(
id int,
geom Geometry(MultipolygonZ,4326)
);

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);

CREATE TABLE qgis_test.pt2d(
id int,
geom Geometry(Point,4326)
);

INSERT INTO qgis_test.pt2d values (1, 'srid=4326;Point(0 0)'::geometry);

CREATE TABLE qgis_test.pt3d(
id int,
geom Geometry(PointZ,4326)
);

INSERT INTO qgis_test.pt3d values (1, 'srid=4326;PointZ(0 0 0)'::geometry);

CREATE TABLE qgis_test.ls2d(
id int,
geom Geometry(LineString,4326)
);

INSERT INTO qgis_test.ls2d values (1, 'srid=4326;Linestring(0 0, 1 1)'::geometry);

CREATE TABLE qgis_test.ls3d(
id int,
geom Geometry(LineStringZ,4326)
);

INSERT INTO qgis_test.ls3d values (1, 'srid=4326;Linestring(0 0 0, 1 1 1)'::geometry);

CREATE TABLE qgis_test.mpt2d(
id int,
geom Geometry(MultiPoint,4326)
);

INSERT INTO qgis_test.mpt2d values (1, 'srid=4326;MultiPoint((0 0),(1 1))'::geometry);

CREATE TABLE qgis_test.mpt3d(
id int,
geom Geometry(MultiPointZ,4326)
);

INSERT INTO qgis_test.mpt3d values (1, 'srid=4326;MultiPoint((0 0 0),(1 1 1))'::geometry);

CREATE TABLE qgis_test.mls2d(
id int,
geom Geometry(MultiLineString,4326)
);

INSERT INTO qgis_test.mls2d values (1, 'srid=4326;MultiLineString((0 0, 1 1),(2 2, 3 3))'::geometry);

CREATE TABLE qgis_test.mls3d(
id int,
geom Geometry(MultiLineStringZ,4326)
);

INSERT INTO qgis_test.mls3d values (1, 'srid=4326;MultiLineString((0 0 0, 1 1 1),(2 2 2, 3 3 3))'::geometry);

0 comments on commit a460e47

Please sign in to comment.