Skip to content

Commit 07dc429

Browse files
committed
[Geometry checker] Some initial tests
1 parent 54019e1 commit 07dc429

21 files changed

+379
-0
lines changed

tests/src/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,5 @@ IF (ENABLE_TESTS)
4545
IF (WITH_BINDINGS)
4646
ADD_SUBDIRECTORY(python)
4747
ENDIF (WITH_BINDINGS)
48+
ADD_SUBDIRECTORY(geometry_checker)
4849
ENDIF (ENABLE_TESTS)
+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Standard includes and utils to compile into all tests.
2+
3+
#####################################################
4+
# Don't forget to include output directory, otherwise
5+
# the UI file won't be wrapped!
6+
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}
7+
${CMAKE_CURRENT_BINARY_DIR}
8+
${CMAKE_SOURCE_DIR}/src/core
9+
${CMAKE_SOURCE_DIR}/src/core/expression
10+
${CMAKE_SOURCE_DIR}/src/core/geometry
11+
${CMAKE_SOURCE_DIR}/src/core/metadata
12+
${CMAKE_SOURCE_DIR}/src/analysis/
13+
${CMAKE_SOURCE_DIR}/src/analysis/vector/geometry_checker
14+
${CMAKE_SOURCE_DIR}/src/test
15+
${CMAKE_SOURCE_DIR}/src/plugins
16+
${CMAKE_BINARY_DIR}/src/core
17+
${CMAKE_BINARY_DIR}/src/analysis
18+
)
19+
INCLUDE_DIRECTORIES(SYSTEM
20+
${QT_INCLUDE_DIR}
21+
${GDAL_INCLUDE_DIR}
22+
${PROJ_INCLUDE_DIR}
23+
${GEOS_INCLUDE_DIR}
24+
${QCA_INCLUDE_DIR}
25+
${QTKEYCHAIN_INCLUDE_DIR}
26+
${POSTGRES_INCLUDE_DIR}
27+
)
28+
29+
#note for tests we should not include the moc of our
30+
#qtests in the executable file list as the moc is
31+
#directly included in the sources
32+
#and should not be compiled twice. Trying to include
33+
#them in will cause an error at build time
34+
35+
#No relinking and full RPATH for the install tree
36+
#See: http://www.cmake.org/Wiki/CMake_RPATH_handling#No_relinking_and_full_RPATH_for_the_install_tree
37+
38+
MACRO (ADD_QGIS_TEST testname testsrc)
39+
SET(qgis_${testname}_SRCS ${testsrc} ${util_SRCS})
40+
ADD_CUSTOM_TARGET(qgis_${testname}moc ALL DEPENDS ${qgis_${testname}_MOC_SRCS})
41+
ADD_EXECUTABLE(qgis_${testname} ${qgis_${testname}_SRCS})
42+
SET_TARGET_PROPERTIES(qgis_${testname} PROPERTIES AUTOMOC TRUE)
43+
TARGET_LINK_LIBRARIES(qgis_${testname}
44+
${QT_QTXML_LIBRARY}
45+
${QT_QTCORE_LIBRARY}
46+
${QT_QTSVG_LIBRARY}
47+
${QT_QTTEST_LIBRARY}
48+
${PROJ_LIBRARY}
49+
${GEOS_LIBRARY}
50+
${GDAL_LIBRARY}
51+
qgis_core
52+
qgis_analysis)
53+
ADD_TEST(qgis_${testname} ${CMAKE_CURRENT_BINARY_DIR}/../../../output/bin/qgis_${testname} -maxwarnings 10000)
54+
ENDMACRO (ADD_QGIS_TEST)
55+
56+
#############################################################
57+
# Tests:
58+
59+
ADD_QGIS_TEST( qgsgeometrycheckstest testqgsgeometrychecks.cpp )
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
/***************************************************************************
2+
testqgsgeometrychecks.cpp
3+
--------------------------------------
4+
Date : September 2017
5+
Copyright : (C) 2017 Sandro Mani
6+
Email : manisandro at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgstest.h"
17+
#include "qgscrscache.h"
18+
#include "qgsfeature.h"
19+
#include "qgsfeaturepool.h"
20+
#include "qgsvectorlayer.h"
21+
22+
#include "qgsgeometryanglecheck.h"
23+
#include "qgsgeometryareacheck.h"
24+
#include "qgsgeometrycontainedcheck.h"
25+
#include "qgsgeometrydanglecheck.h"
26+
#include "qgsgeometrydegeneratepolygoncheck.h"
27+
#include "qgsgeometryduplicatecheck.h"
28+
#include "qgsgeometryduplicatenodescheck.h"
29+
30+
#include "qgsgeometrygapcheck.h"
31+
#include "qgsgeometryholecheck.h"
32+
#include "qgsgeometrylineintersectioncheck.h"
33+
#include "qgsgeometrylinelayerintersectioncheck.h"
34+
#include "qgsgeometrymultipartcheck.h"
35+
#include "qgsgeometryoverlapcheck.h"
36+
#include "qgsgeometrypointcoveredbylinecheck.h"
37+
#include "qgsgeometrypointinpolygoncheck.h"
38+
#include "qgsgeometrysegmentlengthcheck.h"
39+
#include "qgsgeometryselfcontactcheck.h"
40+
#include "qgsgeometryselfintersectioncheck.h"
41+
#include "qgsgeometrysliverpolygoncheck.h"
42+
#include "qgsgeometrytypecheck.h"
43+
44+
45+
class TestQgsGeometryChecks: public QObject
46+
{
47+
Q_OBJECT
48+
private:
49+
double layerToMapUnits( const QgsMapLayer *layer, const QString &mapCrs ) const;
50+
QgsFeaturePool *createFeaturePool( QgsVectorLayer *layer, const QString &mapCrs, bool selectedOnly = false ) const;
51+
QgsGeometryCheckerContext *createTestContext( QMap<QString, QString> &layers, const QString &mapCrs = "EPSG:4326", double prec = 8 ) const;
52+
void cleanupTestContext( QgsGeometryCheckerContext *ctx ) const;
53+
void listErrors( const QList<QgsGeometryCheckError *> &checkErrors, const QStringList &messages ) const;
54+
int searchCheckError( const QList<QgsGeometryCheckError *> &checkErrors, const QString &layerId, const QgsFeatureId &featureId = -1, const QgsPointXY &pos = QgsPointXY(), const QgsVertexId &vid = QgsVertexId(), const QVariant &value = QVariant(), double tol = 1E-4 ) const;
55+
56+
QMap<QString, QString> mLayers;
57+
QgsGeometryCheckerContext *mContext;
58+
59+
private slots:
60+
// will be called before the first testfunction is executed.
61+
void initTestCase();
62+
// will be called after the last testfunction is executed.
63+
void cleanupTestCase();
64+
65+
void testAngleCheck();
66+
void testAreaCheck();
67+
void testContainedCheck();
68+
void testDangleCheck();
69+
void testDegeneratePolygonCheck();
70+
void testDuplicateCheck();
71+
void testDuplicateNodesCheck();
72+
};
73+
74+
void TestQgsGeometryChecks::initTestCase()
75+
{
76+
QgsApplication::initQgis();
77+
78+
mLayers.insert( "point_layer.shp", "" );
79+
mLayers.insert( "line_layer.shp", "" );
80+
mLayers.insert( "polygon_layer.shp", "" );
81+
mContext = createTestContext( mLayers );
82+
}
83+
84+
void TestQgsGeometryChecks::cleanupTestCase()
85+
{
86+
cleanupTestContext( mContext );
87+
QgsApplication::exitQgis();
88+
}
89+
90+
///////////////////////////////////////////////////////////////////////////////
91+
92+
void TestQgsGeometryChecks::testAngleCheck()
93+
{
94+
QList<QgsGeometryCheckError *> checkErrors;
95+
QStringList messages;
96+
97+
QgsGeometryAngleCheck( mContext, 15 ).collectErrors( checkErrors, messages );
98+
listErrors( checkErrors, messages );
99+
100+
QCOMPARE( checkErrors.size(), 7 );
101+
QVERIFY( searchCheckError( checkErrors, mLayers["point_layer.shp"] ) == 0 );
102+
QVERIFY( searchCheckError( checkErrors, mLayers["line_layer.shp"], 0, QgsPointXY( -0.2225, 0.5526 ), QgsVertexId( 0, 0, 3 ), 10.5865 ) == 1 );
103+
QVERIFY( searchCheckError( checkErrors, mLayers["line_layer.shp"], 0, QgsPointXY( -0.94996, 0.99967 ), QgsVertexId( 1, 0, 1 ), 8.3161 ) == 1 );
104+
QVERIFY( searchCheckError( checkErrors, mLayers["line_layer.shp"], 2, QgsPointXY( -0.4547, -0.3059 ), QgsVertexId( 0, 0, 1 ), 5.4165 ) == 1 );
105+
QVERIFY( searchCheckError( checkErrors, mLayers["line_layer.shp"], 2, QgsPointXY( -0.7594, -0.1971 ), QgsVertexId( 0, 0, 2 ), 12.5288 ) == 1 );
106+
QVERIFY( searchCheckError( checkErrors, mLayers["polygon_layer.shp"], 0, QgsPointXY( 0.2402, 1.0786 ), QgsVertexId( 0, 0, 1 ), 13.5140 ) == 1 );
107+
QVERIFY( searchCheckError( checkErrors, mLayers["polygon_layer.shp"], 1, QgsPointXY( 0.6960, 0.5908 ), QgsVertexId( 0, 0, 0 ), 7.0556 ) == 1 );
108+
QVERIFY( searchCheckError( checkErrors, mLayers["polygon_layer.shp"], 1, QgsPointXY( 0.98690, 0.55699 ), QgsVertexId( 1, 0, 5 ), 7.7351 ) == 1 );
109+
}
110+
111+
void TestQgsGeometryChecks::testAreaCheck()
112+
{
113+
QList<QgsGeometryCheckError *> checkErrors;
114+
QStringList messages;
115+
116+
QgsGeometryAreaCheck( mContext, 0.04 ).collectErrors( checkErrors, messages );
117+
listErrors( checkErrors, messages );
118+
119+
QCOMPARE( checkErrors.size(), 3 );
120+
QVERIFY( searchCheckError( checkErrors, mLayers["point_layer.shp"] ) == 0 );
121+
QVERIFY( searchCheckError( checkErrors, mLayers["line_layer.shp"] ) == 0 );
122+
QVERIFY( searchCheckError( checkErrors, mLayers["polygon_layer.shp"], 1, QgsPointXY( 1.0068, 0.3635 ), QgsVertexId(), 0.0105 ) == 1 );
123+
QVERIFY( searchCheckError( checkErrors, mLayers["polygon_layer.shp"], 2, QgsPointXY( 0.9739, 1.0983 ), QgsVertexId(), 0.0141 ) == 1 );
124+
QVERIFY( searchCheckError( checkErrors, mLayers["polygon_layer.shp"], 6, QgsPointXY( 0.9968, 1.7584 ), QgsVertexId(), 0 ) == 1 );
125+
}
126+
127+
void TestQgsGeometryChecks::testContainedCheck()
128+
{
129+
QList<QgsGeometryCheckError *> checkErrors;
130+
QStringList messages;
131+
132+
QgsGeometryContainedCheck( mContext ).collectErrors( checkErrors, messages );
133+
listErrors( checkErrors, messages );
134+
135+
QCOMPARE( checkErrors.size(), 3 );
136+
QVERIFY( searchCheckError( checkErrors, mLayers["point_layer.shp"], 5, QgsPointXY(), QgsVertexId(), QVariant( "polygon_layer.shp:0" ) ) == 1 );
137+
QVERIFY( searchCheckError( checkErrors, mLayers["line_layer.shp"], 3, QgsPointXY(), QgsVertexId(), QVariant( "polygon_layer.shp:0" ) ) == 1 );
138+
QVERIFY( searchCheckError( checkErrors, mLayers["polygon_layer.shp"], 3, QgsPointXY(), QgsVertexId(), QVariant( "polygon_layer.shp:0" ) ) == 1 );
139+
QVERIFY( messages.contains( "Contained check failed for (polygon_layer.shp:1): the geometry is invalid" ) );
140+
}
141+
142+
void TestQgsGeometryChecks::testDangleCheck()
143+
{
144+
QList<QgsGeometryCheckError *> checkErrors;
145+
QStringList messages;
146+
147+
QgsGeometryDangleCheck( mContext ).collectErrors( checkErrors, messages );
148+
listErrors( checkErrors, messages );
149+
150+
QCOMPARE( checkErrors.size(), 4 );
151+
QVERIFY( searchCheckError( checkErrors, mLayers["point_layer.shp"] ) == 0 );
152+
QVERIFY( searchCheckError( checkErrors, mLayers["polygon_layer.shp"] ) == 0 );
153+
QVERIFY( searchCheckError( checkErrors, mLayers["line_layer.shp"], 0, QgsPointXY( -0.7558, 0.7648 ), QgsVertexId( 1, 0, 0 ) ) == 1 );
154+
QVERIFY( searchCheckError( checkErrors, mLayers["line_layer.shp"], 2, QgsPointXY( -0.7787, -0.2237 ), QgsVertexId( 0, 0, 0 ) ) == 1 );
155+
QVERIFY( searchCheckError( checkErrors, mLayers["line_layer.shp"], 3, QgsPointXY( -0.2326, 0.9537 ), QgsVertexId( 0, 0, 0 ) ) == 1 );
156+
QVERIFY( searchCheckError( checkErrors, mLayers["line_layer.shp"], 3, QgsPointXY( 0.07913, 0.8012 ), QgsVertexId( 0, 0, 2 ) ) == 1 );
157+
}
158+
159+
void TestQgsGeometryChecks::testDegeneratePolygonCheck()
160+
{
161+
QList<QgsGeometryCheckError *> checkErrors;
162+
QStringList messages;
163+
164+
QgsGeometryDegeneratePolygonCheck( mContext ).collectErrors( checkErrors, messages );
165+
listErrors( checkErrors, messages );
166+
167+
QCOMPARE( checkErrors.size(), 1 );
168+
QVERIFY( searchCheckError( checkErrors, mLayers["point_layer.shp"] ) == 0 );
169+
QVERIFY( searchCheckError( checkErrors, mLayers["line_layer.shp"] ) == 0 );
170+
QVERIFY( searchCheckError( checkErrors, mLayers["polygon_layer.shp"], 6 ) == 1 );
171+
}
172+
173+
void TestQgsGeometryChecks::testDuplicateCheck()
174+
{
175+
QList<QgsGeometryCheckError *> checkErrors;
176+
QStringList messages;
177+
178+
QgsGeometryDuplicateCheck( mContext ).collectErrors( checkErrors, messages );
179+
listErrors( checkErrors, messages );
180+
181+
QCOMPARE( checkErrors.size(), 3 );
182+
QVERIFY(
183+
searchCheckError( checkErrors, mLayers["point_layer.shp"], 6, QgsPoint(), QgsVertexId(), QVariant( "point_layer.shp:2" ) ) == 1
184+
|| searchCheckError( checkErrors, mLayers["point_layer.shp"], 2, QgsPoint(), QgsVertexId(), QVariant( "point_layer.shp:6" ) ) == 1 );
185+
QVERIFY(
186+
searchCheckError( checkErrors, mLayers["line_layer.shp"], 4, QgsPoint(), QgsVertexId(), QVariant( "line_layer.shp:7" ) ) == 1
187+
|| searchCheckError( checkErrors, mLayers["line_layer.shp"], 7, QgsPoint(), QgsVertexId(), QVariant( "line_layer.shp:4" ) ) == 1 );
188+
QVERIFY(
189+
searchCheckError( checkErrors, mLayers["polygon_layer.shp"], 8, QgsPoint(), QgsVertexId(), QVariant( "polygon_layer.shp:7" ) ) == 1
190+
|| searchCheckError( checkErrors, mLayers["polygon_layer.shp"], 7, QgsPoint(), QgsVertexId(), QVariant( "polygon_layer.shp:8" ) ) == 1 );
191+
}
192+
193+
void TestQgsGeometryChecks::testDuplicateNodesCheck()
194+
{
195+
QList<QgsGeometryCheckError *> checkErrors;
196+
QStringList messages;
197+
198+
QgsGeometryDuplicateNodesCheck( mContext ).collectErrors( checkErrors, messages );
199+
listErrors( checkErrors, messages );
200+
201+
QCOMPARE( checkErrors.size(), 4 );
202+
QVERIFY( searchCheckError( checkErrors, mLayers["point_layer.shp"] ) == 0 );
203+
QVERIFY( searchCheckError( checkErrors, mLayers["line_layer.shp"], 0, QgsPointXY( -0.6360, 0.6203 ), QgsVertexId( 0, 0, 5 ) ) == 1 );
204+
QVERIFY( searchCheckError( checkErrors, mLayers["line_layer.shp"], 6, QgsPointXY( 0.2473, 2.0821 ), QgsVertexId( 0, 0, 1 ) ) == 1 );
205+
QVERIFY( searchCheckError( checkErrors, mLayers["line_layer.shp"], 6, QgsPointXY( 0.5158, 2.0930 ), QgsVertexId( 0, 0, 3 ) ) == 1 );
206+
QVERIFY( searchCheckError( checkErrors, mLayers["polygon_layer.shp"], 4, QgsPointXY( 1.6319, 0.5642 ), QgsVertexId( 0, 0, 1 ) ) == 1 );
207+
}
208+
209+
///////////////////////////////////////////////////////////////////////////////
210+
211+
double TestQgsGeometryChecks::layerToMapUnits( const QgsMapLayer *layer, const QString &mapCrs ) const
212+
{
213+
QgsCoordinateTransform crst = QgsCoordinateTransformCache::instance()->transform( layer->crs().authid(), mapCrs );
214+
QgsRectangle extent = layer->extent();
215+
QgsPointXY l1( extent.xMinimum(), extent.yMinimum() );
216+
QgsPointXY l2( extent.xMaximum(), extent.yMaximum() );
217+
double distLayerUnits = std::sqrt( l1.sqrDist( l2 ) );
218+
QgsPointXY m1 = crst.transform( l1 );
219+
QgsPointXY m2 = crst.transform( l2 );
220+
double distMapUnits = std::sqrt( m1.sqrDist( m2 ) );
221+
return distMapUnits / distLayerUnits;
222+
}
223+
224+
QgsFeaturePool *TestQgsGeometryChecks::createFeaturePool( QgsVectorLayer *layer, const QString &mapCrs, bool selectedOnly ) const
225+
{
226+
double layerToMapUntis = layerToMapUnits( layer, mapCrs );
227+
QgsCoordinateTransform layerToMapTransform = QgsCoordinateTransformCache::instance()->transform( layer->crs().authid(), mapCrs );
228+
return new QgsFeaturePool( layer, layerToMapUntis, layerToMapTransform, selectedOnly );
229+
}
230+
231+
QgsGeometryCheckerContext *TestQgsGeometryChecks::createTestContext( QMap<QString, QString> &layers, const QString &mapCrs, double prec ) const
232+
{
233+
QDir testDataDir( QDir( TEST_DATA_DIR ).absoluteFilePath( "geometry_checker" ) );
234+
235+
QMap<QString, QgsFeaturePool *> featurePools;
236+
for ( const QString &layerFile : layers.keys() )
237+
{
238+
QgsVectorLayer *layer = new QgsVectorLayer( testDataDir.absoluteFilePath( layerFile ), layerFile );
239+
Q_ASSERT( layer && layer->isValid() );
240+
layers[layerFile] = layer->id();
241+
featurePools.insert( layer->id(), createFeaturePool( layer, mapCrs ) );
242+
}
243+
return new QgsGeometryCheckerContext( prec, mapCrs, featurePools );
244+
}
245+
246+
void TestQgsGeometryChecks::cleanupTestContext( QgsGeometryCheckerContext *ctx ) const
247+
{
248+
for ( const QgsFeaturePool *pool : ctx->featurePools )
249+
{
250+
delete pool->getLayer();
251+
}
252+
qDeleteAll( ctx->featurePools );
253+
delete ctx;
254+
}
255+
256+
void TestQgsGeometryChecks::listErrors( const QList<QgsGeometryCheckError *> &checkErrors, const QStringList &messages ) const
257+
{
258+
QTextStream( stdout ) << " - Check result:" << endl;
259+
for ( const QgsGeometryCheckError *error : checkErrors )
260+
{
261+
QTextStream( stdout ) << " * " << error->layerId() << ":" << error->featureId() << " @[" << error->vidx().part << ", " << error->vidx().ring << ", " << error->vidx().vertex << "](" << error->location().x() << ", " << error->location().y() << ") = " << error->value().toString() << endl;
262+
}
263+
if ( !messages.isEmpty() )
264+
{
265+
QTextStream( stdout ) << " - Check messages:" << endl << " * " << messages.join( "\n * " ) << endl;
266+
}
267+
}
268+
269+
int TestQgsGeometryChecks::searchCheckError( const QList<QgsGeometryCheckError *> &checkErrors, const QString &layerId, const QgsFeatureId &featureId, const QgsPointXY &pos, const QgsVertexId &vid, const QVariant &value, double tol ) const
270+
{
271+
int matching = 0;
272+
for ( const QgsGeometryCheckError *error : checkErrors )
273+
{
274+
if ( error->layerId() != layerId )
275+
{
276+
continue;
277+
}
278+
if ( featureId != -1 && error->featureId() != featureId )
279+
{
280+
continue;
281+
}
282+
if ( pos != QgsPointXY() && ( !qgsDoubleNear( error->location().x(), pos.x(), tol ) || !qgsDoubleNear( error->location().y(), pos.y(), tol ) ) )
283+
{
284+
continue;
285+
}
286+
if ( vid.isValid() && vid != error->vidx() )
287+
{
288+
continue;
289+
}
290+
if ( !value.isNull() )
291+
{
292+
if ( value.type() == QVariant::Double )
293+
{
294+
if ( !qgsDoubleNear( value.toDouble(), error->value().toDouble(), tol ) )
295+
{
296+
continue;
297+
}
298+
}
299+
else if ( value != error->value() )
300+
{
301+
continue;
302+
}
303+
}
304+
++matching;
305+
}
306+
return matching;
307+
}
308+
309+
QGSTEST_MAIN( TestQgsGeometryChecks )
310+
#include "testqgsgeometrychecks.moc"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
UTF-8
153 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]
1.18 KB
Binary file not shown.
164 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
UTF-8
142 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]
296 Bytes
Binary file not shown.
156 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
UTF-8
164 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]

0 commit comments

Comments
 (0)