gdal/autotest/ogr/ogr_geojson.py

4365 строки
140 KiB
Python
Исполняемый файл

#!/usr/bin/env pytest
# -*- coding: utf-8 -*-
###############################################################################
# $Id$
#
# Project: GDAL/OGR Test Suite
# Purpose: GeoJSON driver test suite.
# Author: Mateusz Loskot <mateusz@loskot.net>
#
###############################################################################
# Copyright (c) 2007, Mateusz Loskot <mateusz@loskot.net>
# Copyright (c) 2009-2014, Even Rouault <even dot rouault at spatialys.com>
# Copyright (c) 2013, Kyle Shannon <kyle at pobox dot com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
###############################################################################
import json
import math
import os
import struct
import gdaltest
import ogrtest
import pytest
from osgeo import gdal, ogr, osr
pytestmark = pytest.mark.require_driver("GeoJSON")
###############################################################################
# Test utilities
def validate_layer(lyr, name, features, typ, fields, box):
if name is not None and name != lyr.GetName():
print("Wrong layer name")
return False
if features != lyr.GetFeatureCount():
print("Wrong number of features")
return False
lyrDefn = lyr.GetLayerDefn()
if lyrDefn is None:
print("Layer definition is none")
return False
if typ != lyrDefn.GetGeomType():
print("Wrong geometry type")
print(lyrDefn.GetGeomType())
return False
if fields != lyrDefn.GetFieldCount():
print("Wrong number of fields")
return False
extent = lyr.GetExtent()
minx = abs(extent[0] - box[0])
maxx = abs(extent[1] - box[1])
miny = abs(extent[2] - box[2])
maxy = abs(extent[3] - box[3])
if max(minx, maxx, miny, maxy) > 0.0001:
print("Wrong spatial extent of layer")
print(extent)
return False
return True
def verify_geojson_copy(fname, fids, names):
if gdaltest.gjpoint_feat is None:
print("Missing features collection")
return False
ds = ogr.Open(fname)
if ds is None:
print("Can not open '" + fname + "'")
return False
lyr = ds.GetLayer(0)
if lyr is None:
print("Missing layer")
return False
######################################################
# Test attributes
ret = ogrtest.check_features_against_list(lyr, "FID", fids)
if ret != 1:
print("Wrong values in 'FID' field")
return False
lyr.ResetReading()
ret = ogrtest.check_features_against_list(lyr, "NAME", names)
if ret != 1:
print("Wrong values in 'NAME' field")
return False
######################################################
# Test geometries
lyr.ResetReading()
for i in range(len(gdaltest.gjpoint_feat)):
orig_feat = gdaltest.gjpoint_feat[i]
feat = lyr.GetNextFeature()
if feat is None:
print("Failed trying to read feature")
return False
if (
ogrtest.check_feature_geometry(
feat, orig_feat.GetGeometryRef(), max_error=0.001
)
!= 0
):
print("Geometry test failed")
gdaltest.gjpoint_feat = None
return False
gdaltest.gjpoint_feat = None
lyr = None
return True
def copy_shape_to_geojson(gjname, compress=None):
if compress is not None:
if compress[0:5] == "/vsig":
dst_name = os.path.join("/vsigzip/", "tmp", gjname + ".geojson" + ".gz")
elif compress[0:4] == "/vsiz":
dst_name = os.path.join("/vsizip/", "tmp", gjname + ".geojson" + ".zip")
elif compress == "/vsistdout/":
dst_name = compress
else:
return False, None
else:
dst_name = os.path.join("tmp", gjname + ".geojson")
ds = gdal.GetDriverByName("GeoJSON").Create(dst_name, 0, 0, 0, gdal.GDT_Unknown)
if ds is None:
return False, dst_name
######################################################
# Create layer
lyr = ds.CreateLayer(gjname)
if lyr is None:
return False, dst_name
######################################################
# Setup schema (all test shapefiles use common schema)
ogrtest.quick_create_layer_def(lyr, [("FID", ogr.OFTReal), ("NAME", ogr.OFTString)])
######################################################
# Copy in gjpoint.shp
dst_feat = ogr.Feature(feature_def=lyr.GetLayerDefn())
src_name = os.path.join("data", "shp", gjname + ".shp")
shp_ds = ogr.Open(src_name)
shp_lyr = shp_ds.GetLayer(0)
feat = shp_lyr.GetNextFeature()
gdaltest.gjpoint_feat = []
while feat is not None:
gdaltest.gjpoint_feat.append(feat)
dst_feat.SetFrom(feat)
lyr.CreateFeature(dst_feat)
feat = shp_lyr.GetNextFeature()
shp_lyr = None
lyr = None
assert ds.FlushCache() == gdal.CE_None
ds = None
return True, dst_name
###############################################################################
# Test file-based DS with standalone "Point" feature object.
def test_ogr_geojson_2():
ds = ogr.Open("data/geojson/point.geojson")
assert ds is not None, "Failed to open datasource"
assert ds.GetLayerCount() == 1, "Wrong number of layers"
lyr = ds.GetLayerByName("point")
assert lyr is not None, "Missing layer called point"
extent = (100.0, 100.0, 0.0, 0.0)
rc = validate_layer(lyr, "point", 1, ogr.wkbPoint, 0, extent)
assert rc
lyr = None
###############################################################################
# Test file-based DS with standalone "LineString" feature object.
def test_ogr_geojson_3():
ds = ogr.Open("data/geojson/linestring.geojson")
assert ds is not None, "Failed to open datasource"
assert ds.GetLayerCount() == 1, "Wrong number of layers"
lyr = ds.GetLayerByName("linestring")
assert lyr is not None, "Missing layer called linestring"
extent = (100.0, 101.0, 0.0, 1.0)
rc = validate_layer(lyr, "linestring", 1, ogr.wkbLineString, 0, extent)
assert rc
lyr = None
##############################################################################
# Test file-based DS with standalone "Polygon" feature object.
def test_ogr_geojson_4():
ds = ogr.Open("data/geojson/polygon.geojson")
assert ds is not None, "Failed to open datasource"
assert ds.GetLayerCount() == 1, "Wrong number of layers"
lyr = ds.GetLayerByName("polygon")
assert lyr is not None, "Missing layer called polygon"
extent = (100.0, 101.0, 0.0, 1.0)
rc = validate_layer(lyr, "polygon", 1, ogr.wkbPolygon, 0, extent)
assert rc
lyr = None
##############################################################################
# Test file-based DS with standalone "GeometryCollection" feature object.
def test_ogr_geojson_5():
ds = ogr.Open("data/geojson/geometrycollection.geojson")
assert ds is not None, "Failed to open datasource"
assert ds.GetLayerCount() == 1, "Wrong number of layers"
lyr = ds.GetLayerByName("geometrycollection")
assert lyr is not None, "Missing layer called geometrycollection"
extent = (100.0, 102.0, 0.0, 1.0)
rc = validate_layer(
lyr, "geometrycollection", 1, ogr.wkbGeometryCollection, 0, extent
)
assert rc
lyr = None
##############################################################################
# Test file-based DS with standalone "MultiPoint" feature object.
def test_ogr_geojson_6():
ds = ogr.Open("data/geojson/multipoint.geojson")
assert ds is not None, "Failed to open datasource"
assert ds.GetLayerCount() == 1, "Wrong number of layers"
lyr = ds.GetLayerByName("multipoint")
assert lyr is not None, "Missing layer called multipoint"
extent = (100.0, 101.0, 0.0, 1.0)
rc = validate_layer(lyr, "multipoint", 1, ogr.wkbMultiPoint, 0, extent)
assert rc
lyr = None
##############################################################################
# Test file-based DS with standalone "MultiLineString" feature object.
def test_ogr_geojson_7():
ds = ogr.Open("data/geojson/multilinestring.geojson")
assert ds is not None, "Failed to open datasource"
assert ds.GetLayerCount() == 1, "Wrong number of layers"
lyr = ds.GetLayerByName("multilinestring")
assert lyr is not None, "Missing layer called multilinestring"
extent = (100.0, 103.0, 0.0, 3.0)
rc = validate_layer(lyr, "multilinestring", 1, ogr.wkbMultiLineString, 0, extent)
assert rc
lyr = None
##############################################################################
# Test file-based DS with standalone "MultiPolygon" feature object.
def test_ogr_geojson_8():
ds = ogr.Open("data/geojson/multipolygon.geojson")
assert ds is not None, "Failed to open datasource"
assert ds.GetLayerCount() == 1, "Wrong number of layers"
lyr = ds.GetLayerByName("multipolygon")
assert lyr is not None, "Missing layer called multipolygon"
extent = (100.0, 103.0, 0.0, 3.0)
rc = validate_layer(lyr, "multipolygon", 1, ogr.wkbMultiPolygon, 0, extent)
assert rc
lyr = None
##############################################################################
# Test translation of data/gjpoint.shp to GeoJSON file
def test_ogr_geojson_9():
tests = [
["gjpoint", [1], ["Point 1"]],
["gjline", [1], ["Line 1"]],
["gjpoly", [1], ["Polygon 1"]],
["gjmultipoint", [1], ["MultiPoint 1"]],
["gjmultiline", [2], ["MultiLine 1"]],
["gjmultipoly", [2], ["MultiPoly 1"]],
]
for test in tests:
rc, dstname = copy_shape_to_geojson(test[0])
try:
assert rc, "Failed making copy of " + test[0] + ".shp"
rc = verify_geojson_copy(dstname, test[1], test[2])
assert rc, "Verification of copy of " + test[0] + ".shp failed"
finally:
if dstname:
gdal.Unlink(dstname)
##############################################################################
# Test translation of data/gjpoint.shp to GZip compressed GeoJSON file
def test_ogr_geojson_10():
tests = [
["gjpoint", [1], ["Point 1"]],
["gjline", [1], ["Line 1"]],
["gjpoly", [1], ["Polygon 1"]],
["gjmultipoint", [1], ["MultiPoint 1"]],
["gjmultiline", [2], ["MultiLine 1"]],
["gjmultipoly", [2], ["MultiPoly 1"]],
]
for test in tests:
rc, dstname = copy_shape_to_geojson(test[0], "/vsigzip/")
try:
assert rc, "Failed making copy of " + test[0] + ".shp"
rc = verify_geojson_copy(dstname, test[1], test[2])
assert rc, "Verification of copy of " + test[0] + ".shp failed"
finally:
if dstname:
dstname = dstname[len("/vsigzip/") :]
gdal.Unlink(dstname)
if gdal.VSIStatL(dstname + ".properties") is not None:
gdal.Unlink(dstname + ".properties")
###############################################################################
def test_ogr_geojson_11():
ds = ogr.Open("data/geojson/srs_name.geojson")
assert ds is not None, "Failed to open datasource"
assert ds.GetLayerCount() == 1, "Wrong number of layers"
lyr = ds.GetLayerByName("srs_name")
assert lyr is not None, "Missing layer called srs_name"
extent = (100.0, 102.0, 0.0, 1.0)
rc = validate_layer(lyr, "srs_name", 1, ogr.wkbGeometryCollection, 0, extent)
assert rc
ref = lyr.GetSpatialRef()
pcs = int(ref.GetAuthorityCode("PROJCS"))
assert pcs == 26915, "Spatial reference was not valid"
feature = lyr.GetNextFeature()
geometry = feature.GetGeometryRef().GetGeometryRef(0)
srs = geometry.GetSpatialReference()
pcs = int(srs.GetAuthorityCode("PROJCS"))
assert pcs == 26916, "Spatial reference for individual geometry was not valid"
lyr = None
###############################################################################
# Test DS passed as name with standalone "Point" feature object (#3377)
def test_ogr_geojson_12():
if os.name == "nt":
pytest.skip()
import test_cli_utilities
if test_cli_utilities.get_ogrinfo_path() is None:
pytest.skip()
ret = gdaltest.runexternal(
test_cli_utilities.get_ogrinfo_path()
+ ' -ro -al \'{"type": "Point","coordinates": [100.0, 0.0]}\''
)
assert ret.find(" POINT (100 0)") != -1
###############################################################################
# Test writing to stdout (#3381)
def test_ogr_geojson_13():
test = ["gjpoint", [1], ["Point 1"]]
rc, _ = copy_shape_to_geojson(test[0], "/vsistdout/")
assert rc, "Failed making copy of " + test[0] + ".shp"
###############################################################################
# Test reading & writing various degenerated geometries
@gdaltest.disable_exceptions()
def test_ogr_geojson_14():
with gdaltest.error_handler():
ds = ogr.Open("data/geojson/ogr_geojson_14.geojson")
lyr = ds.GetLayer(0)
try:
out_ds = ogr.GetDriverByName("GeoJSON").CreateDataSource(
"tmp/out_ogr_geojson_14.geojson"
)
out_lyr = out_ds.CreateLayer("lyr")
with gdaltest.error_handler():
for feat in lyr:
geom = feat.GetGeometryRef()
if geom is not None:
# print(geom)
out_feat = ogr.Feature(feature_def=out_lyr.GetLayerDefn())
out_feat.SetGeometry(geom)
out_lyr.CreateFeature(out_feat)
out_ds = None
finally:
try:
os.remove("tmp/out_ogr_geojson_14.geojson")
except OSError:
pass
###############################################################################
# Test Feature.ExportToJson (#3870)
def test_ogr_geojson_15():
feature_defn = ogr.FeatureDefn()
feature_defn.AddFieldDefn(ogr.FieldDefn("foo"))
field_defn = ogr.FieldDefn("boolfield", ogr.OFTInteger)
field_defn.SetSubType(ogr.OFSTBoolean)
feature_defn.AddFieldDefn(field_defn)
feature = ogr.Feature(feature_defn)
feature.SetField("foo", "bar")
feature.SetField("boolfield", True)
feature.SetFID(0)
geom = ogr.CreateGeometryFromWkt("POINT(1 2)")
feature.SetGeometry(geom)
try:
out = feature.ExportToJson()
except ImportError:
pytest.skip()
expected_out = """{"geometry": {"type": "Point", "coordinates": [1.0, 2.0]}, "type": "Feature", "properties": {"foo": "bar", "boolfield": true}, "id": 0}"""
if out != expected_out:
out_json = json.loads(out)
expected_out_json = json.loads(expected_out)
assert out_json == expected_out_json, out
out = feature.ExportToJson(as_object=True)
expected_out = {
"geometry": {"type": "Point", "coordinates": [1.0, 2.0]},
"type": "Feature",
"properties": {"foo": "bar", "boolfield": True},
"id": 0,
}
assert out == expected_out
###############################################################################
# Test reading files with no extension (#4314)
def test_ogr_geojson_20():
from glob import glob
geojson_files = glob("data/*.json")
geojson_files.extend(glob("data/*.geojson"))
for gj in geojson_files:
# create tmp file with no file extension
data = open(gj, "rb").read()
f = gdal.VSIFOpenL("/vsimem/testgj", "wb")
gdal.VSIFWriteL(data, 1, len(data), f)
gdal.VSIFCloseL(f)
with gdaltest.error_handler():
ds = ogr.Open("/vsimem/testgj")
if ds is None:
print(gj)
print(data.decode("LATIN1"))
pytest.fail("Failed to open datasource")
ds = None
gdal.Unlink("/vsimem/testgj")
###############################################################################
# Test reading output of geocouch spatiallist
def test_ogr_geojson_21():
ds = ogr.Open(
"""{"type": "FeatureCollection", "features":[
{"type": "Feature",
"geometry": {"type":"Point","coordinates":[1,2]},
"properties": {"_id":"aid", "_rev":"arev", "type":"Feature",
"properties":{"intvalue" : 2, "floatvalue" : 3.2, "strvalue" : "foo", "properties": { "foo": "bar"}}}}]}"""
)
assert ds is not None, "Failed to open datasource"
lyr = ds.GetLayerByName("OGRGeoJSON")
feature = lyr.GetNextFeature()
ref_geom = ogr.CreateGeometryFromWkt("POINT (1 2)")
if (
feature.GetFieldAsString("_id") != "aid"
or feature.GetFieldAsString("_rev") != "arev"
or feature.GetFieldAsInteger("intvalue") != 2
or ogrtest.check_feature_geometry(feature, ref_geom) != 0
):
feature.DumpReadable()
pytest.fail()
lyr = None
ds = None
###############################################################################
# Same as ogr_geojson_21 with several features
def test_ogr_geojson_22():
ds = ogr.Open(
"""{"type": "FeatureCollection", "features":[
{"type": "Feature",
"geometry": {"type":"Point","coordinates":[1,2]},
"properties": {"_id":"aid", "_rev":"arev", "type":"Feature",
"properties":{"intvalue" : 2, "floatvalue" : 3.2, "strvalue" : "foo"}}},
{"type": "Feature",
"geometry": {"type":"Point","coordinates":[3,4]},
"properties": {"_id":"aid2", "_rev":"arev2", "type":"Feature",
"properties":{"intvalue" : 3.5, "str2value" : "bar"}}}]}"""
)
assert ds is not None, "Failed to open datasource"
lyr = ds.GetLayerByName("OGRGeoJSON")
feature = lyr.GetNextFeature()
ref_geom = ogr.CreateGeometryFromWkt("POINT (1 2)")
if (
feature.GetFieldAsString("_id") != "aid"
or feature.GetFieldAsString("_rev") != "arev"
or feature.GetFieldAsDouble("intvalue") != 2
or ogrtest.check_feature_geometry(feature, ref_geom) != 0
):
feature.DumpReadable()
pytest.fail()
feature = lyr.GetNextFeature()
ref_geom = ogr.CreateGeometryFromWkt("POINT (3 4)")
if (
feature.GetFieldAsString("_id") != "aid2"
or feature.GetFieldAsString("_rev") != "arev2"
or feature.GetFieldAsDouble("intvalue") != 3.5
or feature.GetFieldAsString("str2value") != "bar"
or ogrtest.check_feature_geometry(feature, ref_geom) != 0
):
feature.DumpReadable()
pytest.fail()
lyr = None
ds = None
###############################################################################
# Write GeoJSON with bbox and test SRS writing&reading back
def test_ogr_geojson_23():
ds = ogr.GetDriverByName("GeoJSON").CreateDataSource("/vsimem/ogr_geojson_23.json")
sr = osr.SpatialReference()
sr.ImportFromEPSG(4322)
lyr = ds.CreateLayer("foo", srs=sr, options=["WRITE_BBOX=YES"])
feat = ogr.Feature(lyr.GetLayerDefn())
feat.SetGeometry(ogr.CreateGeometryFromWkt("POINT(1 10)"))
lyr.CreateFeature(feat)
feat = ogr.Feature(lyr.GetLayerDefn())
feat.SetGeometry(ogr.CreateGeometryFromWkt("POINT(2 20)"))
lyr.CreateFeature(feat)
assert lyr.GetExtent() == (1.0, 2.0, 10.0, 20.0)
assert lyr.GetExtent(geom_field=0) == (1.0, 2.0, 10.0, 20.0)
assert lyr.GetExtent(geom_field=1, can_return_null=True) is None
lyr = None
ds = None
ds = ogr.Open("/vsimem/ogr_geojson_23.json")
lyr = ds.GetLayer(0)
sr_got = lyr.GetSpatialRef()
ds = None
sr.SetAxisMappingStrategy(osr.OAMS_TRADITIONAL_GIS_ORDER)
assert sr_got.IsSame(sr), "did not get expected SRS"
fp = gdal.VSIFOpenL("/vsimem/ogr_geojson_23.json", "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
gdal.Unlink("/vsimem/ogr_geojson_23.json")
assert data.find('"bbox": [ 1, 10, 2, 20 ]') != -1, "did not find global bbox"
assert (
data.find('"bbox": [ 1.0, 10.0, 1.0, 10.0 ]') != -1
), "did not find first feature bbox"
###############################################################################
# Test alternate form of geojson
def test_ogr_geojson_24():
content = """loadGeoJSON({"layerFoo": { "type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [2, 49]
},
"name": "bar"
},
"layerBar": { "type": "FeatureCollection", "features" : [ { "type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [2, 49]
},
"other_name": "baz"
}]}})"""
for i in range(2):
if i == 0:
ds = ogr.Open(content)
else:
gdal.FileFromMemBuffer("/vsimem/ogr_geojson_24.js", content)
ds = ogr.Open("/vsimem/ogr_geojson_24.js")
gdal.Unlink("/vsimem/ogr_geojson_24.js")
assert ds is not None, "Failed to open datasource"
lyr = ds.GetLayerByName("layerFoo")
assert lyr is not None, "cannot find layer"
feature = lyr.GetNextFeature()
ref_geom = ogr.CreateGeometryFromWkt("POINT (2 49)")
if (
feature.GetFieldAsString("name") != "bar"
or ogrtest.check_feature_geometry(feature, ref_geom) != 0
):
feature.DumpReadable()
pytest.fail()
lyr = ds.GetLayerByName("layerBar")
assert lyr is not None, "cannot find layer"
feature = lyr.GetNextFeature()
ref_geom = ogr.CreateGeometryFromWkt("POINT (2 49)")
if (
feature.GetFieldAsString("other_name") != "baz"
or ogrtest.check_feature_geometry(feature, ref_geom) != 0
):
feature.DumpReadable()
pytest.fail()
ds = None
###############################################################################
# Test 64bit support
def test_ogr_geojson_26():
ds = ogr.Open(
"""{"type": "FeatureCollection", "features":[
{"type": "Feature", "id": 1,
"geometry": {"type":"Point","coordinates":[1,2]},
"properties": { "intvalue" : 1, "int64" : 1234567890123, "intlist" : [1] }},
{"type": "Feature", "id": 1234567890123,
"geometry": {"type":"Point","coordinates":[3,4]},
"properties": { "intvalue" : 1234567890123, "intlist" : [1, 1234567890123] }},
]}"""
)
assert ds is not None, "Failed to open datasource"
lyr = ds.GetLayerByName("OGRGeoJSON")
assert lyr.GetMetadataItem(ogr.OLMD_FID64) is not None
feature = lyr.GetNextFeature()
if feature.GetFID() != 1:
feature.DumpReadable()
pytest.fail()
if feature.GetField("intvalue") != 1:
feature.DumpReadable()
pytest.fail()
if feature.GetField("int64") != 1234567890123:
feature.DumpReadable()
pytest.fail()
feature = lyr.GetNextFeature()
if feature.GetFID() != 1234567890123:
feature.DumpReadable()
pytest.fail()
if feature.GetField("intvalue") != 1234567890123:
feature.DumpReadable()
pytest.fail()
if feature.GetField("intlist") != [1, 1234567890123]:
feature.DumpReadable()
pytest.fail()
lyr = None
ds = None
ds = ogr.GetDriverByName("GeoJSON").CreateDataSource("/vsimem/ogr_geojson_26.json")
lyr = ds.CreateLayer("test")
lyr.CreateField(ogr.FieldDefn("int64", ogr.OFTInteger64))
lyr.CreateField(ogr.FieldDefn("int64list", ogr.OFTInteger64List))
f = ogr.Feature(lyr.GetLayerDefn())
f.SetFID(1234567890123)
f.SetField(0, 1234567890123)
f.SetFieldInteger64List(1, [1234567890123])
lyr.CreateFeature(f)
f = None
ds = None
fp = gdal.VSIFOpenL("/vsimem/ogr_geojson_26.json", "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
gdal.Unlink("/vsimem/ogr_geojson_26.json")
assert (
'{ "type": "Feature", "id": 1234567890123, "properties": { "int64": 1234567890123, "int64list": [ 1234567890123 ] }, "geometry": null }'
in data
)
###############################################################################
# Test workaround for 64bit values (returned as strings)
def test_ogr_geojson_27():
with gdaltest.error_handler():
# Warning 1: Integer values probably ranging out of 64bit integer range
# have been found. Will be clamped to INT64_MIN/INT64_MAX
ds = ogr.Open(
"""{"type": "FeatureCollection", "features":[
{"type": "Feature",
"geometry": {"type":"Point","coordinates":[1,2]},
"properties": { "intvalue" : 1 }},
{"type": "Feature",
"geometry": {"type":"Point","coordinates":[3,4]},
"properties": { "intvalue" : 12345678901231234567890123 }},
]}"""
)
assert ds is not None, "Failed to open datasource"
lyr = ds.GetLayerByName("OGRGeoJSON")
feature = lyr.GetNextFeature()
if feature.GetField("intvalue") != 1:
feature.DumpReadable()
pytest.fail()
feature = lyr.GetNextFeature()
if feature.GetField("intvalue") != 9223372036854775807:
feature.DumpReadable()
pytest.fail()
lyr = None
ds = None
###############################################################################
# Test handling of huge coordinates (#5377)
def test_ogr_geojson_35():
ds = ogr.GetDriverByName("GeoJSON").CreateDataSource("/vsimem/ogr_geojson_35.json")
lyr = ds.CreateLayer("foo")
feat = ogr.Feature(lyr.GetLayerDefn())
feat.SetFID(1)
geom = ogr.Geometry(ogr.wkbPoint)
geom.AddPoint_2D(-1.79769313486231571e308, -1.79769313486231571e308)
feat.SetGeometry(geom)
lyr.CreateFeature(feat)
with gdaltest.error_handler():
feat = ogr.Feature(lyr.GetLayerDefn())
feat.SetFID(2)
geom = ogr.Geometry(ogr.wkbPoint)
geom.AddPoint(-1.7e308 * 2, 1.7e308 * 2, 1.7e308 * 2) # evaluates to -inf, inf
feat.SetGeometry(geom)
lyr.CreateFeature(feat)
feat = ogr.Feature(lyr.GetLayerDefn())
feat.SetFID(3)
geom = ogr.Geometry(ogr.wkbLineString)
geom.AddPoint_2D(0, 0)
geom.AddPoint_2D(-1.7e308 * 2, 1.7e308 * 2) # evaluates to -inf, inf
feat.SetGeometry(geom)
lyr.CreateFeature(feat)
feat = ogr.Feature(lyr.GetLayerDefn())
feat.SetFID(4)
geom = ogr.Geometry(ogr.wkbPolygon)
geom2 = ogr.Geometry(ogr.wkbLinearRing)
geom2.AddPoint_2D(0, 0)
geom2.AddPoint_2D(-1.7e308 * 2, 1.7e308 * 2) # evaluates to -inf, inf
geom.AddGeometry(geom2)
feat.SetGeometry(geom)
lyr.CreateFeature(feat)
feat = ogr.Feature(lyr.GetLayerDefn())
feat.SetFID(5)
geom = ogr.Geometry(ogr.wkbMultiPoint)
geom2 = ogr.Geometry(ogr.wkbPoint)
geom2.AddPoint_2D(0, 0)
geom2 = ogr.Geometry(ogr.wkbPoint)
geom2.AddPoint_2D(-1.7e308 * 2, 1.7e308 * 2) # evaluates to -inf, inf
geom.AddGeometry(geom2)
feat.SetGeometry(geom)
lyr.CreateFeature(feat)
feat = ogr.Feature(lyr.GetLayerDefn())
feat.SetFID(6)
geom = ogr.Geometry(ogr.wkbMultiLineString)
geom2 = ogr.Geometry(ogr.wkbLineString)
geom2.AddPoint_2D(0, 0)
geom2 = ogr.Geometry(ogr.wkbLineString)
geom2.AddPoint_2D(-1.7e308 * 2, 1.7e308 * 2) # evaluates to -inf, inf
geom.AddGeometry(geom2)
feat.SetGeometry(geom)
lyr.CreateFeature(feat)
feat = ogr.Feature(lyr.GetLayerDefn())
feat.SetFID(7)
geom = ogr.Geometry(ogr.wkbMultiPolygon)
geom2 = ogr.Geometry(ogr.wkbPolygon)
geom3 = ogr.Geometry(ogr.wkbLinearRing)
geom3.AddPoint_2D(0, 0)
geom2.AddGeometry(geom3)
geom2 = ogr.Geometry(ogr.wkbPolygon)
geom3 = ogr.Geometry(ogr.wkbLinearRing)
geom3.AddPoint_2D(-1.7e308 * 2, 1.7e308 * 2) # evaluates to -inf, inf
geom2.AddGeometry(geom3)
geom.AddGeometry(geom2)
feat.SetGeometry(geom)
lyr.CreateFeature(feat)
ds = None
fp = gdal.VSIFOpenL("/vsimem/ogr_geojson_35.json", "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
gdal.Unlink("/vsimem/ogr_geojson_35.json")
assert "-1.79" in data and "e+308" in data
for ident in range(2, 8):
assert (
data.find(
'{ "type": "Feature", "id": %d, "properties": { }, "geometry": null }'
% ident
)
!= -1
)
###############################################################################
# Test reading file with UTF-8 BOM (which is supposed to be illegal in JSON...) (#5630)
def test_ogr_geojson_36():
ds = ogr.Open("data/geojson/point_with_utf8bom.json")
assert ds is not None, "Failed to open datasource"
ds = None
#########################################################################
# Test boolean type support
def test_ogr_geojson_37():
# Test read support
ds = ogr.Open(
"""{"type": "FeatureCollection","features": [
{ "type": "Feature", "properties": { "bool" : false, "not_bool": false, "bool_list" : [false, true], "notbool_list" : [false, 3]}, "geometry": null },
{ "type": "Feature", "properties": { "bool" : true, "not_bool": 2, "bool_list" : [true] }, "geometry": null },
] }"""
)
lyr = ds.GetLayer(0)
feat_defn = lyr.GetLayerDefn()
assert (
feat_defn.GetFieldDefn(feat_defn.GetFieldIndex("bool")).GetType()
== ogr.OFTInteger
and feat_defn.GetFieldDefn(feat_defn.GetFieldIndex("bool")).GetSubType()
== ogr.OFSTBoolean
)
assert (
feat_defn.GetFieldDefn(feat_defn.GetFieldIndex("not_bool")).GetSubType()
== ogr.OFSTNone
)
assert (
feat_defn.GetFieldDefn(feat_defn.GetFieldIndex("bool_list")).GetType()
== ogr.OFTIntegerList
and feat_defn.GetFieldDefn(feat_defn.GetFieldIndex("bool_list")).GetSubType()
== ogr.OFSTBoolean
)
assert (
feat_defn.GetFieldDefn(feat_defn.GetFieldIndex("notbool_list")).GetType()
== ogr.OFTString
and feat_defn.GetFieldDefn(feat_defn.GetFieldIndex("notbool_list")).GetSubType()
== ogr.OFSTJSON
)
f = lyr.GetNextFeature()
if f.GetField("bool") != 0 or f.GetField("bool_list") != [0, 1]:
f.DumpReadable()
pytest.fail()
out_ds = ogr.GetDriverByName("GeoJSON").CreateDataSource(
"/vsimem/ogr_geojson_37.json"
)
out_lyr = out_ds.CreateLayer("test")
for i in range(feat_defn.GetFieldCount()):
out_lyr.CreateField(feat_defn.GetFieldDefn(i))
out_f = ogr.Feature(out_lyr.GetLayerDefn())
out_f.SetFrom(f)
out_lyr.CreateFeature(out_f)
out_ds = None
fp = gdal.VSIFOpenL("/vsimem/ogr_geojson_37.json", "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
gdal.Unlink("/vsimem/ogr_geojson_37.json")
assert (
'"bool": false, "not_bool": 0, "bool_list": [ false, true ], "notbool_list": [ false, 3 ]'
in data
)
###############################################################################
# Test datetime/date/time type support
def test_ogr_geojson_38():
# Test read support
ds = gdal.OpenEx(
"""{"type": "FeatureCollection", "features": [
{ "type": "Feature", "properties": { "dt": "2014-11-20 12:34:56+0100", "dt2": "2014\\/11\\/20", "date":"2014\\/11\\/20", "time":"12:34:56", "no_dt": "2014-11-20 12:34:56+0100", "no_dt2": "2014-11-20 12:34:56+0100", "no_date": "2022/05/12 blah" }, "geometry": null },
{ "type": "Feature", "properties": { "dt": "2014\\/11\\/20", "dt2": "2014\\/11\\/20T12:34:56Z", "date":"2014-11-20", "time":"12:34:56", "no_dt": "foo", "no_dt2": 1 }, "geometry": null }
] }"""
)
lyr = ds.GetLayer(0)
feat_defn = lyr.GetLayerDefn()
assert (
feat_defn.GetFieldDefn(feat_defn.GetFieldIndex("dt")).GetType()
== ogr.OFTDateTime
)
assert (
feat_defn.GetFieldDefn(feat_defn.GetFieldIndex("dt2")).GetType()
== ogr.OFTDateTime
)
assert (
feat_defn.GetFieldDefn(feat_defn.GetFieldIndex("date")).GetType() == ogr.OFTDate
)
assert (
feat_defn.GetFieldDefn(feat_defn.GetFieldIndex("time")).GetType() == ogr.OFTTime
)
assert (
feat_defn.GetFieldDefn(feat_defn.GetFieldIndex("no_dt")).GetType()
== ogr.OFTString
)
assert (
feat_defn.GetFieldDefn(feat_defn.GetFieldIndex("no_dt2")).GetType()
== ogr.OFTString
)
assert (
feat_defn.GetFieldDefn(feat_defn.GetFieldIndex("no_date")).GetType()
== ogr.OFTString
)
f = lyr.GetNextFeature()
if (
f.GetField("dt") != "2014/11/20 12:34:56+01"
or f.GetField("dt2") != "2014/11/20 00:00:00"
or f.GetField("date") != "2014/11/20"
or f.GetField("time") != "12:34:56"
):
f.DumpReadable()
pytest.fail()
f = lyr.GetNextFeature()
if (
f.GetField("dt") != "2014/11/20 00:00:00"
or f.GetField("dt2") != "2014/11/20 12:34:56+00"
or f.GetField("date") != "2014/11/20"
or f.GetField("time") != "12:34:56"
):
f.DumpReadable()
pytest.fail()
tmpfilename = "/vsimem/out.json"
gdal.VectorTranslate(
tmpfilename, ds, options="-lco NATIVE_DATA=dummy"
) # dummy NATIVE_DATA so that input values are not copied directly
fp = gdal.VSIFOpenL(tmpfilename, "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
gdal.Unlink(tmpfilename)
assert (
'"dt": "2014-11-20T12:34:56+01:00", "dt2": "2014-11-20T00:00:00", "date": "2014-11-20", "time": "12:34:56"'
in data
), data
ds = gdal.OpenEx(
"""{"type": "FeatureCollection", "features": [
{ "type": "Feature", "properties": { "dt": "2014-11-20 12:34:56+0100", "dt2": "2014\\/11\\/20", "date":"2014\\/11\\/20", "time":"12:34:56", "no_dt": "2014-11-20 12:34:56+0100", "no_dt2": "2014-11-20 12:34:56+0100" }, "geometry": null }
] }""",
open_options=["DATE_AS_STRING=YES"],
)
lyr = ds.GetLayer(0)
feat_defn = lyr.GetLayerDefn()
for i in range(feat_defn.GetFieldCount()):
assert feat_defn.GetFieldDefn(i).GetType() == ogr.OFTString
###############################################################################
# Test id top-object level
@gdaltest.disable_exceptions()
def test_ogr_geojson_39():
ds = ogr.Open(
"""{"type": "FeatureCollection", "features": [
{ "type": "Feature", "id" : "foo", "properties": { "bar" : "baz" }, "geometry": null },
] }"""
)
lyr = ds.GetLayer(0)
feat_defn = lyr.GetLayerDefn()
assert (
feat_defn.GetFieldDefn(0).GetName() == "id"
and feat_defn.GetFieldDefn(0).GetType() == ogr.OFTString
)
feat = lyr.GetNextFeature()
if feat.GetField("id") != "foo" or feat.GetField("bar") != "baz":
feat.DumpReadable()
pytest.fail()
# Crazy case: properties.id has the precedence because we arbitrarily decided that...
ds = ogr.Open(
"""{"type": "FeatureCollection", "features": [
{ "type": "Feature", "id" : "foo", "properties": { "id" : 6 }, "geometry": null },
] }"""
)
lyr = ds.GetLayer(0)
feat_defn = lyr.GetLayerDefn()
assert (
feat_defn.GetFieldDefn(0).GetName() == "id"
and feat_defn.GetFieldDefn(0).GetType() == ogr.OFTInteger
)
feat = lyr.GetNextFeature()
if feat.GetField("id") != 6:
feat.DumpReadable()
pytest.fail()
# Same with 2 features
ds = ogr.Open(
"""{"type": "FeatureCollection", "features": [
{ "type": "Feature", "id" : "foo", "properties": { "id" : 6 }, "geometry": null },
{ "type": "Feature", "id" : "bar", "properties": { "id" : 7 }, "geometry": null }
] }"""
)
lyr = ds.GetLayer(0)
feat_defn = lyr.GetLayerDefn()
assert (
feat_defn.GetFieldDefn(0).GetName() == "id"
and feat_defn.GetFieldDefn(0).GetType() == ogr.OFTInteger
)
feat = lyr.GetNextFeature()
if feat.GetField("id") != 6:
feat.DumpReadable()
pytest.fail()
# Crazy case: properties.id has the precedence because we arbitrarily decided that...
ds = ogr.Open(
"""{"type": "FeatureCollection", "features": [
{ "type": "Feature", "id" : "foo", "properties": { "id" : "baz" }, "geometry": null },
] }"""
)
lyr = ds.GetLayer(0)
feat_defn = lyr.GetLayerDefn()
assert (
feat_defn.GetFieldDefn(0).GetName() == "id"
and feat_defn.GetFieldDefn(0).GetType() == ogr.OFTString
)
feat = lyr.GetNextFeature()
if feat.GetField("id") != "baz":
feat.DumpReadable()
pytest.fail()
# id and properties.ID (#6538)
ds = ogr.Open(
"""{"type": "FeatureCollection", "features": [
{ "type": "Feature", "id" : 1, "properties": { "ID": 2 }, "geometry": null },
] }"""
)
lyr = ds.GetLayer(0)
feat_defn = lyr.GetLayerDefn()
assert (
feat_defn.GetFieldDefn(0).GetName() == "ID"
and feat_defn.GetFieldDefn(0).GetType() == ogr.OFTInteger
)
feat = lyr.GetNextFeature()
if feat.GetFID() != 1 or feat.GetField("ID") != 2:
feat.DumpReadable()
pytest.fail()
# Test handling of duplicated id
gdal.ErrorReset()
with gdaltest.error_handler():
ds = ogr.Open(
"""{"type": "FeatureCollection", "features": [
{ "type": "Feature", "id" : 1, "properties": { "foo": "bar" }, "geometry": null },
{ "type": "Feature", "id" : 1, "properties": { "foo": "baz" }, "geometry": null },
{ "type": "Feature", "id" : 2, "properties": { "foo": "baw" }, "geometry": null }
] }"""
)
assert gdal.GetLastErrorMsg() != "", "expected warning"
lyr = ds.GetLayer(0)
feat_defn = lyr.GetLayerDefn()
feat = lyr.GetNextFeature()
if feat.GetFID() != 1 or feat.GetField("foo") != "bar":
feat.DumpReadable()
pytest.fail()
feat = lyr.GetNextFeature()
if feat.GetFID() != 2 or feat.GetField("foo") != "baz":
feat.DumpReadable()
pytest.fail()
feat = lyr.GetNextFeature()
if feat.GetFID() != 3 or feat.GetField("foo") != "baw":
feat.DumpReadable()
pytest.fail()
# negative id
ds = ogr.Open(
"""{"type": "FeatureCollection", "features": [
{ "type": "Feature", "id" : -1, "properties": { "foo": "bar" }, "geometry": null },
] }"""
)
lyr = ds.GetLayer(0)
feat_defn = lyr.GetLayerDefn()
assert (
feat_defn.GetFieldDefn(0).GetName() == "id"
and feat_defn.GetFieldDefn(0).GetType() == ogr.OFTInteger
)
feat = lyr.GetNextFeature()
if feat.GetField("id") != -1:
feat.DumpReadable()
pytest.fail()
# negative id 64bit
ds = ogr.Open(
"""{"type": "FeatureCollection", "features": [
{ "type": "Feature", "id" : -1234567890123, "properties": { "foo": "bar" }, "geometry": null },
{ "type": "Feature", "id" : -2, "properties": { "foo": "baz" }, "geometry": null },
] }"""
)
lyr = ds.GetLayer(0)
feat_defn = lyr.GetLayerDefn()
assert (
feat_defn.GetFieldDefn(0).GetName() == "id"
and feat_defn.GetFieldDefn(0).GetType() == ogr.OFTInteger64
)
feat = lyr.GetNextFeature()
if feat.GetField("id") != -1234567890123:
feat.DumpReadable()
pytest.fail()
# negative id
ds = ogr.Open(
"""{"type": "FeatureCollection", "features": [
{ "type": "Feature", "id" : -2, "properties": { "foo": "baz" }, "geometry": null },
{ "type": "Feature", "id" : -1234567890123, "properties": { "foo": "bar" }, "geometry": null },
] }"""
)
lyr = ds.GetLayer(0)
feat_defn = lyr.GetLayerDefn()
assert (
feat_defn.GetFieldDefn(0).GetName() == "id"
and feat_defn.GetFieldDefn(0).GetType() == ogr.OFTInteger64
)
feat = lyr.GetNextFeature()
if feat.GetField("id") != -2:
feat.DumpReadable()
pytest.fail()
# positive and then negative id
ds = ogr.Open(
"""{"type": "FeatureCollection", "features": [
{ "type": "Feature", "id" : 1, "properties": { "foo": "baz" }, "geometry": null },
{ "type": "Feature", "id" : -1, "properties": { "foo": "bar" }, "geometry": null },
] }"""
)
lyr = ds.GetLayer(0)
feat_defn = lyr.GetLayerDefn()
assert (
feat_defn.GetFieldDefn(0).GetName() == "id"
and feat_defn.GetFieldDefn(0).GetType() == ogr.OFTInteger
)
feat = lyr.GetNextFeature()
if feat.GetField("id") != 1:
feat.DumpReadable()
pytest.fail()
# mix of int and string id
ds = ogr.Open(
"""{"type": "FeatureCollection", "features": [
{ "type": "Feature", "id" : -2, "properties": { "foo": "baz" }, "geometry": null },
{ "type": "Feature", "id" : "str", "properties": { "foo": "bar" }, "geometry": null },
{ "type": "Feature", "id" : -3, "properties": { "foo": "baz" }, "geometry": null },
] }"""
)
lyr = ds.GetLayer(0)
feat_defn = lyr.GetLayerDefn()
assert (
feat_defn.GetFieldDefn(0).GetName() == "id"
and feat_defn.GetFieldDefn(0).GetType() == ogr.OFTString
)
feat = lyr.GetNextFeature()
if feat.GetField("id") != "-2":
feat.DumpReadable()
pytest.fail()
###############################################################################
# Test nested attributes
def test_ogr_geojson_40():
ds = gdal.OpenEx(
"""{
"type": "FeatureCollection",
"features" :
[
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [ 2, 49 ]
},
"properties": {
"a_property": 1,
"some_object": {
"a_property": 1,
"another_property": 2
}
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [ 2, 49 ]
},
"properties": {
"a_property": "foo",
"some_object": {
"a_property": 1,
"another_property": 2.34
}
}
}
]
}""",
gdal.OF_VECTOR,
open_options=["FLATTEN_NESTED_ATTRIBUTES=YES", "NESTED_ATTRIBUTE_SEPARATOR=."],
)
lyr = ds.GetLayer(0)
feat = lyr.GetNextFeature()
feat = lyr.GetNextFeature()
if (
feat.GetField("a_property") != "foo"
or feat.GetField("some_object.a_property") != 1
or feat.GetField("some_object.another_property") != 2.34
):
feat.DumpReadable()
pytest.fail()
###############################################################################
# Test ogr.CreateGeometryFromJson()
def test_ogr_geojson_41():
# Check that by default we return a WGS 84 SRS
g = ogr.CreateGeometryFromJson("{ 'type': 'Point', 'coordinates' : [ 2, 49] }")
assert g.ExportToWkt() == "POINT (2 49)"
srs = g.GetSpatialReference()
g = None
assert srs.ExportToWkt().find("WGS 84") >= 0
# But if a crs object is set (allowed originally, but not recommended!), we use it
g = ogr.CreateGeometryFromJson(
'{ "type": "Point", "coordinates" : [ 2, 49], "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::4322" } } }'
)
srs = g.GetSpatialReference()
assert srs.ExportToWkt().find("4322") >= 0
# But if a crs object is set to null, set no crs
g = ogr.CreateGeometryFromJson(
'{ "type": "Point", "coordinates" : [ 2, 49], "crs": null }'
)
srs = g.GetSpatialReference()
assert not srs
###############################################################################
# Test Feature without geometry
def test_ogr_geojson_43():
ds = ogr.Open(
"""{"type": "FeatureCollection", "features":[
{"type": "Feature", "properties": {"foo": "bar"}}]}"""
)
assert ds is not None, "Failed to open datasource"
lyr = ds.GetLayerByName("OGRGeoJSON")
feature = lyr.GetNextFeature()
if feature.GetFieldAsString("foo") != "bar":
feature.DumpReadable()
pytest.fail()
lyr = None
ds = None
###############################################################################
# Test null Feature (#6166)
def test_ogr_geojson_44():
with pytest.raises(Exception):
ogr.Open("""{"type": "FeatureCollection", "features":[ null ]}""")
###############################################################################
# Test native data support
def test_ogr_geojson_45():
# Test read support
content = """{"type": "FeatureCollection", "foo": "bar", "bar": "baz",
"features":[ { "type": "Feature", "foo": ["bar", "baz", 1.0, true, false,[],{}], "properties": { "myprop": "myvalue" }, "geometry": null } ]}"""
for i in range(2):
if i == 0:
ds = gdal.OpenEx(content, gdal.OF_VECTOR, open_options=["NATIVE_DATA=YES"])
else:
gdal.FileFromMemBuffer("/vsimem/ogr_geojson_45.json", content)
ds = gdal.OpenEx(
"/vsimem/ogr_geojson_45.json",
gdal.OF_VECTOR,
open_options=["NATIVE_DATA=YES"],
)
lyr = ds.GetLayer(0)
native_data = lyr.GetMetadataItem("NATIVE_DATA", "NATIVE_DATA")
assert native_data == '{ "foo": "bar", "bar": "baz" }'
native_media_type = lyr.GetMetadataItem("NATIVE_MEDIA_TYPE", "NATIVE_DATA")
assert native_media_type == "application/vnd.geo+json"
f = lyr.GetNextFeature()
native_data = f.GetNativeData()
if i == 0:
expected = [
'{ "type": "Feature", "foo": [ "bar", "baz", 1.000000, true, false, [ ], { } ], "properties": { "myprop": "myvalue" }, "geometry": null }',
'{ "type": "Feature", "foo": [ "bar", "baz", 1.0, true, false, [ ], { } ], "properties": { "myprop": "myvalue" }, "geometry": null }',
]
else:
expected = [
'{"type":"Feature","foo":["bar","baz",1.0,true,false,[],{}],"properties":{"myprop":"myvalue"},"geometry":null}'
]
assert native_data in expected
native_media_type = f.GetNativeMediaType()
assert native_media_type == "application/vnd.geo+json"
ds = None
if i == 1:
gdal.Unlink("/vsimem/ogr_geojson_45.json")
ds = ogr.GetDriverByName("GeoJSON").CreateDataSource("/vsimem/ogr_geojson_45.json")
lyr = ds.CreateLayer(
"test",
options=[
'NATIVE_DATA={ "type": "ignored", "bbox": [ 0, 0, 0, 0 ], "foo": "bar", "bar": "baz", "features": "ignored" }',
"NATIVE_MEDIA_TYPE=application/vnd.geo+json",
],
)
f = ogr.Feature(lyr.GetLayerDefn())
json_geom = """{ "type": "GeometryCollection", "foo_gc": "bar_gc", "geometries" : [
{ "type": "Point", "foo_point": "bar_point", "coordinates": [0,1,2, 3] },
{ "type": "LineString", "foo_linestring": "bar_linestring", "coordinates": [[0,1,2, 4]] },
{ "type": "MultiPoint", "foo_multipoint": "bar_multipoint", "coordinates": [[0,1,2, 5]] },
{ "type": "MultiLineString", "foo_multilinestring": "bar_multilinestring", "coordinates": [[[0,1,2, 6]]] },
{ "type": "Polygon", "foo_polygon": "bar_polygon", "coordinates": [[[0,1,2, 7]]] },
{ "type": "MultiPolygon", "foo_multipolygon": "bar_multipolygon", "coordinates": [[[[0,1,2, 8]]]] }
] }"""
f.SetNativeData(
'{ "type": "ignored", "bbox": "ignored", "properties" : "ignored", "foo_feature": "bar_feature", "geometry": %s }'
% json_geom
)
f.SetNativeMediaType("application/vnd.geo+json")
f.SetGeometry(ogr.CreateGeometryFromJson(json_geom))
lyr.CreateFeature(f)
ds = None
fp = gdal.VSIFOpenL("/vsimem/ogr_geojson_45.json", "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
gdal.Unlink("/vsimem/ogr_geojson_45.json")
assert (
'"bbox": [ 0, 1, 2, 0, 1, 2 ],' in data
and '"foo": "bar"' in data
and '"bar": "baz"' in data
and '"foo_feature": "bar_feature"' in data
and '"foo_gc": "bar_gc"' in data
and '"foo_point": "bar_point"' in data
and "3" in data
and '"foo_linestring": "bar_linestring"' in data
and "4" in data
and '"foo_multipoint": "bar_multipoint"' in data
and "5" in data
and '"foo_multilinestring": "bar_multilinestring"' in data
and "6" in data
and '"foo_polygon": "bar_polygon"' in data
and "7" in data
and '"foo_multipolygon": "bar_multipolygon"' in data
and "8" in data
)
# Test native support with string id
src_ds = gdal.OpenEx(
"""{
"type": "FeatureCollection",
"features": [
{ "type": "Feature",
"id": "foobarbaz",
"properties": {},
"geometry": null
}
]
}
""",
open_options=["NATIVE_DATA=YES"],
)
gdal.VectorTranslate("/vsimem/out.json", src_ds, format="GeoJSON")
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "id": "foobarbaz", "properties": { }, "geometry": null }
]
}
"""
assert json.loads(got) == json.loads(expected)
# Test native support with numeric id
src_ds = gdal.OpenEx(
"""{
"type": "FeatureCollection",
"features": [
{ "type": "Feature",
"id": 1234657890123,
"properties": {},
"geometry": null
}
]
}
""",
open_options=["NATIVE_DATA=YES"],
)
gdal.VectorTranslate("/vsimem/out.json", src_ds, format="GeoJSON")
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "id": 1234657890123, "properties": { }, "geometry": null }
]
}
"""
assert json.loads(got) == json.loads(expected)
###############################################################################
# Test that writing JSon content as value of a string field is serialized as it
def test_ogr_geojson_46():
ds = ogr.GetDriverByName("GeoJSON").CreateDataSource("/vsimem/ogr_geojson_46.json")
lyr = ds.CreateLayer("test")
lyr.CreateField(ogr.FieldDefn("myprop"))
f = ogr.Feature(lyr.GetLayerDefn())
f["myprop"] = '{ "a": "b" }'
lyr.CreateFeature(f)
ds = None
fp = gdal.VSIFOpenL("/vsimem/ogr_geojson_46.json", "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
gdal.Unlink("/vsimem/ogr_geojson_46.json")
assert '{ "myprop": { "a": "b" } }' in data
###############################################################################
# Test update support
@gdaltest.disable_exceptions()
def test_ogr_geojson_47():
# ERROR 6: Update from inline definition not supported
with gdaltest.error_handler():
ds = ogr.Open('{"type": "FeatureCollection", "features":[]}', update=1)
assert ds is None
gdal.FileFromMemBuffer(
"/vsimem/ogr_geojson_47.json",
"""{"type": "FeatureCollection", "foo": "bar",
"features":[ { "type": "Feature", "bar": "baz", "properties": { "myprop": "myvalue" }, "geometry": null } ]}""",
)
# Test read support
ds = ogr.Open("/vsimem/ogr_geojson_47.json", update=1)
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
f.SetField("myprop", "another_value")
lyr.SetFeature(f)
ds = None
fp = gdal.VSIFOpenL("/vsimem/ogr_geojson_47.json", "rb")
if fp is not None:
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
else:
data = None
# we don't want crs if there's no in the source
assert (
'"foo": "bar"' in data
and '"bar": "baz"' in data
and "crs" not in data
and '"myprop": "another_value"' in data
)
# Test append support
ds = ogr.Open("/vsimem/ogr_geojson_47.json", update=1)
lyr = ds.GetLayer(0)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(1 2)"))
lyr.CreateFeature(f)
if f.GetFID() != 1:
f.DumpReadable()
pytest.fail()
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(2 3)"))
lyr.CreateFeature(f)
f = lyr.GetNextFeature()
if f.GetFID() != 0:
f.DumpReadable()
pytest.fail()
ds = None
# Test append support
ds = ogr.Open("/vsimem/ogr_geojson_47.json", update=1)
lyr = ds.GetLayer(0)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(4 5)"))
lyr.CreateFeature(f)
f.SetField("myprop", "value_of_point_4_5")
lyr.SetFeature(f)
ds = None
ds = ogr.Open("/vsimem/ogr_geojson_47.json")
lyr = ds.GetLayer(0)
assert lyr.GetFeatureCount() == 4
ds = None
fp = gdal.VSIFOpenL("/vsimem/ogr_geojson_47.json", "rb")
if fp is not None:
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
else:
data = None
# we don't want crs if there's no in the source
assert (
'"foo": "bar"' in data
and '"bar": "baz"' in data
and "crs" not in data
and '"myprop": "another_value"' in data
and '"myprop": "value_of_point_4_5"' in data
and "id" not in data
)
gdal.Unlink("/vsimem/ogr_geojson_47.json")
# Test appending to empty features array
gdal.FileFromMemBuffer(
"/vsimem/ogr_geojson_47.json",
"""{ "type": "FeatureCollection", "features": []}""",
)
ds = ogr.Open("/vsimem/ogr_geojson_47.json", update=1)
lyr = ds.GetLayer(0)
f = ogr.Feature(lyr.GetLayerDefn())
lyr.CreateFeature(f)
ds = None
ds = ogr.Open("/vsimem/ogr_geojson_47.json")
lyr = ds.GetLayer(0)
assert lyr.GetFeatureCount() == 1
ds = None
# Test appending to array ending with non feature
gdal.FileFromMemBuffer(
"/vsimem/ogr_geojson_47.json",
"""{ "type": "FeatureCollection", "features": [ null ]}""",
)
ds = ogr.Open("/vsimem/ogr_geojson_47.json", update=1)
lyr = ds.GetLayer(0)
f = ogr.Feature(lyr.GetLayerDefn())
lyr.CreateFeature(f)
ds = None
ds = ogr.Open("/vsimem/ogr_geojson_47.json")
lyr = ds.GetLayer(0)
assert lyr.GetFeatureCount() == 1
ds = None
# Test appending to feature collection not ending with "features"
gdal.FileFromMemBuffer(
"/vsimem/ogr_geojson_47.json",
"""{ "type": "FeatureCollection", "features": [], "something": "else"}""",
)
ds = ogr.Open("/vsimem/ogr_geojson_47.json", update=1)
lyr = ds.GetLayer(0)
f = ogr.Feature(lyr.GetLayerDefn())
lyr.CreateFeature(f)
ds = None
ds = ogr.Open("/vsimem/ogr_geojson_47.json")
lyr = ds.GetLayer(0)
assert lyr.GetFeatureCount() == 1
ds = None
fp = gdal.VSIFOpenL("/vsimem/ogr_geojson_47.json", "rb")
if fp is not None:
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
else:
data = None
assert "something" in data
with gdaltest.config_option("OGR_GEOJSON_REWRITE_IN_PLACE", "YES"):
# Test appending to feature collection with "bbox"
gdal.FileFromMemBuffer(
"/vsimem/ogr_geojson_47.json",
"""{ "type": "FeatureCollection", "bbox": [0,0,0,0], "features": [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [0,0]} } ]}""",
)
ds = ogr.Open("/vsimem/ogr_geojson_47.json", update=1)
lyr = ds.GetLayer(0)
f = ogr.Feature(lyr.GetLayerDefn())
lyr.CreateFeature(f)
ds = None
ds = ogr.Open("/vsimem/ogr_geojson_47.json")
lyr = ds.GetLayer(0)
assert lyr.GetFeatureCount() == 2
ds = None
fp = gdal.VSIFOpenL("/vsimem/ogr_geojson_47.json", "rb")
if fp is not None:
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
else:
data = None
assert "bbox" in data
gdal.Unlink("/vsimem/ogr_geojson_47.json")
###############################################################################
# Test update support with file that has a single feature not in a FeatureCollection
def test_ogr_geojson_48():
gdal.FileFromMemBuffer(
"/vsimem/ogr_geojson_48.json",
"""{ "type": "Feature", "bar": "baz", "bbox": [2,49,2,49], "properties": { "myprop": "myvalue" }, "geometry": {"type": "Point", "coordinates": [ 2, 49]} }""",
)
# Test read support
ds = ogr.Open("/vsimem/ogr_geojson_48.json", update=1)
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
f.SetField("myprop", "another_value")
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (3 50)"))
lyr.SetFeature(f)
ds = None
fp = gdal.VSIFOpenL("/vsimem/ogr_geojson_48.json", "rb")
if fp is not None:
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
else:
data = None
gdal.Unlink("/vsimem/ogr_geojson_48.json")
# we don't want crs if there's no in the source
assert (
'"bar": "baz"' in data
and '"bbox": [ 3.0, 50.0, 3.0, 50.0 ]' in data
and "crs" not in data
and "FeatureCollection" not in data
and '"myprop": "another_value"' in data
)
###############################################################################
# Test ARRAY_AS_STRING
def test_ogr_geojson_49():
gdal.FileFromMemBuffer(
"/vsimem/ogr_geojson_49.json",
"""{ "type": "Feature", "properties": { "foo": ["bar"] }, "geometry": null }""",
)
# Test read support
ds = gdal.OpenEx(
"/vsimem/ogr_geojson_49.json", open_options=["ARRAY_AS_STRING=YES"]
)
lyr = ds.GetLayer(0)
assert lyr.GetLayerDefn().GetFieldDefn(0).GetType() == ogr.OFTString
f = lyr.GetNextFeature()
if f["foo"] != '[ "bar" ]':
f.DumpReadable()
pytest.fail()
ds = None
gdal.Unlink("/vsimem/ogr_geojson_49.json")
###############################################################################
# Test that we serialize floating point values with enough significant figures
def test_ogr_geojson_50():
ds = ogr.GetDriverByName("GeoJSON").CreateDataSource("/vsimem/ogr_geojson_50.json")
lyr = ds.CreateLayer("test")
lyr.CreateField(ogr.FieldDefn("val", ogr.OFTReal))
f = ogr.Feature(lyr.GetLayerDefn())
f["val"] = 1.23456789012456
lyr.CreateFeature(f)
# To test smart rounding
f = ogr.Feature(lyr.GetLayerDefn())
f["val"] = 5268.813
lyr.CreateFeature(f)
f = None
ds = None
fp = gdal.VSIFOpenL("/vsimem/ogr_geojson_50.json", "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
gdal.Unlink("/vsimem/ogr_geojson_50.json")
assert "1.23456789012456" in data or "5268.813 " in data
# If SIGNIFICANT_FIGURES is explicitly specified, and COORDINATE_PRECISION not,
# then it also applies to coordinates
ds = ogr.GetDriverByName("GeoJSON").CreateDataSource("/vsimem/ogr_geojson_50.json")
lyr = ds.CreateLayer("test", options=["SIGNIFICANT_FIGURES=17"])
lyr.CreateField(ogr.FieldDefn("val", ogr.OFTReal))
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (0.0000123456789012456 0)"))
lyr.CreateFeature(f)
f = None
ds = None
fp = gdal.VSIFOpenL("/vsimem/ogr_geojson_50.json", "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
gdal.Unlink("/vsimem/ogr_geojson_50.json")
assert "1.23456789012456" in data or "-5" in data
# If SIGNIFICANT_FIGURES is explicitly specified, and COORDINATE_PRECISION too,
# then SIGNIFICANT_FIGURES only applies to non-coordinates floating point values.
ds = ogr.GetDriverByName("GeoJSON").CreateDataSource("/vsimem/ogr_geojson_50.json")
lyr = ds.CreateLayer(
"test", options=["COORDINATE_PRECISION=15", "SIGNIFICANT_FIGURES=17"]
)
lyr.CreateField(ogr.FieldDefn("val", ogr.OFTReal))
f = ogr.Feature(lyr.GetLayerDefn())
f["val"] = 1.23456789012456
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (0.0000123456789012456 0)"))
lyr.CreateFeature(f)
f = None
ds = None
fp = gdal.VSIFOpenL("/vsimem/ogr_geojson_50.json", "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
gdal.Unlink("/vsimem/ogr_geojson_50.json")
assert "0.00001234" in data and "1.23456789012456" in data
###############################################################################
# Test writing empty geometries
def test_ogr_geojson_51():
ds = ogr.GetDriverByName("GeoJSON").CreateDataSource("/vsimem/ogr_geojson_51.json")
lyr = ds.CreateLayer("test")
lyr.CreateField(ogr.FieldDefn("id", ogr.OFTInteger))
f = ogr.Feature(lyr.GetLayerDefn())
f["id"] = 1
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT EMPTY"))
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f["id"] = 2
f.SetGeometry(ogr.CreateGeometryFromWkt("LINESTRING EMPTY"))
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f["id"] = 3
f.SetGeometry(ogr.CreateGeometryFromWkt("POLYGON EMPTY"))
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f["id"] = 4
f.SetGeometry(ogr.CreateGeometryFromWkt("MULTIPOINT EMPTY"))
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f["id"] = 5
f.SetGeometry(ogr.CreateGeometryFromWkt("MULTILINESTRING EMPTY"))
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f["id"] = 6
f.SetGeometry(ogr.CreateGeometryFromWkt("MULTIPOLYGON EMPTY"))
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f["id"] = 7
f.SetGeometry(ogr.CreateGeometryFromWkt("GEOMETRYCOLLECTION EMPTY"))
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
ds = None
fp = gdal.VSIFOpenL("/vsimem/ogr_geojson_51.json", "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
gdal.Unlink("/vsimem/ogr_geojson_51.json")
assert '{ "id": 1 }, "geometry": null' in data
assert (
'{ "id": 2 }, "geometry": { "type": "LineString", "coordinates": [ ] } }'
in data
)
assert (
'{ "id": 3 }, "geometry": { "type": "Polygon", "coordinates": [ ] } }' in data
)
assert (
'{ "id": 4 }, "geometry": { "type": "MultiPoint", "coordinates": [ ] } }'
in data
)
assert (
'{ "id": 5 }, "geometry": { "type": "MultiLineString", "coordinates": [ ] } }'
in data
)
assert (
'{ "id": 6 }, "geometry": { "type": "MultiPolygon", "coordinates": [ ] } }'
in data
)
assert (
'{ "id": 7 }, "geometry": { "type": "GeometryCollection", "geometries": [ ] } }'
in data
)
###############################################################################
# Test NULL type detection
def test_ogr_geojson_52():
ds = ogr.Open("data/geojson/nullvalues.geojson")
assert ds is not None, "Failed to open datasource"
assert ds.GetLayerCount() == 1, "Wrong number of layers"
lyr = ds.GetLayerByName("nullvalues")
assert lyr is not None, "Missing layer called nullvalues"
fld = lyr.GetLayerDefn().GetFieldDefn(0)
assert fld.GetNameRef() == "int"
assert fld.GetType() == ogr.OFTInteger
fld = lyr.GetLayerDefn().GetFieldDefn(1)
assert fld.GetNameRef() == "string"
assert fld.GetType() == ogr.OFTString
fld = lyr.GetLayerDefn().GetFieldDefn(2)
assert fld.GetNameRef() == "double"
assert fld.GetType() == ogr.OFTReal
###############################################################################
# Test that M is ignored (this is a test of OGRLayer::CreateFeature() actually)
def test_ogr_geojson_53():
ds = ogr.GetDriverByName("GeoJSON").CreateDataSource("/vsimem/ogr_geojson_53.json")
lyr = ds.CreateLayer("test")
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT ZM (1 2 3 4)"))
lyr.CreateFeature(f)
ds = None
fp = gdal.VSIFOpenL("/vsimem/ogr_geojson_53.json", "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
gdal.Unlink("/vsimem/ogr_geojson_53.json")
assert '{ "type": "Point", "coordinates": [ 1.0, 2.0, 3.0 ] }' in data
###############################################################################
# Test NULL type detection when first value is null
def test_ogr_geojson_54():
ds = ogr.Open(
"""{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "properties": { "int": null, "string": null, "double": null, "dt" : null, "boolean": null, "null": null }, "geometry": null },
{ "type": "Feature", "properties": { "int": 168, "string": "string", "double": 1.23, "dt" : "2016-05-18T12:34:56Z", "boolean": true }, "geometry": null }
]
}
"""
)
lyr = ds.GetLayer(0)
fld = lyr.GetLayerDefn().GetFieldDefn(0)
assert fld.GetType() == ogr.OFTInteger
fld = lyr.GetLayerDefn().GetFieldDefn(1)
assert fld.GetType() == ogr.OFTString
fld = lyr.GetLayerDefn().GetFieldDefn(2)
assert fld.GetType() == ogr.OFTReal
fld = lyr.GetLayerDefn().GetFieldDefn(3)
assert fld.GetType() == ogr.OFTDateTime
fld = lyr.GetLayerDefn().GetFieldDefn(4)
assert fld.GetType() == ogr.OFTInteger
assert fld.GetSubType() == ogr.OFSTBoolean
assert fld.GetWidth() == 1
fld = lyr.GetLayerDefn().GetFieldDefn(5)
assert fld.GetType() == ogr.OFTString
###############################################################################
# Test RFC 7946
def read_file(filename):
f = gdal.VSIFOpenL(filename, "rb")
if f is None:
return None
content = gdal.VSIFReadL(1, 10000, f).decode("UTF-8")
gdal.VSIFCloseL(f)
return content
def test_ogr_geojson_55():
# Basic test for standard bbox and coordinate truncation
gdal.VectorTranslate(
"/vsimem/out.json",
"""{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "id": 123, "properties": {}, "geometry": { "type": "Point", "coordinates": [2.123456789, 49] } },
{ "type": "Feature", "id": 124, "properties": {}, "geometry": { "type": "Point", "coordinates": [3, 50] } }
]
}""",
options="-f GeoJSON -lco RFC7946=YES -lco WRITE_BBOX=YES -preserve_fid",
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"bbox": [ 2.1234568, 49.0000000, 3.0000000, 50.0000000 ],
"features": [
{ "type": "Feature", "id": 123, "properties": { }, "bbox": [ 2.1234568, 49.0, 2.1234568, 49.0 ], "geometry": { "type": "Point", "coordinates": [ 2.1234568, 49.0 ] } },
{ "type": "Feature", "id": 124, "properties": { }, "bbox": [ 3.0, 50.0, 3.0, 50.0 ], "geometry": { "type": "Point", "coordinates": [ 3.0, 50.0 ] } }
]
}
"""
assert json.loads(got) == json.loads(expected)
# Test polygon winding order
gdal.VectorTranslate(
"/vsimem/out.json",
"""{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [[[2,49],[3,49],[3,50],[2,50],[2,49]],[[2.1,49.1],[2.1,49.9],[2.9,49.9],[2.9,49.1],[2.1,49.1]]] } },
{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [[[2,49],[2,50],[3,50],[3,49],[2,49]],[[2.1,49.1],[2.9,49.1],[2.9,49.9],[2.1,49.9],[2.1,49.1]]] } },
]
}
""",
format="GeoJSON",
layerCreationOptions=["RFC7946=YES", "WRITE_BBOX=YES"],
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"bbox": [ 2.0000000, 49.0000000, 3.0000000, 50.0000000 ],
"features": [
{ "type": "Feature", "properties": { }, "bbox": [ 2.0, 49.0, 3.0, 50.0 ], "geometry": { "type": "Polygon", "coordinates": [ [ [ 2.0, 49.0 ], [ 3.0, 49.0 ], [ 3.0, 50.0 ], [ 2.0, 50.0 ], [ 2.0, 49.0 ] ], [ [ 2.1, 49.1 ], [ 2.1, 49.9 ], [ 2.9, 49.9 ], [ 2.9, 49.1 ], [ 2.1, 49.1 ] ] ] } },
{ "type": "Feature", "properties": { }, "bbox": [ 2.0, 49.0, 3.0, 50.0 ], "geometry": { "type": "Polygon", "coordinates": [ [ [ 2.0, 49.0 ], [ 3.0, 49.0 ], [ 3.0, 50.0 ], [ 2.0, 50.0 ], [ 2.0, 49.0 ] ], [ [ 2.1, 49.1 ], [ 2.1, 49.9 ], [ 2.9, 49.9 ], [ 2.9, 49.1 ], [ 2.1, 49.1 ] ] ] } }
]
}
"""
assert json.loads(got) == json.loads(expected)
# Test foreign member
src_ds = gdal.OpenEx(
"""{
"type": "FeatureCollection",
"coordinates": "should not be found in output",
"geometries": "should not be found in output",
"geometry": "should not be found in output",
"properties": "should not be found in output",
"valid": "should be in output",
"crs": "should not be found in output",
"bbox": [0,0,0,0],
"features": [
{ "type": "Feature",
"id": ["not expected as child of features"],
"coordinates": "should not be found in output",
"geometries": "should not be found in output",
"features": "should not be found in output",
"valid": "should be in output",
"properties": { "foo": "bar" },
"geometry": {
"type": "Point",
"bbox": [0,0,0,0],
"geometry": "should not be found in output",
"properties": "should not be found in output",
"features": "should not be found in output",
"valid": "should be in output",
"coordinates": [2,49]
}
}
]
}
""",
open_options=["NATIVE_DATA=YES"],
)
gdal.VectorTranslate(
"/vsimem/out.json",
src_ds,
format="GeoJSON",
layerCreationOptions=["RFC7946=YES"],
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"valid": "should be in output",
"bbox": [ 2.0000000, 49.0000000, 2.0000000, 49.0000000 ],
"features": [
{ "type": "Feature",
"valid": "should be in output",
"properties": { "id": [ "not expected as child of features" ], "foo": "bar" },
"geometry": { "type": "Point", "coordinates": [ 2.0, 49.0 ], "valid": "should be in output" } }
]
}
"""
assert json.loads(got) == json.loads(expected)
###############################################################################
# Test RFC 7946 (that require geos)
@pytest.mark.require_geos
def test_ogr_geojson_56():
# Test offsetting longitudes beyond antimeridian
gdal.VectorTranslate(
"/vsimem/out.json",
"""{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "geometry": { "type": "Point", "coordinates": [182, 49] } },
{ "type": "Feature", "geometry": { "type": "Point", "coordinates": [-183, 50] } },
{ "type": "Feature", "geometry": { "type": "LineString", "coordinates": [[-183, 51],[-182, 48]] } },
{ "type": "Feature", "geometry": { "type": "LineString", "coordinates": [[182, 52],[183, 47]] } },
{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [[[-183, 51],[-183, 48],[-182, 48],[-183, 48],[-183, 51]]] } },
{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [[[183, 51],[183, 48],[182, 48],[183, 48],[183, 51]]] } },
]
}""",
format="GeoJSON",
layerCreationOptions=["RFC7946=YES", "WRITE_BBOX=YES"],
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"bbox": [ -178.0000000, 47.0000000, 178.0000000, 52.0000000 ],
"features": [
{ "type": "Feature", "properties": { }, "bbox": [ -178.0, 49.0, -178.0, 49.0 ], "geometry": { "type": "Point", "coordinates": [ -178.0, 49.0 ] } },
{ "type": "Feature", "properties": { }, "bbox": [ 177.0, 50.0, 177.0, 50.0 ], "geometry": { "type": "Point", "coordinates": [ 177.0, 50.0 ] } },
{ "type": "Feature", "properties": { }, "bbox": [ 177.0, 48.0, 178.0, 51.0 ], "geometry": { "type": "LineString", "coordinates": [ [ 177.0, 51.0 ], [ 178.0, 48.0 ] ] } },
{ "type": "Feature", "properties": { }, "bbox": [ -178.0, 47.0, -177.0, 52.0 ], "geometry": { "type": "LineString", "coordinates": [ [ -178.0, 52.0 ], [ -177.0, 47.0 ] ] } },
{ "type": "Feature", "properties": { }, "bbox": [ 177.0, 48.0, 178.0, 51.0 ], "geometry": { "type": "Polygon", "coordinates": [ [ [ 177.0, 51.0 ], [ 177.0, 48.0 ], [ 178.0, 48.0 ], [ 177.0, 48.0 ], [ 177.0, 51.0 ] ] ] } },
{ "type": "Feature", "properties": { }, "bbox": [ -178.0, 48.0, -177.0, 51.0 ], "geometry": { "type": "Polygon", "coordinates": [ [ [ -177.0, 51.0 ], [ -177.0, 48.0 ], [ -178.0, 48.0 ], [ -177.0, 48.0 ], [ -177.0, 51.0 ] ] ] } }
]
}
"""
assert json.loads(got) == json.loads(expected)
# Test geometries across the antimeridian
gdal.VectorTranslate(
"/vsimem/out.json",
"""{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "geometry": { "type": "LineString", "coordinates": [[179, 51],[-179, 48]] } },
{ "type": "Feature", "geometry": { "type": "LineString", "coordinates": [[-179, 52],[179, 47]] } },
{ "type": "Feature", "geometry": { "type": "MultiLineString", "coordinates": [ [ [ 179.0, 51.0 ], [ 180.0, 49.5 ] ], [ [ -180.0, 49.5 ], [ -179.0, 48.0 ] ] ] } },
{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [[[177, 51],[-175, 51],[-175, 48],[177, 48],[177, 51]]] } },
{ "type": "Feature", "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 177.0, 51.0 ], [ 177.0, 48.0 ], [ 180.0, 48.0 ], [ 180.0, 51.0 ], [ 177.0, 51.0 ] ] ], [ [ [ -180.0, 51.0 ], [ -180.0, 48.0 ], [ -175.0, 48.0 ], [ -175.0, 51.0 ], [ -180.0, 51.0 ] ] ] ] } }
]
}""",
format="GeoJSON",
layerCreationOptions=["RFC7946=YES", "WRITE_BBOX=YES"],
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"bbox": [ 177.0000000, 47.0000000, -175.0000000, 52.0000000 ],
"features": [
{ "type": "Feature", "properties": { }, "bbox": [ 179.0, 48.0, -179.0, 51.0 ], "geometry": { "type": "MultiLineString", "coordinates": [ [ [ 179.0, 51.0 ], [ 180.0, 49.5 ] ], [ [ -180.0, 49.5 ], [ -179.0, 48.0 ] ] ] } },
{ "type": "Feature", "properties": { }, "bbox": [ 179.0, 47.0, -179.0, 52.0 ], "geometry": { "type": "MultiLineString", "coordinates": [ [ [ -179.0, 52.0 ], [ -180.0, 49.5 ] ], [ [ 180.0, 49.5 ], [ 179.0, 47.0 ] ] ] } },
{ "type": "Feature", "properties": { }, "bbox": [ 179.0, 48.0, -179.0, 51.0 ], "geometry": { "type": "MultiLineString", "coordinates": [ [ [ 179.0, 51.0 ], [ 180.0, 49.5 ] ], [ [ -180.0, 49.5 ], [ -179.0, 48.0 ] ] ] } },
{ "type": "Feature", "properties": { }, "bbox": [ 177.0, 48.0, -175.0, 51.0 ], "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 177.0, 51.0 ], [ 177.0, 48.0 ], [ 180.0, 48.0 ], [ 180.0, 51.0 ], [ 177.0, 51.0 ] ] ], [ [ [ -180.0, 51.0 ], [ -180.0, 48.0 ], [ -175.0, 48.0 ], [ -175.0, 51.0 ], [ -180.0, 51.0 ] ] ] ] } },
{ "type": "Feature", "properties": { }, "bbox": [ 177.0, 48.0, -175.0, 51.0 ], "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 177.0, 51.0 ], [ 177.0, 48.0 ], [ 180.0, 48.0 ], [ 180.0, 51.0 ], [ 177.0, 51.0 ] ] ], [ [ [ -180.0, 51.0 ], [ -180.0, 48.0 ], [ -175.0, 48.0 ], [ -175.0, 51.0 ], [ -180.0, 51.0 ] ] ] ] } }
]
}
"""
j_got = json.loads(got)
j_expected = json.loads(expected)
assert j_got["bbox"] == j_expected["bbox"]
assert len(j_expected["features"]) == 5
assert (
ogrtest.check_feature_geometry(
ogr.CreateGeometryFromJson(json.dumps(j_got["features"][0]["geometry"])),
ogr.CreateGeometryFromJson(
json.dumps(j_expected["features"][0]["geometry"])
),
)
== 0
)
assert (
ogrtest.check_feature_geometry(
ogr.CreateGeometryFromJson(json.dumps(j_got["features"][1]["geometry"])),
ogr.CreateGeometryFromJson(
json.dumps(j_expected["features"][1]["geometry"])
),
)
== 0
)
assert (
ogrtest.check_feature_geometry(
ogr.CreateGeometryFromJson(json.dumps(j_got["features"][2]["geometry"])),
ogr.CreateGeometryFromJson(
json.dumps(j_expected["features"][2]["geometry"])
),
)
== 0
)
assert (
ogrtest.check_feature_geometry(
ogr.CreateGeometryFromJson(json.dumps(j_got["features"][3]["geometry"])),
ogr.CreateGeometryFromJson(
json.dumps(j_expected["features"][3]["geometry"])
),
)
== 0
)
assert (
ogrtest.check_feature_geometry(
ogr.CreateGeometryFromJson(json.dumps(j_got["features"][4]["geometry"])),
ogr.CreateGeometryFromJson(
json.dumps(j_expected["features"][4]["geometry"])
),
)
== 0
)
# Test polygon geometry that covers the whole world (#2833)
gdal.VectorTranslate(
"/vsimem/out.json",
"""{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "geometry": {"type":"Polygon","coordinates":[[[-180,-90.0],[180,-90.0],[180,90.0],[-180,90.0],[-180,-90.0]]]} }
]
}""",
format="GeoJSON",
layerCreationOptions=["RFC7946=YES", "WRITE_BBOX=YES"],
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"bbox": [ -180.0000000, -90.0000000, 180.0000000, 90.0000000 ],
"features": [
{ "type": "Feature", "properties": { }, "bbox": [ -180.0, -90.0, 180.0, 90.0 ], "geometry": { "type": "Polygon", "coordinates": [ [ [ -180.0, -90.0 ], [ 180.0, -90.0 ], [ 180.0, 90.0 ], [ -180.0, 90.0 ], [ -180.0, -90.0 ] ] ] } }
]
}
"""
assert json.loads(got) == json.loads(expected)
# Test polygon geometry with one longitude at +/- 180deg (#6250)
gdal.VectorTranslate(
"/vsimem/out.json",
"""{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "geometry": {"type":"Polygon","coordinates":[[[-180,50],[179.5,50.0],[179.5,40],[-180,45],[-180,50]]]} }
]
}""",
format="GeoJSON",
layerCreationOptions=["RFC7946=YES", "WRITE_BBOX=YES"],
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "properties": { }, "bbox": [ -180.0, 40.0, 179.5, 50.0 ], "geometry": { "type": "Polygon", "coordinates": [ [ [ -180.0, 50.0 ], [ -180.0, 45.0 ], [ 179.5, 40.0 ], [ 179.5, 50.0 ], [ -180.0, 50.0 ] ] ] } }
],
"bbox": [ -180.0000000, 40.0000000, 179.5000000, 50.0000000 ]
}
"""
assert json.loads(got) == json.loads(expected)
# Test WRAPDATELINE=NO (#6250)
gdal.VectorTranslate(
"/vsimem/out.json",
"""{"type":"LineString","coordinates":[[179,50],[-179,50]]}""",
format="GeoJSON",
layerCreationOptions=["RFC7946=YES", "WRITE_BBOX=YES", "WRAPDATELINE=NO"],
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "properties": { }, "bbox": [ -179.0, 50.0, 179.0, 50.0 ], "geometry": { "type": "LineString", "coordinates": [ [ 179.0, 50.0 ], [ -179.0, 50.0 ] ] } }
],
"bbox": [ -179.0000000, 50.0000000, 179.0000000, 50.0000000 ]
}
"""
assert json.loads(got) == json.loads(expected)
###############################################################################
# Test RFC 7946 and reprojection
@pytest.mark.require_geos
def test_ogr_geojson_57():
# Standard case: EPSG:32662: WGS 84 / Plate Carre
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0)
sr = osr.SpatialReference()
sr.SetFromUserInput(
"+proj=eqc +lat_ts=0 +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs"
)
lyr = src_ds.CreateLayer("test", srs=sr)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"POLYGON((2000000 2000000,2000000 -2000000,-2000000 -2000000,-2000000 2000000,2000000 2000000))"
)
)
lyr.CreateFeature(f)
gdal.VectorTranslate(
"/vsimem/out.json",
src_ds,
format="GeoJSON",
layerCreationOptions=["WRITE_NAME=NO", "RFC7946=YES", "WRITE_BBOX=YES"],
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"bbox": [ -17.9663057, -17.9663057, 17.9663057, 17.9663057 ],
"features": [
{ "type": "Feature", "properties": { }, "bbox": [ -17.9663057, -17.9663057, 17.9663057, 17.9663057 ], "geometry": { "type": "Polygon", "coordinates": [ [ [ 17.9663057, 17.9663057 ], [ -17.9663057, 17.9663057 ], [ -17.9663057, -17.9663057 ], [ 17.9663057, -17.9663057 ], [ 17.9663057, 17.9663057 ] ] ] } }
]
}
"""
assert json.loads(got) == json.loads(expected)
# Polar case: EPSG:3995: WGS 84 / Arctic Polar Stereographic
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0)
sr = osr.SpatialReference()
sr.SetFromUserInput(
"+proj=stere +lat_0=90 +lat_ts=71 +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs"
)
lyr = src_ds.CreateLayer("test", srs=sr)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"POLYGON((2000000 2000000,2000000 -2000000,-2000000 -2000000,-2000000 2000000,2000000 2000000))"
)
)
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"POLYGON((-2000000 -2000000,-1000000 -2000000,-1000000 2000000,-2000000 2000000,-2000000 -2000000))"
)
)
lyr.CreateFeature(f)
gdal.VectorTranslate(
"/vsimem/out.json",
src_ds,
format="GeoJSON",
layerCreationOptions=["WRITE_NAME=NO", "RFC7946=YES", "WRITE_BBOX=YES"],
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"bbox": [ -180.0000000, 64.3861643, 180.0000000, 90.0000000 ],
"features": [
{ "type": "Feature", "properties": { }, "bbox": [ -180.0, 64.3861643, 180.0, 90.0 ], "geometry": { "type": "Polygon", "coordinates": [ [ [ 135.0, 64.3861643 ], [ 180.0, 71.7425119 ], [ 180.0, 90.0 ], [ -180.0, 90.0 ], [ -180.0, 71.7425119 ], [ -135.0, 64.3861643 ], [ -45.0, 64.3861643 ], [ 45.0, 64.3861643 ], [ 135.0, 64.3861643 ] ] ] } },
{ "type": "Feature", "properties": { }, "bbox": [ -153.4349488, 64.3861643, -26.5650512, 69.6286694 ], "geometry": { "type": "Polygon", "coordinates": [ [ [ -45.0, 64.3861643 ], [ -26.5650512, 69.6286694 ], [ -153.4349488, 69.6286694 ], [ -135.0, 64.3861643 ], [ -45.0, 64.3861643 ] ] ] } }
]
}
"""
j_got = json.loads(got)
j_expected = json.loads(expected)
assert j_got["bbox"] == j_expected["bbox"]
assert len(j_expected["features"]) == 2
assert (
ogrtest.check_feature_geometry(
ogr.CreateGeometryFromJson(json.dumps(j_got["features"][0]["geometry"])),
ogr.CreateGeometryFromJson(
json.dumps(j_expected["features"][0]["geometry"])
),
)
== 0
)
assert (
ogrtest.check_feature_geometry(
ogr.CreateGeometryFromJson(json.dumps(j_got["features"][1]["geometry"])),
ogr.CreateGeometryFromJson(
json.dumps(j_expected["features"][1]["geometry"])
),
)
== 0
)
# Polar case: slice of spherical cap (not intersecting antimeridian, west hemisphere)
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0)
sr = osr.SpatialReference()
sr.SetFromUserInput(
"+proj=stere +lat_0=90 +lat_ts=71 +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs"
)
lyr = src_ds.CreateLayer("test", srs=sr)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"POLYGON((-2000000 2000000,0 0,-2000000 -2000000,-2000000 2000000))"
)
)
lyr.CreateFeature(f)
gdal.VectorTranslate(
"/vsimem/out.json",
src_ds,
format="GeoJSON",
layerCreationOptions=["WRITE_NAME=NO", "RFC7946=YES", "WRITE_BBOX=YES"],
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"bbox": [ -135.0000000, 64.3861643, -45.0000000, 90.0000000 ],
"features": [
{ "type": "Feature", "properties": { }, "bbox": [ -135.0, 64.3861643, -45.0, 90.0 ], "geometry": { "type": "Polygon", "coordinates": [ [ [ -135.0, 64.3861643 ], [ -45.0, 64.3861643 ], [ -45.0, 90.0 ], [ -135.0, 90.0 ], [ -135.0, 64.3861643 ] ] ] } }
]
}
"""
assert json.loads(got) == json.loads(expected)
# Polar case: slice of spherical cap (not intersecting antimeridian, east hemisphere)
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0)
sr = osr.SpatialReference()
sr.SetFromUserInput(
"+proj=stere +lat_0=90 +lat_ts=71 +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs"
)
lyr = src_ds.CreateLayer("test", srs=sr)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"MULTIPOLYGON(((2000000 2000000,0 0,2000000 -2000000,2000000 2000000)))"
)
)
lyr.CreateFeature(f)
gdal.VectorTranslate(
"/vsimem/out.json",
src_ds,
format="GeoJSON",
layerCreationOptions=["WRITE_NAME=NO", "RFC7946=YES", "WRITE_BBOX=YES"],
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"bbox": [ 45.0000000, 64.3861643, 135.0000000, 90.0000000 ],
"features": [
{ "type": "Feature", "properties": { }, "bbox": [ 45.0, 64.3861643, 135.0, 90.0 ], "geometry": { "type": "Polygon", "coordinates": [ [ [ 135.0, 64.3861643 ], [ 135.0, 90.0 ], [ 45.0, 90.0 ], [ 45.0, 64.3861643 ], [ 135.0, 64.3861643 ] ] ] } }
]
}
"""
assert json.loads(got) == json.loads(expected)
# Polar case: slice of spherical cap crossing the antimeridian
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0)
sr = osr.SpatialReference()
sr.SetFromUserInput(
"+proj=stere +lat_0=90 +lat_ts=71 +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs"
)
lyr = src_ds.CreateLayer("test", srs=sr)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"POLYGON((100000 100000,-100000 100000,0 0,100000 100000))"
)
)
lyr.CreateFeature(f)
gdal.VectorTranslate(
"/vsimem/out.json",
src_ds,
format="GeoJSON",
layerCreationOptions=["WRITE_NAME=NO", "RFC7946=YES", "WRITE_BBOX=YES"],
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"bbox": [ 135.0000000, 88.6984598, -135.0000000, 90.0000000 ],
"features": [
{ "type": "Feature", "properties": { }, "bbox": [ 135.0, 88.6984598, -135.0, 90.0 ], "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 180.0, 89.0796531 ], [ 180.0, 90.0 ], [ 135.0, 88.6984598 ], [ 180.0, 89.0796531 ] ] ], [ [ [ -180.0, 90.0 ], [ -180.0, 89.0796531 ], [ -135.0, 88.6984598 ] ] ] ] } }
]
}
"""
expected_geos_overlay_ng = """{
"type": "FeatureCollection",
"bbox": [ 135.0000000, 88.6984598, -135.0000000, 90.0000000 ],
"features": [
{ "type": "Feature", "properties": { }, "bbox": [ 135.0, 88.6984598, -135.0, 90.0 ], "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -135.0, 88.6984598 ], [ -180.0, 90.0 ], [ -180.0, 89.0796531 ], [ -135.0, 88.6984598 ] ] ], [ [ [ 180.0, 90.0 ], [ 135.0, 88.6984598 ], [ 180.0, 89.0796531 ], [ 180.0, 90.0 ] ] ] ] } }
]
}"""
expected_geos_3_9_1 = """{
"type": "FeatureCollection",
"bbox": [ 135.0000000, 88.6984598, -135.0000000, 90.0000000 ],
"features": [
{ "type": "Feature", "properties": { }, "bbox": [ 135.0, 88.6984598, -135.0, 90.0 ], "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 135.0, 88.6984598 ], [ 180.0, 89.0796531 ], [ 180.0, 90.0 ], [ 135.0, 88.6984598 ] ] ], [ [ [ -135.0, 88.6984598 ], [ -180.0, 90.0 ], [ -180.0, 89.0796531 ], [ -135.0, 88.6984598 ] ] ] ] } }
]
}"""
assert (
json.loads(got) == json.loads(expected)
or json.loads(got) == json.loads(expected_geos_overlay_ng)
or json.loads(got) == json.loads(expected_geos_3_9_1)
), got
# Polar case: EPSG:3031: WGS 84 / Antarctic Polar Stereographic
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0)
sr = osr.SpatialReference()
sr.SetFromUserInput(
"+proj=stere +lat_0=-90 +lat_ts=-71 +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs"
)
lyr = src_ds.CreateLayer("test", srs=sr)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"MULTIPOLYGON(((2000000 2000000,2000000 -2000000,-2000000 -2000000,-2000000 2000000,2000000 2000000)))"
)
)
lyr.CreateFeature(f)
gdal.VectorTranslate(
"/vsimem/out.json",
src_ds,
format="GeoJSON",
layerCreationOptions=["WRITE_NAME=NO", "RFC7946=YES", "WRITE_BBOX=YES"],
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"bbox": [ -180.0000000, -90.0000000, 180.0000000, -64.3861643 ],
"features": [
{ "type": "Feature", "properties": { }, "bbox": [ -180.0, -90.0, 180.0, -64.3861643 ], "geometry": { "type": "Polygon", "coordinates": [ [ [ 45.0, -64.3861643 ], [ -45.0, -64.3861643 ], [ -135.0, -64.3861643 ], [ -180.0, -71.7425119 ], [ -180.0, -90.0 ], [ 180.0, -90.0 ], [ 180.0, -71.7425119 ], [ 135.0, -64.3861643 ], [ 45.0, -64.3861643 ] ] ] } }
]
}
"""
j_got = json.loads(got)
j_expected = json.loads(expected)
assert j_got["bbox"] == j_expected["bbox"]
assert len(j_expected["features"]) == 1
assert (
ogrtest.check_feature_geometry(
ogr.CreateGeometryFromJson(json.dumps(j_got["features"][0]["geometry"])),
ogr.CreateGeometryFromJson(
json.dumps(j_expected["features"][0]["geometry"])
),
)
== 0
)
# Antimeridian case: EPSG:32660: WGS 84 / UTM zone 60N with polygon and line crossing
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0)
sr = osr.SpatialReference()
sr.SetFromUserInput("+proj=utm +zone=60 +datum=WGS84 +units=m +no_defs")
lyr = src_ds.CreateLayer("test", srs=sr)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"POLYGON((670000 4000000,850000 4000000,850000 4100000,670000 4100000,670000 4000000))"
)
)
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(
ogr.CreateGeometryFromWkt("MULTILINESTRING((670000 4000000,850000 4100000))")
)
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("LINESTRING(670000 0,850000 0)"))
lyr.CreateFeature(f)
gdal.VectorTranslate(
"/vsimem/out.json",
src_ds,
format="GeoJSON",
layerCreationOptions=["WRITE_NAME=NO", "RFC7946=YES", "WRITE_BBOX=YES"],
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"bbox": [ 178.5275649, 0.0000000, -179.0681936, 37.0308258 ],
"features": [
{ "type": "Feature", "properties": { }, "bbox": [ 178.8892102, 36.0816324, -179.0681936, 37.0308258 ], "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 180.0, 36.1071354 ], [ 180.0, 36.1071354 ], [ 180.0, 37.0082839 ], [ 180.0, 37.0082839 ], [ 178.9112998, 37.0308258 ], [ 178.8892102, 36.1298163 ], [ 180.0, 36.1071354 ] ] ], [ [ [ -180.0, 37.0082839 ], [ -180.0, 36.1071354 ], [ -180.0, 36.1071354 ], [ -179.1135277, 36.0816324 ], [ -179.0681936, 36.9810434 ], [ -180.0, 37.0082839 ] ] ] ] } },
{ "type": "Feature", "properties": { }, "bbox": [ 178.8892102, 36.1298163, -179.0681936, 36.9810434 ], "geometry": { "type": "MultiLineString", "coordinates": [ [ [ 178.8892102, 36.1298163 ], [ 180.0, 36.5995612 ] ], [ [ -180.0, 36.5995612 ], [ -179.0681936, 36.9810434 ] ] ] } },
{ "type": "Feature", "properties": { }, "bbox": [ 178.5275649, 0.0, -179.8562277, 0.0 ], "geometry": { "type": "MultiLineString", "coordinates": [ [ [ 178.5275649, 0.0 ], [ 180.0, 0.0 ] ], [ [ -180.0, 0.0 ], [ -179.8562277, 0.0 ] ] ] } }
]
}
"""
j_got = json.loads(got)
j_expected = json.loads(expected)
assert j_got["bbox"] == j_expected["bbox"]
assert len(j_expected["features"]) == 3
assert (
ogrtest.check_feature_geometry(
ogr.CreateGeometryFromJson(json.dumps(j_got["features"][0]["geometry"])),
ogr.CreateGeometryFromJson(
json.dumps(j_expected["features"][0]["geometry"])
),
)
== 0
)
assert (
ogrtest.check_feature_geometry(
ogr.CreateGeometryFromJson(json.dumps(j_got["features"][1]["geometry"])),
ogr.CreateGeometryFromJson(
json.dumps(j_expected["features"][1]["geometry"])
),
)
== 0
)
assert (
ogrtest.check_feature_geometry(
ogr.CreateGeometryFromJson(json.dumps(j_got["features"][2]["geometry"])),
ogr.CreateGeometryFromJson(
json.dumps(j_expected["features"][2]["geometry"])
),
)
== 0
)
# Antimeridian case: EPSG:32660: WGS 84 / UTM zone 60N with polygon on west of antimeridian
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0)
sr = osr.SpatialReference()
sr.SetFromUserInput("+proj=utm +zone=60 +datum=WGS84 +units=m +no_defs")
lyr = src_ds.CreateLayer("test", srs=sr)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"POLYGON((670000 4000000,700000 4000000,700000 4100000,670000 4100000,670000 4000000))"
)
)
lyr.CreateFeature(f)
gdal.VectorTranslate(
"/vsimem/out.json",
src_ds,
format="GeoJSON",
layerCreationOptions=["WRITE_NAME=NO", "RFC7946=YES", "WRITE_BBOX=YES"],
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
expected = """{
"type": "FeatureCollection",
"bbox": [ 178.8892102, 36.1240958, 179.2483693, 37.0308258 ],
"features": [
{ "type": "Feature", "properties": { }, "bbox": [ 178.8892102, 36.1240958, 179.2483693, 37.0308258 ], "geometry": { "type": "Polygon", "coordinates": [ [ [ 178.8892102, 36.1298163 ], [ 179.2223914, 36.1240958 ], [ 179.2483693, 37.0249155 ], [ 178.9112998, 37.0308258 ], [ 178.8892102, 36.1298163 ] ] ] } }
]
}
"""
assert json.loads(got) == json.loads(expected)
###############################################################################
# Test using the name member of FeatureCollection
def test_ogr_geojson_58():
ds = ogr.Open(
'{ "type": "FeatureCollection", "name": "layer_name", "features": []}'
)
assert ds is not None, "Failed to open datasource"
lyr = ds.GetLayerByName("layer_name")
assert lyr is not None, "Missing layer called layer_name"
ds = None
ds = ogr.GetDriverByName("GeoJSON").CreateDataSource("/vsimem/ogr_geojson_58.json")
lyr = ds.CreateLayer("foo")
ds = None
ds = ogr.Open("/vsimem/ogr_geojson_58.json")
assert ds.GetLayerByName("foo") is not None, "Missing layer called foo"
ds = None
gdal.Unlink("/vsimem/ogr_geojson_58.json")
###############################################################################
# Test using the description member of FeatureCollection
def test_ogr_geojson_59():
ds = ogr.Open(
'{ "type": "FeatureCollection", "description": "my_description", "features": []}'
)
assert ds is not None, "Failed to open datasource"
lyr = ds.GetLayer(0)
assert (
lyr.GetMetadataItem("DESCRIPTION") == "my_description"
), "Did not get DESCRIPTION"
ds = None
ds = ogr.GetDriverByName("GeoJSON").CreateDataSource("/vsimem/ogr_geojson_59.json")
lyr = ds.CreateLayer("foo", options=["DESCRIPTION=my desc"])
ds = None
ds = ogr.Open("/vsimem/ogr_geojson_59.json")
lyr = ds.GetLayerByName("foo")
assert lyr.GetMetadataItem("DESCRIPTION") == "my desc", "Did not get DESCRIPTION"
ds = None
gdal.Unlink("/vsimem/ogr_geojson_59.json")
###############################################################################
# Test null vs unset field
def test_ogr_geojson_60():
ds = gdal.OpenEx(
"""{ "type": "FeatureCollection", "features": [
{ "type": "Feature", "properties" : { "foo" : "bar" } },
{ "type": "Feature", "properties" : { "foo": null } },
{ "type": "Feature", "properties" : { } } ] }"""
)
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if f["foo"] != "bar":
f.DumpReadable()
pytest.fail()
f = lyr.GetNextFeature()
if not f.IsFieldNull("foo"):
f.DumpReadable()
pytest.fail()
f = lyr.GetNextFeature()
if f.IsFieldSet("foo"):
f.DumpReadable()
pytest.fail()
# Test writing side
gdal.VectorTranslate("/vsimem/ogr_geojson_60.json", ds, format="GeoJSON")
fp = gdal.VSIFOpenL("/vsimem/ogr_geojson_60.json", "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
gdal.Unlink("/vsimem/ogr_geojson_60.json")
assert (
'"properties": { "foo": "bar" }' in data
and '"properties": { "foo": null }' in data
and '"properties": { }' in data
)
###############################################################################
# Test corner cases
def test_ogr_geojson_61():
# Invalid JSon
gdal.FileFromMemBuffer(
"/vsimem/ogr_geojson_61.json",
"""{ "type": "FeatureCollection", "features": [""",
)
with pytest.raises(Exception):
ds = gdal.OpenEx("/vsimem/ogr_geojson_61.json")
gdal.Unlink("/vsimem/ogr_geojson_61.json")
# Invalid single geometry
with pytest.raises(Exception):
ds = gdal.OpenEx("""{ "type": "Point", "x" : { "coordinates" : null } } """)
# Empty property name
gdal.FileFromMemBuffer(
"/vsimem/ogr_geojson_61.json",
"""{ "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": {"": 1}, "geometry": null }] }""",
)
ds = gdal.OpenEx("/vsimem/ogr_geojson_61.json")
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
assert f.GetField("") == 1
ds = None
gdal.Unlink("/vsimem/ogr_geojson_61.json")
###############################################################################
# Test crs object
def test_ogr_geojson_62():
# crs type=name tests
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"name" }, "features":[] }"""
)
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"name", "properties":null }, "features":[] }"""
)
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"name", "properties":1 }, "features":[] }"""
)
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"name", "properties":{"name":null} }, "features":[] }"""
)
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"name", "properties":{"name":1} }, "features":[] }"""
)
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"name", "properties":{"name":"x"} }, "features":[] }"""
)
ds = gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"name", "properties":{"name": "urn:ogc:def:crs:EPSG::32631"} }, "features":[] }"""
)
lyr = ds.GetLayer(0)
srs = lyr.GetSpatialRef()
assert srs.GetAuthorityCode(None) == "32631"
assert srs.GetDataAxisToSRSAxisMapping() == [1, 2]
# See https://github.com/OSGeo/gdal/issues/2035
ds = gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"name", "properties":{"name": "urn:ogc:def:crs:OGC:1.3:CRS84"} }, "features":[] }"""
)
lyr = ds.GetLayer(0)
srs = lyr.GetSpatialRef()
assert srs.GetAuthorityCode(None) == "4326"
assert srs.GetDataAxisToSRSAxisMapping() == [2, 1]
# crs type=EPSG (not even documented in GJ2008 spec!) tests. Just for coverage completeness
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"EPSG" }, "features":[] }"""
)
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"EPSG", "properties":null }, "features":[] }"""
)
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"EPSG", "properties":1 }, "features":[] }"""
)
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"EPSG", "properties":{"code":null} }, "features":[] }"""
)
with gdaltest.error_handler():
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"EPSG", "properties":{"code":1} }, "features":[] }"""
)
with gdaltest.error_handler():
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"EPSG", "properties":{"code":"x"} }, "features":[] }"""
)
ds = gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"EPSG", "properties":{"code": 32631} }, "features":[] }"""
)
lyr = ds.GetLayer(0)
srs = lyr.GetSpatialRef()
assert srs.ExportToWkt().find("32631") >= 0
# crs type=link tests
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"link" }, "features":[] }"""
)
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"link", "properties":null }, "features":[] }"""
)
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"link", "properties":1 }, "features":[] }"""
)
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"link", "properties":{"href":null} }, "features":[] }"""
)
with gdaltest.error_handler():
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"link", "properties":{"href":1} }, "features":[] }"""
)
with gdaltest.error_handler():
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"link", "properties":{"href": "1"} }, "features":[] }"""
)
# crs type=OGC (not even documented in GJ2008 spec!) tests. Just for coverage completeness
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"OGC" }, "features":[] }"""
)
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"OGC", "properties":null }, "features":[] }"""
)
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"OGC", "properties":1 }, "features":[] }"""
)
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"OGC", "properties":{"urn":null} }, "features":[] }"""
)
with gdaltest.error_handler():
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"OGC", "properties":{"urn":1} }, "features":[] }"""
)
with gdaltest.error_handler():
gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"OGC", "properties":{"urn":"x"} }, "features":[] }"""
)
ds = gdal.OpenEx(
"""{ "type": "FeatureCollection", "crs": { "type":"OGC", "properties":{"urn": "urn:ogc:def:crs:EPSG::32631"} }, "features":[] }"""
)
lyr = ds.GetLayer(0)
srs = lyr.GetSpatialRef()
assert srs.ExportToWkt().find("32631") >= 0
###############################################################################
# Extensive test of field type promotion
def test_ogr_geojson_63():
ds_ref = ogr.Open("data/geojson/test_type_promotion_ref.json")
lyr_ref = ds_ref.GetLayer(0)
ds = ogr.Open("data/geojson/test_type_promotion.json")
lyr = ds.GetLayer(0)
return ogrtest.compare_layers(lyr, lyr_ref)
###############################################################################
# Test exporting XYM / XYZM (#6935)
def test_ogr_geojson_64():
g = ogr.CreateGeometryFromWkt("POINT ZM(1 2 3 4)")
assert (
ogrtest.check_feature_geometry(
ogr.CreateGeometryFromJson(g.ExportToJson()),
ogr.CreateGeometryFromWkt("POINT Z(1 2 3)"),
)
== 0
)
g = ogr.CreateGeometryFromWkt("POINT M(1 2 3)")
assert (
ogrtest.check_feature_geometry(
ogr.CreateGeometryFromJson(g.ExportToJson()),
ogr.CreateGeometryFromWkt("POINT (1 2)"),
)
== 0
)
g = ogr.CreateGeometryFromWkt("LINESTRING ZM(1 2 3 4,5 6 7 8)")
assert (
ogrtest.check_feature_geometry(
ogr.CreateGeometryFromJson(g.ExportToJson()),
ogr.CreateGeometryFromWkt("LINESTRING Z(1 2 3,5 6 7)"),
)
== 0
)
g = ogr.CreateGeometryFromWkt("LINESTRING M(1 2 3,4 5 6)")
assert (
ogrtest.check_feature_geometry(
ogr.CreateGeometryFromJson(g.ExportToJson()),
ogr.CreateGeometryFromWkt("LINESTRING (1 2,4 5)"),
)
== 0
)
###############################################################################
# Test feature geometry CRS when CRS set on the FeatureCollection
# See https://github.com/r-spatial/sf/issues/449#issuecomment-319369945
def test_ogr_geojson_65():
ds = ogr.Open(
"""{
"type": "FeatureCollection",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32631" } },
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [500000,4500000]},
"properties": {
}}]}"""
)
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
srs = f.GetGeometryRef().GetSpatialReference()
pcs = int(srs.GetAuthorityCode("PROJCS"))
assert pcs == 32631, "Spatial reference for individual geometry was not valid"
###############################################################################
# Test features with properties not being a dictionary
def test_ogr_geojson_66():
ds = ogr.Open(
"""{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": null,
"properties": null
},
{
"type": "Feature",
"geometry": null,
"properties": []
}
]}"""
)
lyr = ds.GetLayer(0)
assert lyr.GetLayerDefn().GetFieldCount() == 0
###############################################################################
# Test reading GeoJSON files starting with {"features":[{"geometry":.... (#7198)
def test_ogr_geojson_67():
ds = ogr.Open("data/geojson/grenada.geojson")
assert ds is not None
assert ds.GetDriver().GetName() == "GeoJSON"
lyr = ds.GetLayer(0)
assert lyr.GetFeatureCount() == 1
###############################################################################
def test_ogr_geojson_id_field_and_id_type():
gdal.VectorTranslate(
"/vsimem/out.json",
"data/poly.shp",
options="-f GeoJSON -lco ID_TYPE=String -preserve_fid -limit 1 -fid 2",
)
got = read_file("/vsimem/out.json")
assert (
'"id": "2", "properties": { "AREA": 261752.781, "EAS_ID": 171, "PRFEDEA": "35043414" }'
in got
)
gdal.VectorTranslate(
"/vsimem/out.json",
"data/poly.shp",
options="-f GeoJSON -lco ID_TYPE=Integer -preserve_fid -limit 1 -fid 2",
)
got = read_file("/vsimem/out.json")
assert (
'"id": 2, "properties": { "AREA": 261752.781, "EAS_ID": 171, "PRFEDEA": "35043414" }'
in got
)
gdal.VectorTranslate(
"/vsimem/out.json",
"data/poly.shp",
format="GeoJSON",
layerCreationOptions=["ID_FIELD=EAS_ID"],
limit=1,
)
got = read_file("/vsimem/out.json")
assert (
'"id": 168, "properties": { "AREA": 215229.266, "PRFEDEA": "35043411" }' in got
)
src_ds = gdal.OpenEx("/vsimem/out.json", open_options=["NATIVE_DATA=YES"])
gdal.VectorTranslate("/vsimem/out2.json", src_ds, format="GeoJSON")
src_ds = None
got = read_file("/vsimem/out2.json")
gdal.Unlink("/vsimem/out2.json")
assert (
'"id": 168, "properties": { "AREA": 215229.266, "PRFEDEA": "35043411" }' in got
)
src_ds = gdal.OpenEx("/vsimem/out.json", open_options=["NATIVE_DATA=YES"])
gdal.VectorTranslate(
"/vsimem/out2.json",
src_ds,
format="GeoJSON",
layerCreationOptions=["ID_TYPE=String"],
)
src_ds = None
got = read_file("/vsimem/out2.json")
gdal.Unlink("/vsimem/out2.json")
assert (
'"id": "168", "properties": { "AREA": 215229.266, "PRFEDEA": "35043411" }'
in got
)
src_ds = gdal.OpenEx("/vsimem/out.json", open_options=["NATIVE_DATA=YES"])
gdal.VectorTranslate(
"/vsimem/out2.json",
src_ds,
format="GeoJSON",
layerCreationOptions=["ID_TYPE=Integer"],
)
src_ds = None
got = read_file("/vsimem/out2.json")
gdal.Unlink("/vsimem/out2.json")
assert (
'"id": 168, "properties": { "AREA": 215229.266, "PRFEDEA": "35043411" }' in got
)
gdal.Unlink("/vsimem/out.json")
gdal.VectorTranslate(
"/vsimem/out.json",
"data/poly.shp",
format="GeoJSON",
layerCreationOptions=["ID_FIELD=EAS_ID", "ID_TYPE=String"],
limit=1,
)
got = read_file("/vsimem/out.json")
assert (
'"id": "168", "properties": { "AREA": 215229.266, "PRFEDEA": "35043411" }'
in got
)
src_ds = gdal.OpenEx("/vsimem/out.json", open_options=["NATIVE_DATA=YES"])
gdal.VectorTranslate("/vsimem/out2.json", src_ds, format="GeoJSON")
src_ds = None
got = read_file("/vsimem/out2.json")
gdal.Unlink("/vsimem/out2.json")
assert (
'"id": "168", "properties": { "AREA": 215229.266, "PRFEDEA": "35043411" }'
in got
)
src_ds = gdal.OpenEx("/vsimem/out.json", open_options=["NATIVE_DATA=YES"])
gdal.VectorTranslate(
"/vsimem/out2.json",
src_ds,
format="GeoJSON",
layerCreationOptions=["ID_TYPE=String"],
)
src_ds = None
got = read_file("/vsimem/out2.json")
gdal.Unlink("/vsimem/out2.json")
assert (
'"id": "168", "properties": { "AREA": 215229.266, "PRFEDEA": "35043411" }'
in got
)
src_ds = gdal.OpenEx("/vsimem/out.json", open_options=["NATIVE_DATA=YES"])
gdal.VectorTranslate(
"/vsimem/out2.json",
src_ds,
format="GeoJSON",
layerCreationOptions=["ID_TYPE=Integer"],
)
src_ds = None
got = read_file("/vsimem/out2.json")
gdal.Unlink("/vsimem/out2.json")
assert (
'"id": 168, "properties": { "AREA": 215229.266, "PRFEDEA": "35043411" }' in got
)
gdal.Unlink("/vsimem/out.json")
gdal.VectorTranslate(
"/vsimem/out.json",
"data/poly.shp",
format="GeoJSON",
layerCreationOptions=["ID_FIELD=PRFEDEA"],
limit=1,
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
assert (
'"id": "35043411", "properties": { "AREA": 215229.266, "EAS_ID": 168 }' in got
)
gdal.VectorTranslate(
"/vsimem/out.json",
"data/poly.shp",
format="GeoJSON",
layerCreationOptions=["ID_FIELD=PRFEDEA", "ID_TYPE=Integer"],
limit=1,
)
got = read_file("/vsimem/out.json")
gdal.Unlink("/vsimem/out.json")
assert '"id": 35043411, "properties": { "AREA": 215229.266, "EAS_ID": 168 }' in got
gdal.VectorTranslate(
"/vsimem/out.json",
"data/poly.shp",
format="GeoJSON",
layerCreationOptions=["ID_GENERATE=YES"],
limit=1,
)
got = read_file("/vsimem/out.json")
assert (
'"id": 0, "properties": { "AREA": 215229.266, "EAS_ID": 168, "PRFEDEA": "35043411" }'
in got
)
gdal.VectorTranslate(
"/vsimem/out.json",
"data/poly.shp",
format="GeoJSON",
layerCreationOptions=["ID_GENERATE=YES", "ID_TYPE=Integer"],
limit=1,
)
got = read_file("/vsimem/out.json")
assert (
'"id": 0, "properties": { "AREA": 215229.266, "EAS_ID": 168, "PRFEDEA": "35043411" }'
in got
)
gdal.VectorTranslate(
"/vsimem/out.json",
"data/poly.shp",
format="GeoJSON",
layerCreationOptions=["ID_GENERATE=YES", "ID_TYPE=String"],
limit=1,
)
got = read_file("/vsimem/out.json")
assert (
'"id": "0", "properties": { "AREA": 215229.266, "EAS_ID": 168, "PRFEDEA": "35043411" }'
in got
)
###############################################################################
@gdaltest.disable_exceptions()
def test_ogr_geojson_geom_export_failure():
g = ogr.CreateGeometryFromWkt("POINT EMPTY")
geojson = g.ExportToJson()
assert geojson is None
g = ogr.CreateGeometryFromWkt("GEOMETRYCOLLECTION(TIN EMPTY)")
geojson = json.loads(g.ExportToJson())
assert geojson == {"type": "GeometryCollection", "geometries": None}
g = ogr.Geometry(ogr.wkbLineString)
g.AddPoint_2D(float("nan"), 0)
with gdaltest.error_handler():
geojson = g.ExportToJson()
assert geojson is None
g = ogr.Geometry(ogr.wkbPolygon)
lr = ogr.Geometry(ogr.wkbLinearRing)
lr.AddPoint_2D(0, 0)
lr.AddPoint_2D(0, 1)
lr.AddPoint_2D(1, 1)
lr.AddPoint_2D(0, 0)
g.AddGeometry(lr)
lr = ogr.Geometry(ogr.wkbLinearRing)
lr.AddPoint_2D(0, 0)
lr.AddPoint_2D(float("nan"), 1)
lr.AddPoint_2D(1, 1)
lr.AddPoint_2D(0, 0)
g.AddGeometry(lr)
with gdaltest.error_handler():
geojson = g.ExportToJson()
assert geojson is None
###############################################################################
def test_ogr_geojson_starting_with_crs():
ds = ogr.Open(
"""{
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32631" } },
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [500000,4500000]},
"properties": {
}}]}"""
)
assert ds is not None
###############################################################################
# Test we properly flush the file in SyncToDisk() in append situations
def test_ogr_geojson_append_flush():
tmpfilename = "tmp/ogr_geojson_append_flush.json"
f = gdal.VSIFOpenL(tmpfilename, "wb")
content = """{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "properties": { "x": 1, "y": 2, "z": 3, "w": 4 }, "geometry": { "type": "Point", "coordinates": [ 0, 0 ] } } ] }"""
gdal.VSIFWriteL(content, 1, len(content), f)
gdal.VSIFCloseL(f)
ds = ogr.Open(tmpfilename, update=1)
lyr = ds.GetLayer(0)
f = ogr.Feature(lyr.GetLayerDefn())
f["x"] = 10
lyr.CreateFeature(f)
lyr.SyncToDisk()
ds2 = ogr.Open(tmpfilename, update=1)
lyr = ds2.GetLayer(0)
lyr.GetNextFeature()
f = lyr.GetNextFeature()
assert f is not None and f["x"] == 10
ds = None
ds2 = None
gdal.Unlink(tmpfilename)
###############################################################################
def test_ogr_geojson_empty_geometrycollection():
g = ogr.CreateGeometryFromJson('{"type": "GeometryCollection", "geometries": []}')
assert g.ExportToWkt() == "GEOMETRYCOLLECTION EMPTY"
###############################################################################
def test_ogr_geojson_read_fields_with_different_case():
ds = ogr.Open(
"""{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "id": "my_id", "geometry": null, "properties":
{ "ID": "MY_ID", "x": "foo", "X": "FOO"} }
]}"""
)
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if (
f.GetField(0) != "my_id"
or f.GetField(1) != "MY_ID"
or f.GetField(2) != "foo"
or f.GetField(3) != "FOO"
):
f.DumpReadable()
pytest.fail()
###############################################################################
# Test bugfix for https://github.com/OSGeo/gdal/issues/1068
@pytest.mark.require_geos
def test_ogr_geojson_clip_geometries_rfc7946():
tmpfilename = "/vsimem/out.json"
gdal.VectorTranslate(
tmpfilename,
"""{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "geometry": {"type":"Polygon","coordinates":[[[-220,-20],[-220,30],[16,30],[16,-20],[-220,-20]]]} },
{ "type": "Feature", "geometry": {"type":"Polygon","coordinates":[[[220,40],[220,70],[-16,70],[-16,40],[220,40]]]} },
{ "type": "Feature", "geometry": {"type":"Polygon","coordinates":[[[170,-40],[170,-70],[-16,70],[-16,-40],[170,-40]]]} }
]
}""",
options="-f GeoJSON -lco RFC7946=YES",
)
ds = ogr.Open(tmpfilename)
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
ref_geom = ogr.CreateGeometryFromWkt(
"MULTIPOLYGON (((-180 30,-180 -20,16 -20,16 30,-180 30)),((140 -20,180 -20,180 30,140 30,140 -20)))"
)
if ogrtest.check_feature_geometry(f, ref_geom) != 0:
f.DumpReadable()
pytest.fail()
f = lyr.GetNextFeature()
ref_geom = ogr.CreateGeometryFromWkt(
"MULTIPOLYGON (((180 40,180 70,-16 70,-16 40,180 40)),((-180 70,-180 40,-140 40,-140 70,-180 70)))"
)
if ogrtest.check_feature_geometry(f, ref_geom) != 0:
f.DumpReadable()
pytest.fail()
f = lyr.GetNextFeature()
ref_geom = ogr.CreateGeometryFromWkt(
"POLYGON ((170 -40,-16 -40,-16 70,170 -70,170 -40))"
)
if ogrtest.check_feature_geometry(f, ref_geom) != 0:
f.DumpReadable()
pytest.fail()
ds = None
gdal.Unlink(tmpfilename)
###############################################################################
# Test bugfix for https://github.com/OSGeo/gdal/issues/1109
def test_ogr_geojson_non_finite():
json_content = """{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "properties": { "inf_prop": infinity, "minus_inf_prop": -infinity, "nan_prop": nan }, "geometry": null }
]
}"""
with gdaltest.error_handler():
ds = ogr.Open(json_content)
if ds is None:
# Might fail with older libjson-c versions
pytest.skip()
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
for i in range(3):
assert lyr.GetLayerDefn().GetFieldDefn(i).GetType() == ogr.OFTReal
if f["inf_prop"] != float("inf"):
f.DumpReadable()
pytest.fail()
if f["minus_inf_prop"] != float("-inf"):
f.DumpReadable()
pytest.fail()
if not math.isnan(f["nan_prop"]):
f.DumpReadable()
pytest.fail(str(f["nan_prop"]))
ds = None
tmpfilename = "/vsimem/out.json"
with gdaltest.error_handler():
gdal.VectorTranslate(tmpfilename, json_content, options="-f GeoJSON")
ds = ogr.Open(tmpfilename)
lyr = ds.GetLayer(0)
assert lyr.GetLayerDefn().GetFieldCount() == 0
ds = None
gdal.VectorTranslate(
tmpfilename, json_content, options="-f GeoJSON -lco WRITE_NON_FINITE_VALUES=YES"
)
ds = ogr.Open(tmpfilename)
lyr = ds.GetLayer(0)
assert lyr.GetLayerDefn().GetFieldCount() == 3
f = lyr.GetNextFeature()
if f["inf_prop"] != float("inf"):
f.DumpReadable()
pytest.fail()
if f["minus_inf_prop"] != float("-inf"):
f.DumpReadable()
pytest.fail()
if not math.isnan(f["nan_prop"]):
f.DumpReadable()
pytest.fail(str(f["nan_prop"]))
ds = None
gdal.Unlink(tmpfilename)
###############################################################################
def test_ogr_geojson_random_reading_with_id():
json_content = """{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "id": 1, "properties": { "a": "a" }, "geometry": null },
{ "type": "Feature", "id": 2, "properties": { "a": "bc" }, "geometry": null }
]
}"""
tmpfilename = "/vsimem/temp.json"
gdal.FileFromMemBuffer(tmpfilename, json_content)
ds = ogr.Open(tmpfilename)
lyr = ds.GetLayer(0)
f1_ref = lyr.GetNextFeature()
f2_ref = lyr.GetNextFeature()
f1 = lyr.GetFeature(1)
f2 = lyr.GetFeature(2)
assert f1.Equal(f1_ref)
assert f2.Equal(f2_ref)
assert not lyr.GetFeature(3)
ds = None
gdal.Unlink(tmpfilename)
###############################################################################
def test_ogr_geojson_random_reading_without_id():
json_content = """{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "properties": { "a": "a" }, "geometry": null },
{ "type": "Feature", "properties": { "a": "bc" }, "geometry": null }
]
}"""
tmpfilename = "/vsimem/temp.json"
gdal.FileFromMemBuffer(tmpfilename, json_content)
ds = ogr.Open(tmpfilename)
lyr = ds.GetLayer(0)
f1_ref = lyr.GetNextFeature()
f2_ref = lyr.GetNextFeature()
f1 = lyr.GetFeature(0)
f2 = lyr.GetFeature(1)
assert f1.Equal(f1_ref)
assert f2.Equal(f2_ref)
assert not lyr.GetFeature(2)
ds = None
gdal.Unlink(tmpfilename)
###############################################################################
def test_ogr_geojson_single_feature_random_reading_with_id():
json_content = """
{ "type": "Feature", "id": 1, "properties": { "a": "a" }, "geometry": null }
}"""
tmpfilename = "/vsimem/temp.json"
gdal.FileFromMemBuffer(tmpfilename, json_content)
ds = ogr.Open(tmpfilename)
lyr = ds.GetLayer(0)
f1_ref = lyr.GetNextFeature()
f1 = lyr.GetFeature(1)
assert f1.Equal(f1_ref)
ds = None
gdal.Unlink(tmpfilename)
###############################################################################
def test_ogr_geojson_3D_geom_type():
ds = ogr.Open(
"""{"type": "FeatureCollection", "features":[
{"type": "Feature", "geometry": {"type":"Point","coordinates":[1,2,3]}, "properties": null},
{"type": "Feature", "geometry": {"type":"Point","coordinates":[1,2,4]}, "properties": null}
]}"""
)
lyr = ds.GetLayer(0)
assert lyr.GetGeomType() == ogr.wkbPoint25D
ds = ogr.Open(
"""{"type": "FeatureCollection", "features":[
{"type": "Feature", "geometry": {"type":"Point","coordinates":[1,2,3]}, "properties": null},
{"type": "Feature", "geometry": {"type":"Point","coordinates":[1,2]}, "properties": null}
]}"""
)
lyr = ds.GetLayer(0)
assert lyr.GetGeomType() == ogr.wkbPoint25D
ds = ogr.Open(
"""{"type": "FeatureCollection", "features":[
{"type": "Feature", "geometry": {"type":"Point","coordinates":[1,2]}, "properties": null},
{"type": "Feature", "geometry": {"type":"Point","coordinates":[1,2,4]}, "properties": null}
]}"""
)
lyr = ds.GetLayer(0)
assert lyr.GetGeomType() == ogr.wkbPoint25D
###############################################################################
def test_ogr_geojson_update_in_loop():
tmpfilename = "/vsimem/temp.json"
# No explicit id
gdal.FileFromMemBuffer(
tmpfilename,
'{"type": "FeatureCollection", "name": "test", "features": [{ "type": "Feature", "properties": { "foo": 1 }, "geometry": null }, { "type": "Feature", "properties": { "foo": 2 }, "geometry": null }]}',
)
ds = gdal.OpenEx(tmpfilename, gdal.OF_VECTOR | gdal.GA_Update)
layer = ds.GetLayer()
fids = []
for feature in layer:
fids.append(feature.GetFID())
layer.SetFeature(feature)
assert fids == [0, 1]
ds = None
# Explicit id no holes
gdal.FileFromMemBuffer(
tmpfilename,
'{"type": "FeatureCollection", "name": "test", "features": [{ "type": "Feature", "id": 0, "properties": { "foo": 1 }, "geometry": null }, { "type": "Feature", "properties": { "foo": 2 }, "id": 1, "geometry": null }]}',
)
ds = gdal.OpenEx(tmpfilename, gdal.OF_VECTOR | gdal.GA_Update)
layer = ds.GetLayer()
fids = []
for feature in layer:
fids.append(feature.GetFID())
layer.SetFeature(feature)
assert fids == [0, 1]
ds = None
# Explicit id with holes
gdal.FileFromMemBuffer(
tmpfilename,
'{"type": "FeatureCollection", "name": "test", "features": [{ "type": "Feature", "id": 1, "properties": { "foo": 1 }, "geometry": null }, { "type": "Feature", "properties": { "foo": 2 }, "id": 3, "geometry": null }]}',
)
ds = gdal.OpenEx(tmpfilename, gdal.OF_VECTOR | gdal.GA_Update)
layer = ds.GetLayer()
fids = []
for feature in layer:
fids.append(feature.GetFID())
layer.SetFeature(feature)
assert fids == [1, 3]
ds = None
gdal.Unlink(tmpfilename)
###############################################################################
# Test fix for https://github.com/OSGeo/gdal/issues/2720
def test_ogr_geojson_starting_with_coordinates():
tmpfilename = "/vsimem/temp.json"
gdal.FileFromMemBuffer(
tmpfilename, '{ "coordinates": [' + (" " * 10000) + '2,49], "type": "Point"}'
)
ds = gdal.OpenEx(tmpfilename, gdal.OF_VECTOR)
assert ds is not None
gdal.Unlink(tmpfilename)
###############################################################################
# Test fix for https://github.com/OSGeo/gdal/issues/2787
def test_ogr_geojson_starting_with_geometry_coordinates():
tmpfilename = "/vsimem/temp.json"
gdal.FileFromMemBuffer(
tmpfilename,
'{ "geometry": {"coordinates": ['
+ (" " * 10000)
+ '2,49], "type": "Point"}, "type": "Feature", "properties": {} }',
)
ds = gdal.OpenEx(tmpfilename, gdal.OF_VECTOR)
assert ds is not None
gdal.Unlink(tmpfilename)
###############################################################################
# Test serialization of Float32 values
def test_ogr_geojson_write_float32():
def cast_as_float(x):
return struct.unpack("f", struct.pack("f", x))[0]
filename = "/vsimem/test_ogr_geojson_write_float32.json"
ds = ogr.GetDriverByName("GeoJSON").CreateDataSource(filename)
lyr = ds.CreateLayer("foo")
fldn_defn = ogr.FieldDefn("float32", ogr.OFTReal)
fldn_defn.SetSubType(ogr.OFSTFloat32)
lyr.CreateField(fldn_defn)
fldn_defn = ogr.FieldDefn("float32list", ogr.OFTRealList)
fldn_defn.SetSubType(ogr.OFSTFloat32)
lyr.CreateField(fldn_defn)
f = ogr.Feature(lyr.GetLayerDefn())
f["float32"] = cast_as_float(0.35)
f["float32list"] = [
cast_as_float(123.0),
cast_as_float(0.35),
cast_as_float(0.15),
cast_as_float(0.12345678),
cast_as_float(1.2345678e-15),
cast_as_float(1.2345678e15),
cast_as_float(0.123456789), # more decimals than Float32 can hold
]
lyr.CreateFeature(f)
ds = None
fp = gdal.VSIFOpenL(filename, "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
gdal.Unlink(filename)
data = data.replace("e+0", "e+").replace("e-0", "e-")
assert '"float32": 0.35,' in data
assert (
'"float32list": [ 123.0, 0.35, 0.15, 0.12345678, 1.2345678e-15, 1.2345678e+15, 0.12345679 ]'
in data
)
###############################################################################
# Test bugfix for #3172
def test_ogr_geojson_write_float_exponential_without_dot():
filename = "/vsimem/test_ogr_geojson_write_float_exponential_without_dot.json"
ds = ogr.GetDriverByName("GeoJSON").CreateDataSource(filename)
lyr = ds.CreateLayer("foo")
fldn_defn = ogr.FieldDefn("float32", ogr.OFTReal)
fldn_defn.SetSubType(ogr.OFSTFloat32)
lyr.CreateField(fldn_defn)
fldn_defn = ogr.FieldDefn("float64", ogr.OFTReal)
lyr.CreateField(fldn_defn)
f = ogr.Feature(lyr.GetLayerDefn())
f["float32"] = 1e-7
f["float64"] = 1e-8
lyr.CreateFeature(f)
ds = None
fp = gdal.VSIFOpenL(filename, "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
gdal.Unlink(filename)
# Check that the json can be parsed
json.loads(data)
###############################################################################
# Test bugfix for #3280
def test_ogr_geojson_feature_starting_with_big_properties():
filename = "/vsimem/test_ogr_geojson_feature_starting_with_big_properties.json"
gdal.FileFromMemBuffer(
filename,
'{"properties":{"foo":"%s"},"type":"Feature","geometry":null}' % ("x" * 10000),
)
assert ogr.Open(filename) is not None
gdal.Unlink(filename)
###############################################################################
def test_ogr_geojson_export_geometry_axis_order():
# EPSG:4326 and lat,long data order
sr = osr.SpatialReference()
sr.ImportFromEPSG(4326)
sr.SetAxisMappingStrategy(osr.OAMS_AUTHORITY_COMPLIANT)
g = ogr.CreateGeometryFromWkt("POINT (49 2)")
g.AssignSpatialReference(sr)
before_wkt = g.ExportToWkt()
assert json.loads(g.ExportToJson()) == {"type": "Point", "coordinates": [2.0, 49.0]}
assert g.ExportToWkt() == before_wkt
# EPSG:4326 and long,lat data order
sr = osr.SpatialReference()
sr.ImportFromEPSG(4326)
sr.SetAxisMappingStrategy(osr.OAMS_TRADITIONAL_GIS_ORDER)
g = ogr.CreateGeometryFromWkt("POINT (2 49)")
g.AssignSpatialReference(sr)
assert json.loads(g.ExportToJson()) == {"type": "Point", "coordinates": [2.0, 49.0]}
# CRS84 with long,lat CRS and data order
sr = osr.SpatialReference()
sr.SetFromUserInput("OGC:CRS84")
g = ogr.CreateGeometryFromWkt("POINT (2 49)")
g.AssignSpatialReference(sr)
assert json.loads(g.ExportToJson()) == {"type": "Point", "coordinates": [2.0, 49.0]}
# Projected CRS with easting, northing order
sr = osr.SpatialReference()
sr.ImportFromEPSG(32631)
g = ogr.CreateGeometryFromWkt("POINT (2 49)")
g.AssignSpatialReference(sr)
assert json.loads(g.ExportToJson()) == {"type": "Point", "coordinates": [2.0, 49.0]}
# Projected CRS with northing, easting order
sr = osr.SpatialReference()
sr.ImportFromEPSG(2393)
sr.SetAxisMappingStrategy(osr.OAMS_AUTHORITY_COMPLIANT)
g = ogr.CreateGeometryFromWkt("POINT (49 2)")
g.AssignSpatialReference(sr)
assert json.loads(g.ExportToJson()) == {"type": "Point", "coordinates": [2.0, 49.0]}
# No CRS
g = ogr.CreateGeometryFromWkt("POINT (2 49)")
assert json.loads(g.ExportToJson()) == {"type": "Point", "coordinates": [2.0, 49.0]}
###############################################################################
def test_ogr_geojson_sparse_fields():
ds = ogr.Open("data/geojson/sparse_fields.geojson")
lyr = ds.GetLayer(0)
lyr_defn = lyr.GetLayerDefn()
field_names = [
lyr_defn.GetFieldDefn(i).GetName() for i in range(lyr_defn.GetFieldCount())
]
assert field_names == ["C", "B", "A", "D", "E_prev", "E", "E_next", "F", "X"]
###############################################################################
@pytest.mark.parametrize("filename", ["point.geojson", "featurecollection_point.json"])
def test_ogr_geojson_crs_4326(filename):
ds = ogr.Open("data/geojson/" + filename)
lyr = ds.GetLayer(0)
srs = lyr.GetSpatialRef()
assert srs.GetAuthorityCode(None) == "4326"
assert srs.GetDataAxisToSRSAxisMapping() == [2, 1]
###############################################################################
@pytest.mark.parametrize("filename", ["pointz.json", "featurecollection_pointz.json"])
def test_ogr_geojson_crs_4979(filename):
ds = ogr.Open("data/geojson/" + filename)
lyr = ds.GetLayer(0)
srs = lyr.GetSpatialRef()
assert srs.GetAuthorityCode(None) == "4979"
assert srs.GetDataAxisToSRSAxisMapping() == [2, 1, 3]
###############################################################################
def test_ogr_geojson_write_rfc7946_from_3D_crs():
srs_4979 = osr.SpatialReference()
srs_4979.ImportFromEPSG(4979)
srs_4979.SetAxisMappingStrategy(osr.OAMS_TRADITIONAL_GIS_ORDER)
srs_4326_5773 = osr.SpatialReference()
srs_4326_5773.SetFromUserInput("EPSG:4326+5773")
srs_4326_5773.SetAxisMappingStrategy(osr.OAMS_TRADITIONAL_GIS_ORDER)
ct = osr.CoordinateTransformation(srs_4979, srs_4326_5773)
ellipsoidal_height = 100
lon, lat, z = ct.TransformPoint(2, 49, ellipsoidal_height)
# If we have the egm96 grid, then z should be different from 100
filename = "/vsimem/out.geojson"
ds = ogr.GetDriverByName("GeoJSON").CreateDataSource(filename)
lyr = ds.CreateLayer("out", srs=srs_4326_5773, options=["RFC7946=YES"])
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(%.18g %.18g %.18g)" % (lon, lat, z)))
lyr.CreateFeature(f)
ds = None
fp = gdal.VSIFOpenL(filename, "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
gdal.Unlink(filename)
# Check that we get back the ellipsoidal height
assert '"coordinates": [ 2.0, 49.0, 100.0' in data
###############################################################################
# Test effect of OGR_GEOJSON_MAX_OBJ_SIZE
@gdaltest.disable_exceptions()
def test_ogr_geojson_feature_large():
filename = "/vsimem/test_ogr_geojson_feature_large.json"
gdal.FileFromMemBuffer(
filename,
'{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[%s]}}]}'
% ",".join(["[0,0]" for _ in range(10 * 1024)]),
)
assert ogr.Open(filename) is not None
with gdaltest.config_option("OGR_GEOJSON_MAX_OBJ_SIZE", "0"):
assert ogr.Open(filename) is not None
with gdaltest.config_option("OGR_GEOJSON_MAX_OBJ_SIZE", "0.1"):
with gdaltest.error_handler():
assert ogr.Open(filename) is None
gdal.Unlink(filename)
###############################################################################
# Test reading http:// resource
@pytest.mark.require_curl()
def test_ogr_geojson_read_from_http():
import webserver
(webserver_process, webserver_port) = webserver.launch(
handler=webserver.DispatcherHttpHandler
)
if webserver_port == 0:
pytest.skip()
response = """{"type": "FeatureCollection", "features":[
{"type": "Feature", "geometry": {"type":"Point","coordinates":[1,2]}, "properties": null}]}"""
handler = webserver.SequentialHandler()
handler.add(
"GET",
"/foo",
200,
{},
response,
expected_headers={"Accept": "text/plain, application/json"},
)
try:
with webserver.install_http_handler(handler):
ds = ogr.Open("http://localhost:%d/foo" % webserver_port)
assert ds is not None
lyr = ds.GetLayer(0)
assert lyr.GetFeatureCount() == 1
finally:
webserver.server_stop(webserver_process, webserver_port)
###############################################################################
# Test ogr2ogr -nln with a input dataset being a GeoJSON file with a name
def test_ogr_geojson_ogr2ogr_nln_with_input_dataset_having_name():
filename = "/vsimem/test_ogr_geojson_feature_large.geojson"
gdal.VectorTranslate(
filename,
'{"type":"FeatureCollection","name":"to_be_overriden","features":[]}',
layerName="new_name",
)
ds = ogr.Open(filename)
assert ds.GetLayer(0).GetName() == "new_name"
ds = None
gdal.Unlink(filename)
###############################################################################
# Test reading a file with a id property with a mix of features where it is set
# and others none
@pytest.mark.parametrize("read_from_file", [True, False])
def test_ogr_geojson_ids_0_1_null_unset(read_from_file):
connection_name = "data/geojson/ids_0_1_null_unset.json"
if not read_from_file:
connection_name = open(connection_name, "rb").read().decode("ascii")
ds = ogr.Open(connection_name)
assert ds
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
assert f.GetFID() == 0
assert f["id"] == 0
assert f["seq"] == 0
f = lyr.GetNextFeature()
assert f.GetFID() == 1
assert f["id"] == 1
assert f["seq"] == 1
f = lyr.GetNextFeature()
assert f.GetFID() == 2
assert f["id"] is None
assert f["seq"] == 2
f = lyr.GetNextFeature()
assert f.GetFID() == 3
assert not f.IsFieldSet("id")
assert f["seq"] == 3
f = lyr.GetNextFeature()
assert f is None
for i in range(4):
f = lyr.GetFeature(i)
assert f.GetFID() == i
assert f["seq"] == i
###############################################################################
# Test reading a file with a id property with a mix of features where it is set
# and others none, and a conflicting id
@gdaltest.disable_exceptions()
@pytest.mark.parametrize("read_from_file", [True, False])
def test_ogr_geojson_ids_0_1_null_1_null(read_from_file):
connection_name = "data/geojson/ids_0_1_null_1_null.json"
if not read_from_file:
connection_name = open(connection_name, "rb").read().decode("ascii")
with gdaltest.error_handler():
gdal.ErrorReset()
ds = ogr.Open(connection_name)
assert ds
if not read_from_file:
assert gdal.GetLastErrorType() == gdal.CE_Warning
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
assert f.GetFID() == 0
assert f["id"] == 0
assert f["seq"] == 0
f = lyr.GetNextFeature()
assert f.GetFID() == 1
assert f["id"] == 1
assert f["seq"] == 1
f = lyr.GetNextFeature()
if read_from_file:
assert gdal.GetLastErrorType() == gdal.CE_Warning
assert f.GetFID() == 2
assert f["id"] is None
assert f["seq"] == 2
f = lyr.GetNextFeature()
assert f.GetFID() == 3
assert f["id"] == 1
assert f["seq"] == 3
f = lyr.GetNextFeature()
assert f.GetFID() == 4
assert f["id"] is None
assert f["seq"] == 4
f = lyr.GetNextFeature()
assert f is None
gdal.ErrorReset()
for i in range(5):
f = lyr.GetFeature(i)
assert f.GetFID() == i
assert f["seq"] == i
assert gdal.GetLastErrorType() == gdal.CE_None
###############################################################################
# Run test_ogrsf
def test_ogr_geojson_test_ogrsf():
import test_cli_utilities
if test_cli_utilities.get_test_ogrsf_path() is None:
pytest.skip()
ret = gdaltest.runexternal(
test_cli_utilities.get_test_ogrsf_path()
+ " -ro data/geojson/ids_0_1_null_1_null.json"
)
assert "INFO" in ret
assert "ERROR" not in ret
###############################################################################
# Test fix for https://github.com/OSGeo/gdal/issues/7313
@pytest.mark.parametrize(
"properties",
[
["a_string", 42.0, {"a_field": "a_value"}],
["a_string", 42, {"a_field": "a_value"}],
["a_string", {"a_field": "a_value"}, 42],
[42, "a_string", {"a_field": "a_value"}],
[42, {"a_field": "a_value"}, "a_string"],
[{"a_field": "a_value"}, 42, "a_string"],
[{"a_field": "a_value"}, "a_string", 42],
],
)
def test_ogr_geojson_mixed_type_promotion(properties):
tmpfilename = "/vsimem/temp.json"
jdata = {"type": "FeatureCollection", "features": []}
for prop_val in properties:
jdata["features"].append({"type": "Feature", "properties": {"prop0": prop_val}})
gdal.FileFromMemBuffer(
tmpfilename,
json.dumps(jdata),
)
ds = gdal.OpenEx(tmpfilename, gdal.OF_VECTOR)
assert ds is not None
lyr = ds.GetLayer(0)
lyr_def = lyr.GetLayerDefn()
fld_def = lyr_def.GetFieldDefn(0)
assert fld_def.GetTypeName() == "String"
assert fld_def.GetSubType() == ogr.OFSTJSON
gdal.Unlink(tmpfilename)
###############################################################################
# Test fix for https://github.com/OSGeo/gdal/issues/7319
def test_ogr_geojson_coordinate_precision():
filename = "/vsimem/test_ogr_geojson_coordinate_precision.json"
ds = ogr.GetDriverByName("GeoJSON").CreateDataSource(filename)
lyr = ds.CreateLayer("foo", options=["COORDINATE_PRECISION=1", "WRITE_BBOX=YES"])
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (1.23456789 2.3456789)"))
lyr.CreateFeature(f)
ds = None
fp = gdal.VSIFOpenL(filename, "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
gdal.Unlink(filename)
assert '"bbox": [ 1.2, 2.3, 1.2, 2.3 ]' in data
assert '"coordinates": [ 1.2, 2.3 ]' in data
assert "3456" not in data
###############################################################################
# Test fix for https://github.com/OSGeo/gdal/issues/7319
def test_ogr_geojson_field_types():
filename = "/vsimem/test_ogr_geojson_field_types.json"
test_data = """{"type":"FeatureCollection","name":"My Collection","features":[
{ "type": "Feature", "properties": { "prop0": 42 }, "geometry": { "type": "Point", "coordinates": [ 102.0, 0.5 ] } },
{ "type": "Feature", "properties": { "prop0": "42" }, "geometry": { "type": "Point", "coordinates": [ 102.0, 0.5 ] } },
{ "type": "Feature", "properties": { "prop0": "astring" }, "geometry": { "type": "Point", "coordinates": [ 102.0, 0.5 ] } },
{ "type": "Feature", "properties": { "prop0": { "nested": 75 } }, "geometry": { "type": "Point", "coordinates": [ 102.0, 0.5 ] } },
{ "type": "Feature", "properties": { "prop0": { "a": "b" } }, "geometry": { "type": "Point", "coordinates": [ 102.0, 0.5 ] } }
]}
"""
srcds = gdal.OpenEx(
test_data,
gdal.OF_VECTOR,
open_options=["NATIVE_DATA=TRUE"],
)
gdal.VectorTranslate(filename, srcds, options="-f GeoJSON -lco NATIVE_DATA=TRUE")
fp = gdal.VSIFOpenL(filename, "rb")
data = gdal.VSIFReadL(1, 10000, fp).decode("ascii")
gdal.VSIFCloseL(fp)
assert '{ "prop0": "42" }' in data
assert '{ "prop0": "astring" }' in data
assert '{ "prop0": { "nested": 75 } }' in data
assert '{ "prop0": 42 }' in data
assert '{ "prop0": { "a": "b" } }' in data
gdal.Unlink(filename)
###############################################################################
# Test opening with non C locale
def test_ogr_geojson_open_with_non_C_locale():
import locale
original_locale = locale.setlocale(locale.LC_ALL)
try:
locale.setlocale(locale.LC_ALL, "")
if locale.localeconv()["decimal_point"] == ".":
pytest.skip("cannot test, as decimal_point is dot")
test_ogr_geojson_2()
finally:
locale.setlocale(locale.LC_ALL, original_locale)