gdal/autotest/ogr/ogr_mvt.py

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

#!/usr/bin/env pytest
###############################################################################
# $Id$
#
# Project: GDAL/OGR Test Suite
# Purpose: Test OGR MVT driver functionality.
# Author: Even Rouault <even dot rouault at spatialys dot com>
#
###############################################################################
# Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
###############################################################################
import json
import gdaltest
import ogrtest
import pytest
import webserver
from osgeo import gdal, ogr, osr
pytestmark = pytest.mark.require_driver("MVT")
@pytest.fixture(scope="module", autouse=True)
def init():
with gdaltest.config_option("OGR_MVT_ENFORE_EXTERNAL_RING_IS_CLOCKWISE", "YES"):
yield
###############################################################################
@pytest.fixture
def has_make_valid():
# Check if MakeValid() is available
g = ogr.CreateGeometryFromWkt("POLYGON ((0 0,10 10,0 10,10 0,0 0))")
with gdaltest.error_handler(), gdaltest.disable_exceptions():
return g.MakeValid() is not None
###############################################################################
def test_ogr_mvt_datatypes():
# With metadata.json
ds = ogr.Open("data/mvt/datatypes/0/0/0.pbf")
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if (
f["bool_false"] != 0
or f["bool_true"] != 1
or f["pos_int_value"] != 1
or f["pos_int64_value"] != 123456789012345
or f["neg_int_value"] != -1
or f["neg_int64_value"] != -123456789012345
or f["pos_sint_value"] != 1
or f["pos_sint64_value"] != 123456789012345
or f["neg_sint_value"] != -1
or f["neg_sint64_value"] != -123456789012345
or f["uint_value"] != 2000000000
or f["uint64_value"] != 4000000000
or f["float_value"] != 1.25
or f["real_value"] != 1.23456789
or f["string_value"] != "str"
):
f.DumpReadable()
pytest.fail()
# Without metadata.json
ds = gdal.OpenEx("data/mvt/datatypes/0/0/0.pbf", open_options=["METADATA_FILE="])
lyr = ds.GetLayer(0)
count = lyr.GetLayerDefn().GetFieldCount()
assert count == 16
tab = []
for i in range(lyr.GetLayerDefn().GetFieldCount()):
fld_defn = lyr.GetLayerDefn().GetFieldDefn(i)
tab += [(fld_defn.GetName(), fld_defn.GetType(), fld_defn.GetSubType())]
expected_tab = [
("mvt_id", ogr.OFTInteger64, ogr.OFSTNone),
("bool_true", ogr.OFTInteger, ogr.OFSTBoolean),
("bool_false", ogr.OFTInteger, ogr.OFSTBoolean),
("pos_int_value", ogr.OFTInteger, ogr.OFSTNone),
("pos_int64_value", ogr.OFTInteger64, ogr.OFSTNone),
("neg_int_value", ogr.OFTInteger, ogr.OFSTNone),
("neg_int64_value", ogr.OFTInteger64, ogr.OFSTNone),
("pos_sint_value", ogr.OFTInteger, ogr.OFSTNone),
("pos_sint64_value", ogr.OFTInteger64, ogr.OFSTNone),
("neg_sint_value", ogr.OFTInteger, ogr.OFSTNone),
("neg_sint64_value", ogr.OFTInteger64, ogr.OFSTNone),
("uint_value", ogr.OFTInteger, ogr.OFSTNone),
("uint64_value", ogr.OFTInteger64, ogr.OFSTNone),
("float_value", ogr.OFTReal, ogr.OFSTFloat32),
("real_value", ogr.OFTReal, ogr.OFSTNone),
("string_value", ogr.OFTString, ogr.OFSTNone),
]
assert tab == expected_tab
f = lyr.GetNextFeature()
if (
f["bool_false"] != 0
or f["bool_true"] != 1
or f["pos_int_value"] != 1
or f["pos_int64_value"] != 123456789012345
or f["neg_int_value"] != -1
or f["neg_int64_value"] != -123456789012345
or f["pos_sint_value"] != 1
or f["pos_sint64_value"] != 123456789012345
or f["neg_sint_value"] != -1
or f["neg_sint64_value"] != -123456789012345
or f["uint_value"] != 2000000000
or f["uint64_value"] != 4000000000
or f["float_value"] != 1.25
or f["real_value"] != 1.23456789
or f["string_value"] != "str"
):
f.DumpReadable()
pytest.fail()
###############################################################################
def test_ogr_mvt_datatype_promotion():
ds = ogr.Open("data/mvt/datatype_promotion.pbf")
tab = [
("int_to_int64", ogr.OFTInteger64),
("int_to_real", ogr.OFTReal),
("int64_to_real", ogr.OFTReal),
("bool_to_int", ogr.OFTInteger),
("bool_to_str", ogr.OFTString),
("float_to_double", ogr.OFTReal),
]
for layer_name, dt in tab:
lyr = ds.GetLayerByName(layer_name)
fld_defn = lyr.GetLayerDefn().GetFieldDefn(1)
assert fld_defn.GetType() == dt, layer_name
assert fld_defn.GetSubType() == ogr.OFSTNone, layer_name
###############################################################################
def test_ogr_mvt_limit_cases():
with gdaltest.error_handler():
ds = ogr.Open("data/mvt/limit_cases.pbf")
lyr = ds.GetLayerByName("empty")
assert lyr.GetFeatureCount() == 0
lyr = ds.GetLayerByName("layer1")
assert lyr.GetFeatureCount() == 7
f = lyr.GetFeature(1)
assert f["mvt_id"] == 1
with pytest.raises(Exception):
f = lyr.GetFeature(6)
assert f["b"] == 1
lyr = ds.GetLayerByName("layer2")
assert lyr.GetFeatureCount() == 0
lyr = ds.GetLayerByName("layer3")
assert lyr.GetFeatureCount() == 0
lyr = ds.GetLayerByName("layer4")
assert lyr.GetFeatureCount() == 0
lyr = ds.GetLayerByName("layer5")
assert lyr.GetFeatureCount() == 1
f = lyr.GetNextFeature()
assert f.GetGeometryRef().ExportToWkt() == "POINT (2070 2690)"
###############################################################################
def test_ogr_mvt_with_extension_fields():
ds = ogr.Open("data/mvt/with_extension_fields.pbf")
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if ogrtest.check_feature_geometry(f, "LINESTRING (2070 2690,2082 2707)") != 0:
f.DumpReadable()
pytest.fail()
###############################################################################
def test_ogr_mvt_mixed():
ds = ogr.Open("data/mvt/mixed/0/0/0.pbf")
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
f,
"MULTIPOINT ((215246.671651058 6281289.23636264),(332653.947097085 6447616.20991119))",
)
!= 0
):
f.DumpReadable()
pytest.fail()
f = lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
f,
"LINESTRING (215246.671651058 6281289.23636264,332653.947097085 6447616.20991119)",
)
!= 0
):
f.DumpReadable()
pytest.fail()
###############################################################################
def test_ogr_mvt_linestring():
ds = ogr.Open("data/mvt/linestring/0/0/0.pbf")
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
f,
"MULTILINESTRING ((215246.671651058 6281289.23636264,332653.947097085 6447616.20991119))",
)
!= 0
):
f.DumpReadable()
pytest.fail()
ds = gdal.OpenEx("data/mvt/linestring/0/0/0.pbf", open_options=["METADATA_FILE="])
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
f,
"LINESTRING (215246.671651058 6281289.23636264,332653.947097085 6447616.20991119)",
)
!= 0
):
f.DumpReadable()
pytest.fail()
###############################################################################
def test_ogr_mvt_multilinestring():
ds = ogr.Open("data/mvt/multilinestring/0/0/0.pbf")
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
f,
"MULTILINESTRING ((215246.671651058 6281289.23636264,332653.947097085 6447616.20991119),(440277.282922614 6623727.12308023,547900.618748143 6809621.97586978),(665307.894194175 6985732.88903883,772931.230019704 7171627.74182838))",
)
!= 0
):
f.DumpReadable()
pytest.fail()
ds = gdal.OpenEx(
"data/mvt/multilinestring/0/0/0.pbf", open_options=["METADATA_FILE="]
)
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
f,
"MULTILINESTRING ((215246.671651058 6281289.23636264,332653.947097085 6447616.20991119),(440277.282922614 6623727.12308023,547900.618748143 6809621.97586978),(665307.894194175 6985732.88903883,772931.230019704 7171627.74182838))",
)
!= 0
):
f.DumpReadable()
pytest.fail()
###############################################################################
def test_ogr_mvt_polygon():
ds = ogr.Open("data/mvt/polygon/0/0/0.pbf")
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
f,
"MULTIPOLYGON (((332653.947097085 6447616.20991119,332653.947097085 6281289.23636264,215246.671651058 6281289.23636264,215246.671651058 6447616.20991119,332653.947097085 6447616.20991119)))",
)
!= 0
):
f.DumpReadable()
pytest.fail()
ds = gdal.OpenEx("data/mvt/polygon/0/0/0.pbf", open_options=["METADATA_FILE="])
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
f,
"POLYGON ((332653.947097085 6447616.20991119,332653.947097085 6281289.23636264,215246.671651058 6281289.23636264,215246.671651058 6447616.20991119,332653.947097085 6447616.20991119))",
)
!= 0
):
f.DumpReadable()
pytest.fail()
###############################################################################
def test_ogr_mvt_point_polygon():
ds = ogr.Open("data/mvt/point_polygon/0")
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
f, "MULTIPOINT ((215246.671651058 6281289.23636264))"
)
!= 0
):
f.DumpReadable()
pytest.fail()
lyr = ds.GetLayer(1)
f = lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
f,
"MULTIPOLYGON (((440277.282922614 450061.222543117,440277.282922614 -440277.282922614,0.0 -440277.282922614,0.0 -215246.671651058,215246.671651058 -215246.671651058,215246.671651058 225030.61127156,0.0 225030.61127156,0.0 450061.222543117,440277.282922614 450061.222543117)),((0.0 117407.275446031,0.0 -107623.335825529,-117407.275446031 -107623.335825529,-117407.275446031 117407.275446031,0.0 117407.275446031)),((107623.335825529 58703.6377230138,107623.335825529 -48919.6981025115,48919.6981025115 -48919.6981025115,48919.6981025115 58703.6377230138,107623.335825529 58703.6377230138)))",
)
!= 0
):
f.DumpReadable()
pytest.fail()
###############################################################################
def test_ogr_mvt_point_polygon_clip():
if not (ogrtest.have_geos() or gdal.GetConfigOption("OGR_MVT_CLIP") is not None):
pytest.skip()
ds = ogr.Open("data/mvt/point_polygon/1")
lyr = ds.GetLayer(1)
f = lyr.GetNextFeature()
expected_wkt = "MULTIPOLYGON (((0.0 112515.30563578,0 0,-112515.30563578 0.0,-112515.30563578 112515.30563578,0.0 112515.30563578)))"
expected_wkt2 = "MULTIPOLYGON (((-112515.30563578 112515.30563578,0.0 112515.30563578,0 0,-112515.30563578 0.0,-112515.30563578 112515.30563578)))"
expected_wkt3 = "MULTIPOLYGON (((0 0,-112515.30563578 0.0,-112515.30563578 112515.30563578,0.0 112515.30563578,0 0)))"
assert (
ogrtest.check_feature_geometry(f, expected_wkt) == 0
or ogrtest.check_feature_geometry(f, expected_wkt2) == 0
or ogrtest.check_feature_geometry(f, expected_wkt3) == 0
), f.GetGeometryRef().ExportToWkt()
###############################################################################
def test_ogr_mvt_tileset_without_readdir():
with gdaltest.config_option("MVT_USE_READDIR", "NO"):
ds = gdal.OpenEx("data/mvt/linestring/0")
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
assert f is not None
###############################################################################
def test_ogr_mvt_tileset_tilegl():
ds = ogr.Open("data/mvt/linestring_tilejson_gl/0")
lyr = ds.GetLayer(0)
assert lyr.GetLayerDefn().GetFieldCount() == 2
f = lyr.GetNextFeature()
assert f is not None
###############################################################################
def test_ogr_mvt_tileset_without_metadata_file():
ds = gdal.OpenEx(
"data/mvt/point_polygon/1", open_options=["METADATA_FILE=", "CLIP=NO"]
)
lyr = ds.GetLayerByName("point")
assert lyr.GetGeomType() == ogr.wkbMultiPoint
lyr = ds.GetLayerByName("polygon2")
assert lyr.GetGeomType() == ogr.wkbMultiPolygon
assert lyr.GetLayerDefn().GetFieldCount() == 2
###############################################################################
def test_ogr_mvt_tileset_json_field():
ds = gdal.OpenEx(
"data/mvt/datatypes/0",
open_options=["METADATA_FILE=", "JSON_FIELD=YES", "CLIP=NO"],
)
lyr = ds.GetLayer(0)
assert lyr.GetLayerDefn().GetFieldCount() == 2
f = lyr.GetNextFeature()
d = json.loads(f.GetFieldAsString("json"))
assert d == {
"bool_true": True,
"bool_false": False,
"pos_int_value": 1,
"pos_int64_value": 123456789012345,
"neg_int_value": -1,
"neg_int64_value": -123456789012345,
"pos_sint_value": 1,
"pos_sint64_value": 123456789012345,
"neg_sint_value": -1,
"neg_sint64_value": -123456789012345,
"uint_value": 2000000000,
"uint64_value": 4000000000,
"float_value": 1.25,
"real_value": 1.23456789,
"string_value": "str",
}, f.GetFieldAsString("json")
###############################################################################
def test_ogr_mvt_open_variants():
expected_geom = "MULTILINESTRING ((215246.671651058 6281289.23636264,332653.947097085 6447616.20991119))"
ds = ogr.Open("MVT:data/mvt/linestring/0/0/0.pbf")
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if ogrtest.check_feature_geometry(f, expected_geom) != 0:
f.DumpReadable()
pytest.fail()
ds = ogr.Open("MVT:data/mvt/linestring/0")
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if ogrtest.check_feature_geometry(f, expected_geom) != 0:
f.DumpReadable()
pytest.fail()
ds = ogr.Open("/vsigzip/data/mvt/linestring/0/0/0.pbf")
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if ogrtest.check_feature_geometry(f, expected_geom) != 0:
f.DumpReadable()
pytest.fail()
ds = ogr.Open("MVT:/vsigzip/data/mvt/linestring/0/0/0.pbf")
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if ogrtest.check_feature_geometry(f, expected_geom) != 0:
f.DumpReadable()
pytest.fail()
###############################################################################
def test_ogr_mvt_xyz_options():
ds = gdal.OpenEx("data/mvt/datatypes/0/0/0.pbf", open_options=["X=1", "Y=2", "Z=3"])
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(f, "POINT (-12496536.8802869 8299226.7830913)")
!= 0
):
f.DumpReadable()
pytest.fail()
###############################################################################
def test_ogr_mvt_test_ogrsf_pbf():
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/mvt/datatypes/0/0/0.pbf"
)
assert ret.find("INFO") != -1 and ret.find("ERROR") == -1
###############################################################################
def test_ogr_mvt_test_ogrsf_directory():
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/mvt/datatypes/0"
)
assert ret.find("INFO") != -1 and ret.find("ERROR") == -1
###############################################################################
@pytest.mark.require_driver("MBTILES")
def test_ogr_mvt_mbtiles():
ds = ogr.Open("data/mvt/point_polygon.mbtiles")
lyr = ds.GetLayerByName("point")
f = lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
f, "MULTIPOINT ((220138.641461308 6276397.26655239))"
)
!= 0
):
f.DumpReadable()
pytest.fail()
lyr.SetSpatialFilterRect(0, 0, 10000000, 10000000)
lyr.ResetReading()
f = lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
f, "MULTIPOINT ((220138.641461308 6276397.26655239))"
)
!= 0
):
f.DumpReadable()
pytest.fail()
###############################################################################
@pytest.mark.require_driver("MBTILES")
def test_ogr_mvt_mbtiles_json_field():
ds = gdal.OpenEx(
"data/mvt/datatypes.mbtiles", open_options=["JSON_FIELD=YES", "CLIP=NO"]
)
lyr = ds.GetLayer(0)
assert lyr.GetLayerDefn().GetFieldCount() == 2
f = lyr.GetNextFeature()
d = json.loads(f.GetFieldAsString("json"))
assert d == {
"int64_value": 123456789012345,
"string_value": "str",
"real_value": 1.23456789,
"bool_false": False,
"pos_int_value": 1,
"neg_int_value": -1,
"bool_true": True,
"float_value": 1.25,
}, f.GetFieldAsString("json")
###############################################################################
@pytest.mark.require_driver("MBTILES")
def test_ogr_mvt_mbtiles_json_field_auto():
ds = gdal.OpenEx(
"data/mvt/datatypes_json_field_auto.mbtiles", open_options=["CLIP=NO"]
)
lyr = ds.GetLayer(0)
assert lyr.GetLayerDefn().GetFieldCount() == 2
f = lyr.GetNextFeature()
d = json.loads(f.GetFieldAsString("json"))
assert d == {
"int64_value": 123456789012345,
"string_value": "str",
"real_value": 1.23456789,
"bool_false": False,
"pos_int_value": 1,
"neg_int_value": -1,
"bool_true": True,
"float_value": 1.25,
}, f.GetFieldAsString("json")
###############################################################################
@pytest.mark.require_driver("MBTILES")
def test_ogr_mvt_mbtiles_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/mvt/point_polygon.mbtiles polygon2"
)
assert ret.find("INFO") != -1 and ret.find("ERROR") == -1
###############################################################################
@pytest.mark.require_driver("MBTILES")
def test_ogr_mvt_mbtiles_open_vector_in_raster_mode():
with pytest.raises(Exception):
gdal.OpenEx("data/mvt/datatypes.mbtiles", gdal.OF_RASTER)
###############################################################################
def test_ogr_mvt_x_y_z_filename_scheme():
tmpfilename = "/vsimem/0-0-0.pbf"
gdal.FileFromMemBuffer(
tmpfilename, open("data/mvt/linestring/0/0/0.pbf", "rb").read()
)
ds = ogr.Open(tmpfilename)
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
f,
"LINESTRING (215246.671651058 6281289.23636264,332653.947097085 6447616.20991119)",
)
!= 0
):
f.DumpReadable()
pytest.fail()
ds = None
gdal.Unlink(tmpfilename)
###############################################################################
def test_ogr_mvt_polygon_larger_than_header():
with gdaltest.config_option("OGR_MVT_ENFORE_EXTERNAL_RING_IS_CLOCKWISE", "NO"):
ds = gdal.OpenEx(
"data/mvt/polygon_larger_than_header.pbf", open_options=["CLIP=NO"]
)
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
assert f is not None
###############################################################################
def test_ogr_mvt_errors():
with pytest.raises(Exception):
assert ogr.Open("MVT:/i_do_not/exist") is None
# Cannot detect Z in directory name
with pytest.raises(Exception):
assert ogr.Open("MVT:data") is None
# Invalid Z
gdal.Mkdir("/vsimem/33", 0)
with pytest.raises(Exception):
assert ogr.Open("MVT:/vsimem/33") is None
gdal.Rmdir("/vsimem/33")
# Inexisting metadata
with gdaltest.error_handler():
assert (
gdal.OpenEx(
"data/mvt/linestring/0/0/0.pbf",
open_options=["METADATA_FILE=/i_do_not/exist"],
)
is not None
)
# Invalid metadata
with gdaltest.error_handler():
assert (
gdal.OpenEx(
"data/mvt/linestring/0/0/0.pbf",
open_options=["METADATA_FILE=ogr_mvt.py"],
)
is not None
)
# Invalid metadata
gdal.FileFromMemBuffer("/vsimem/my.json", "{}")
with gdaltest.error_handler():
assert (
gdal.OpenEx(
"data/mvt/linestring/0/0/0.pbf",
open_options=["METADATA_FILE=/vsimem/my.json"],
)
is not None
)
gdal.Unlink("/vsimem/my.json")
# Invalid metadata
gdal.FileFromMemBuffer("/vsimem/my.json", '{ "json": "x y" }')
with gdaltest.error_handler():
assert (
gdal.OpenEx(
"data/mvt/linestring/0/0/0.pbf",
open_options=["METADATA_FILE=/vsimem/my.json"],
)
is not None
)
gdal.Unlink("/vsimem/my.json")
# Too big file
tmpfilename = "/vsimem/foo.pbf"
gdal.FileFromMemBuffer(
tmpfilename, open("data/mvt/polygon_larger_than_header.pbf", "rb").read()
)
f = gdal.VSIFOpenL(tmpfilename, "rb+")
gdal.VSIFSeekL(f, 20 * 1024 * 1024, 0)
gdal.VSIFWriteL(" ", 1, 1, f)
gdal.VSIFCloseL(f)
with pytest.raises(Exception):
ogr.Open(tmpfilename)
gdal.Unlink(tmpfilename)
###############################################################################
@pytest.mark.require_curl()
def test_ogr_mvt_http_start():
gdaltest.webserver_process = None
gdaltest.webserver_port = 0
(gdaltest.webserver_process, gdaltest.webserver_port) = webserver.launch(
handler=webserver.DispatcherHttpHandler
)
if gdaltest.webserver_port == 0:
pytest.skip()
###############################################################################
@pytest.mark.require_curl()
def test_ogr_mvt_http():
if gdaltest.webserver_port == 0:
pytest.skip()
handler = webserver.SequentialHandler()
handler.add(
"GET",
"/linestring/metadata.json",
200,
{},
open("data/mvt/linestring/metadata.json", "rb").read(),
)
handler.add(
"GET",
"/linestring/0/0/0.pbf",
200,
{},
open("data/mvt/linestring/0/0/0.pbf", "rb").read(),
)
handler.add(
"GET",
"/linestring/0/0/0.pbf",
200,
{},
open("data/mvt/linestring/0/0/0.pbf", "rb").read(),
)
with webserver.install_http_handler(handler):
ds = ogr.Open("MVT:http://127.0.0.1:%d/linestring/0" % gdaltest.webserver_port)
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
assert f is not None
# No metadata file nor tile
handler = webserver.SequentialHandler()
handler.add("GET", "/linestring/metadata.json", 404, {})
handler.add("GET", "/linestring.json", 404, {})
handler.add("GET", "/linestring/0/0/0.pbf", 404, {})
with webserver.install_http_handler(handler):
with pytest.raises(Exception):
ogr.Open("MVT:http://127.0.0.1:%d/linestring/0" % gdaltest.webserver_port)
# No metadata file, but tiles
handler = webserver.SequentialHandler()
handler.add("GET", "/linestring/metadata.json", 404, {})
handler.add("GET", "/linestring.json", 404, {})
handler.add(
"GET",
"/linestring/0/0/0.pbf",
200,
{},
open("data/mvt/linestring/0/0/0.pbf", "rb").read(),
)
handler.add(
"GET",
"/linestring/0/0/0.pbf",
200,
{},
open("data/mvt/linestring/0/0/0.pbf", "rb").read(),
)
with webserver.install_http_handler(handler):
ds = ogr.Open("MVT:http://127.0.0.1:%d/linestring/0" % gdaltest.webserver_port)
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
assert f is not None
# metadata.json file, but no tiles
handler = webserver.SequentialHandler()
handler.add(
"GET",
"/linestring/metadata.json",
200,
{},
open("data/mvt/linestring/metadata.json", "rb").read(),
)
handler.add("GET", "/linestring/0/0/0.pbf", 404, {})
handler.add("GET", "/linestring/0/0/0.pbf", 404, {})
with webserver.install_http_handler(handler):
ds = ogr.Open("MVT:http://127.0.0.1:%d/linestring/0" % gdaltest.webserver_port)
lyr = ds.GetLayer(0)
with pytest.raises(Exception):
lyr.GetNextFeature()
# No metadata.json file, but a linestring.json and no tiles
handler = webserver.SequentialHandler()
handler.add("GET", "/linestring/metadata.json", 404, {})
handler.add(
"GET",
"/linestring.json",
200,
{},
open("data/mvt/linestring/metadata.json", "rb").read(),
)
handler.add("GET", "/linestring/0/0/0.pbf", 404, {})
handler.add("GET", "/linestring/0/0/0.pbf", 404, {})
with webserver.install_http_handler(handler):
ds = ogr.Open("MVT:http://127.0.0.1:%d/linestring/0" % gdaltest.webserver_port)
lyr = ds.GetLayer(0)
with pytest.raises(Exception):
lyr.GetNextFeature()
# Open pbf file
handler = webserver.SequentialHandler()
handler.add(
"GET",
"/linestring/0/0/0.pbf",
200,
{},
open("data/mvt/linestring/0/0/0.pbf", "rb").read(),
)
with webserver.install_http_handler(handler):
ds = ogr.Open(
"MVT:http://127.0.0.1:%d/linestring/0/0/0.pbf" % gdaltest.webserver_port
)
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
assert f is not None
###############################################################################
@pytest.mark.require_curl()
def test_ogr_mvt_http_stop():
if gdaltest.webserver_port == 0:
pytest.skip()
webserver.server_stop(gdaltest.webserver_process, gdaltest.webserver_port)
###############################################################################
@pytest.mark.require_driver("SQLite")
@pytest.mark.require_geos
def test_ogr_mvt_write_one_layer(has_make_valid):
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0, gdal.GDT_Unknown)
lyr = src_ds.CreateLayer("mylayer")
lyr.CreateField(ogr.FieldDefn("strfield", ogr.OFTString))
lyr.CreateField(ogr.FieldDefn("intfield", ogr.OFTInteger))
lyr.CreateField(ogr.FieldDefn("int64field", ogr.OFTInteger64))
lyr.CreateField(ogr.FieldDefn("realfield", ogr.OFTReal))
lyr.CreateField(ogr.FieldDefn("datefield", ogr.OFTDate))
lyr.CreateField(ogr.FieldDefn("datetimefield", ogr.OFTDateTime))
boolfield = ogr.FieldDefn("boolfield", ogr.OFTInteger)
boolfield.SetSubType(ogr.OFSTBoolean)
lyr.CreateField(boolfield)
# Test empty layer: OK
with gdaltest.error_handler():
out_ds = gdal.VectorTranslate("/vsimem/outmvt", src_ds, format="MVT")
assert out_ds is not None
# Cannot create directory
with pytest.raises(Exception):
gdal.VectorTranslate("/i_dont/exist/outmvt", src_ds, format="MVT")
# Directory already exists
with pytest.raises(Exception):
gdal.VectorTranslate("/vsimem/outmvt", src_ds, format="MVT")
gdal.RmdirRecursive("/vsimem/outmvt")
f = ogr.Feature(lyr.GetLayerDefn())
f.SetFID(123)
f["strfield"] = "foo"
f["intfield"] = -1
f["int64field"] = 123456789012345
f["realfield"] = 1.25
f["datefield"] = "2018/02/01"
f["datetimefield"] = "2018/02/01 12:34:56"
f["boolfield"] = True
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(500000 1000000)"))
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetFID(124)
f["strfield"] = "foo"
f["intfield"] = -1
f["int64field"] = 123456789012345
f["realfield"] = 1.25
f["datefield"] = "2018/02/01"
f["datetimefield"] = "2018/02/01 12:34:56"
f["boolfield"] = True
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"LINESTRING(500000 1000000,510000 1010000,520000 1020000)"
)
)
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetFID(125)
f["strfield"] = "foobarbazbaw"
f["intfield"] = 1
f["int64field"] = -123456789012345
f["realfield"] = -1.25678
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"POLYGON((500000 1000000,510000 1000000,510000 1010000,500000 1000000))"
)
)
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetFID(126)
f.SetGeometry(
ogr.CreateGeometryFromWkt("MULTIPOINT(500000 1000000,510000 1010000)")
)
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetFID(127)
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"MULTILINESTRING((500000 1000000,510000 1010000),(510000 1010000,510000 1000000))"
)
)
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetFID(128)
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"MULTIPOLYGON(((500000 1000000,510000 1000000,510000 1010000,500000 1000000)),((-500000 1000000,-510000 1000000,-510000 1010000,-500000 1000000),(-502000 1001000,-509000 1001000,-509000 1008500,-502000 1001000)))"
)
)
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetFID(129)
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"GEOMETRYCOLLECTION(POINT(500000 1000000),LINESTRING(500000 1000000,510000 1010000,520000 1020000))"
)
)
lyr.CreateFeature(f)
with gdaltest.error_handler():
out_ds = gdal.VectorTranslate(
"/vsimem/outmvt", src_ds, options="-f MVT -preserve_fid"
)
assert out_ds is not None
out_ds = None
out_ds = ogr.Open("/vsimem/outmvt/0")
assert out_ds is not None
out_lyr = out_ds.GetLayerByName("mylayer")
out_f = out_lyr.GetNextFeature()
if (
out_f["mvt_id"] != 123
or out_f["strfield"] != "foo"
or out_f["intfield"] != -1
or out_f["int64field"] != 123456789012345
or out_f["realfield"] != 1.25
or out_f["datefield"] != "2018-02-01"
or out_f["datetimefield"] != "2018-02-01T12:34:56"
or out_f["boolfield"] is False
or ogrtest.check_feature_geometry(
out_f, "POINT (498980.920645632 997961.84129126)"
)
!= 0
):
out_f.DumpReadable()
pytest.fail()
out_f = out_lyr.GetNextFeature()
if (
out_f["strfield"] != "foo"
or out_f["intfield"] != -1
or out_f["int64field"] != 123456789012345
or out_f["realfield"] != 1.25
or out_f["datefield"] != "2018-02-01"
or out_f["datetimefield"] != "2018-02-01T12:34:56"
or out_f["boolfield"] is False
or ogrtest.check_feature_geometry(
out_f,
"MULTILINESTRING ((498980.920645632 997961.84129126,508764.860266134 1007745.78091176,518548.799886636 1017529.72053226))",
)
!= 0
):
out_f.DumpReadable()
pytest.fail()
out_f = out_lyr.GetNextFeature()
if (
out_f["strfield"] != "foobarbazbaw"
or out_f["intfield"] != 1
or out_f["int64field"] != -123456789012345
or out_f["realfield"] != -1.25678
or out_f.IsFieldSet("datefield")
or out_f.IsFieldSet("datetimefield")
or out_f.IsFieldSet("boolfield")
or ogrtest.check_feature_geometry(
out_f,
"POLYGON ((498980.920645632 997961.84129126,508764.860266134 1007745.78091176,508764.860266134 997961.84129126,498980.920645632 997961.84129126))",
)
!= 0
):
out_f.DumpReadable()
pytest.fail()
out_f = out_lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
out_f,
"MULTIPOINT ((498980.920645632 997961.84129126),(508764.860266134 1007745.78091176))",
)
!= 0
):
out_f.DumpReadable()
pytest.fail()
out_f = out_lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
out_f,
"MULTILINESTRING ((498980.920645632 997961.84129126,508764.860266134 1007745.78091176),(508764.860266134 1007745.78091176,508764.860266134 997961.84129126))",
)
!= 0
):
out_f.DumpReadable()
pytest.fail()
out_f = out_lyr.GetNextFeature()
if (
(
(not has_make_valid)
and ogrtest.check_feature_geometry(
out_f,
"MULTIPOLYGON (((498980.920645632 997961.84129126,508764.860266134 1007745.78091176,508764.860266134 997961.84129126,498980.920645632 997961.84129126)),((-498980.920645632 997961.84129126,-508764.860266134 997961.84129126,-508764.860266134 1007745.78091176,-498980.920645632 997961.84129126)))",
)
!= 0
)
# Below is what we get with MakeValid() available
or (
# GEOS > 3.8 (not sure which minimum version)
has_make_valid
and ogrtest.check_feature_geometry(
out_f,
"MULTIPOLYGON (((-508764.860266134 1007745.78091176,-498980.920645632 997961.84129126,-508764.860266134 997961.84129126,-508764.860266134 1007745.78091176)),((508764.860266134 1007745.78091176,508764.860266134 997961.84129126,498980.920645632 997961.84129126,508764.860266134 1007745.78091176)))",
)
!= 0
# Below result with GEOS 3.8
and ogrtest.check_feature_geometry(
out_f,
"MULTIPOLYGON (((498980.920645632 997961.84129126,508764.860266134 1007745.78091176,508764.860266134 997961.84129126,498980.920645632 997961.84129126)),((-508764.860266134 997961.84129126,-508764.860266134 1007745.78091176,-498980.920645632 997961.84129126,-508764.860266134 997961.84129126)))",
)
!= 0
)
):
out_f.DumpReadable()
pytest.fail()
out_f = out_lyr.GetNextFeature()
if (
(
out_f.GetGeometryRef().GetGeometryType() == ogr.wkbPoint
and ogrtest.check_feature_geometry(
out_f, "POINT (498980.920645632 997961.84129126)"
)
!= 0
)
or (
out_f.GetGeometryRef().GetGeometryType() == ogr.wkbMultiLineString
and ogrtest.check_feature_geometry(
out_f,
"MULTILINESTRING ((498980.920645632 997961.84129126,508764.860266134 1007745.78091176,518548.799886636 1017529.72053226))",
)
!= 0
)
or out_f.GetGeometryRef().GetGeometryType()
not in (ogr.wkbPoint, ogr.wkbMultiLineString)
):
out_f.DumpReadable()
pytest.fail()
out_f = out_lyr.GetNextFeature()
if (
(
out_f.GetGeometryRef().GetGeometryType() == ogr.wkbPoint
and ogrtest.check_feature_geometry(
out_f, "POINT (498980.920645632 997961.84129126)"
)
!= 0
)
or (
out_f.GetGeometryRef().GetGeometryType() == ogr.wkbMultiLineString
and ogrtest.check_feature_geometry(
out_f,
"MULTILINESTRING ((498980.920645632 997961.84129126,508764.860266134 1007745.78091176,518548.799886636 1017529.72053226))",
)
!= 0
)
or out_f.GetGeometryRef().GetGeometryType()
not in (ogr.wkbPoint, ogr.wkbMultiLineString)
):
out_f.DumpReadable()
pytest.fail()
out_ds = ogr.Open("/vsimem/outmvt/5")
assert out_ds is not None
out_lyr = out_ds.GetLayerByName("mylayer")
assert out_lyr.GetFeatureCount() == 9
f = gdal.VSIFOpenL("/vsimem/outmvt/metadata.json", "rb")
assert f is not None
data = gdal.VSIFReadL(1, 100000, f).decode("ASCII")
gdal.VSIFCloseL(f)
data_json = json.loads(data)
expected_json = {
"name": "outmvt",
"description": "",
"version": 2,
"minzoom": 0,
"maxzoom": 5,
"center": "0.0449158,9.0352907,0",
"bounds": "-4.5814079,8.9465739,4.6712395,9.1240075",
"type": "overlay",
"format": "pbf",
}
for k in expected_json:
assert k in data_json and data_json[k] == expected_json[k], data
json_json = json.loads(data_json["json"])
expected_json_json = {
"vector_layers": [
{
"id": "mylayer",
"description": "",
"minzoom": 0,
"maxzoom": 5,
"fields": {
"strfield": "String",
"intfield": "Number",
"int64field": "Number",
"realfield": "Number",
"datefield": "String",
"datetimefield": "String",
"boolfield": "Boolean",
},
}
],
"tilestats": {
"layerCount": 1,
"layers": [
{
"layer": "mylayer",
"count": 7,
"geometry": "LineString",
"attributeCount": 7,
"attributes": [
{
"attribute": "strfield",
"count": 2,
"type": "string",
"values": ["foo", "foobarbazbaw"],
},
{
"attribute": "intfield",
"count": 2,
"type": "number",
"values": [-1, 1],
"min": -1,
"max": 1,
},
{
"attribute": "int64field",
"count": 2,
"type": "number",
"values": [-123456789012345, 123456789012345],
"min": -123456789012345,
"max": 123456789012345,
},
{
"attribute": "realfield",
"count": 2,
"type": "number",
"values": [-1.256780, 1.250000],
"min": -1.25678,
"max": 1.25,
},
{
"attribute": "datefield",
"count": 1,
"type": "string",
"values": ["2018-02-01"],
},
{
"attribute": "datetimefield",
"count": 1,
"type": "string",
"values": ["2018-02-01T12:34:56"],
},
{
"attribute": "boolfield",
"count": 1,
"type": "boolean",
"values": [True],
},
],
}
],
},
}
assert json_json == expected_json_json, data_json["json"]
gdal.RmdirRecursive("/vsimem/outmvt")
###############################################################################
@pytest.mark.require_driver("SQLite")
@pytest.mark.require_geos
def test_ogr_mvt_write_conf():
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0, gdal.GDT_Unknown)
lyr = src_ds.CreateLayer("mylayer")
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(500000 1000000)"))
lyr.CreateFeature(f)
conf = {
"mylayer": {
"target_name": "TheLayer",
"description": "the layer",
"minzoom": 1,
"maxzoom": 2,
}
}
with gdaltest.tempfile("/vsimem/conf.json", json.dumps(conf)):
out_ds = gdal.VectorTranslate(
"/vsimem/outmvt",
src_ds,
format="MVT",
datasetCreationOptions=["CONF=/vsimem/conf.json"],
)
assert out_ds is not None
out_ds = None
out_ds = ogr.Open("/vsimem/outmvt/1")
assert out_ds is not None
out_lyr = out_ds.GetLayerByName("TheLayer")
assert out_lyr
out_ds = None
gdal.RmdirRecursive("/vsimem/outmvt")
out_ds = gdal.VectorTranslate(
"/vsimem/outmvt",
src_ds,
format="MVT",
datasetCreationOptions=["CONF=%s" % json.dumps(conf)],
)
assert out_ds is not None
out_ds = None
out_ds = ogr.Open("/vsimem/outmvt/1")
assert out_ds is not None
out_lyr = out_ds.GetLayerByName("TheLayer")
assert out_lyr
out_f = out_lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
out_f, "MULTIPOINT (498980.920645632 997961.84129126)"
)
!= 0
):
out_f.DumpReadable()
pytest.fail()
f = gdal.VSIFOpenL("/vsimem/outmvt/metadata.json", "rb")
assert f is not None
data = gdal.VSIFReadL(1, 100000, f).decode("ASCII")
gdal.VSIFCloseL(f)
data_json = json.loads(data)
json_json = json.loads(data_json["json"])
expected_json_json = {
"vector_layers": [
{
"id": "TheLayer",
"description": "the layer",
"minzoom": 1,
"maxzoom": 2,
"fields": {},
}
],
"tilestats": {
"layerCount": 1,
"layers": [
{
"layer": "TheLayer",
"count": 1,
"geometry": "Point",
"attributeCount": 0,
"attributes": [],
}
],
},
}
assert json_json == expected_json_json, data_json["json"]
gdal.RmdirRecursive("/vsimem/outmvt")
###############################################################################
@pytest.mark.require_driver("SQLite")
@pytest.mark.require_geos
def test_ogr_mvt_write_mbtiles():
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0, gdal.GDT_Unknown)
lyr = src_ds.CreateLayer("mylayer")
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(500000 1000000)"))
lyr.CreateFeature(f)
out_ds = gdal.VectorTranslate("/vsimem/out.mbtiles", src_ds)
assert out_ds is not None
out_ds = None
out_ds = ogr.Open("/vsimem/out.mbtiles")
assert out_ds is not None
out_lyr = out_ds.GetLayerByName("mylayer")
out_f = out_lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
out_f, "MULTIPOINT ((499898.164985053 1000102.07808325))"
)
!= 0
):
out_f.DumpReadable()
pytest.fail()
out_ds = None
gdal.Unlink("/vsimem/out.mbtiles")
###############################################################################
@pytest.mark.require_driver("SQLite")
@pytest.mark.require_geos
def test_ogr_mvt_write_limitations_max_size():
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0, gdal.GDT_Unknown)
lyr = src_ds.CreateLayer("mylayer")
lyr.CreateField(ogr.FieldDefn("field"))
f = ogr.Feature(lyr.GetLayerDefn())
f.SetField(
"field", "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
)
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(500000 1000000)"))
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(
ogr.CreateGeometryFromWkt("LINESTRING(500000 1000000,510000 1000000)")
)
lyr.CreateFeature(f)
# Also test single threaded execution
with gdaltest.config_option("GDAL_NUM_THREADS", "1"):
out_ds = gdal.VectorTranslate(
"/vsimem/out.mbtiles",
src_ds,
datasetCreationOptions=["MAX_SIZE=100", "SIMPLIFICATION=1"],
)
assert out_ds is not None
out_ds = None
out_ds = ogr.Open("/vsimem/out.mbtiles")
assert out_ds is not None
out_lyr = out_ds.GetLayerByName("mylayer")
out_f = out_lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
out_f,
"MULTILINESTRING ((498980.920645631 1007745.78091176,508764.860266133 1007745.78091176))",
)
!= 0
):
out_f.DumpReadable()
pytest.fail()
out_f = out_lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
out_f, "POINT (498980.920645631 1007745.78091176)"
)
!= 0
):
out_f.DumpReadable()
pytest.fail()
out_ds = None
gdal.Unlink("/vsimem/out.mbtiles")
###############################################################################
@pytest.mark.require_driver("SQLite")
@pytest.mark.require_geos
def test_ogr_mvt_write_polygon_repaired():
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0, gdal.GDT_Unknown)
lyr = src_ds.CreateLayer("mylayer")
lyr.CreateField(ogr.FieldDefn("field"))
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"POLYGON((0 0,0 500000,100000 500000,100000 200000,100500 200000,100500 500000,500000 500000,500000 0,0 0))"
)
)
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"MULTIPOLYGON(((0 0,0 500000,100000 500000,100000 200000,100500 200000,100500 500000,500000 500000,500000 0,0 0)),((1000000 0,1000000 1000000,2000000 1000000,1000000 0)))"
)
)
lyr.CreateFeature(f)
# Cannot be repaired
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("POLYGON((0 0,0 1,1 1,0 0))"))
lyr.CreateFeature(f)
out_ds = gdal.VectorTranslate(
"/vsimem/out.mbtiles", src_ds, datasetCreationOptions=["MAXZOOM=0"]
)
assert out_ds is not None
out_ds = None
out_ds = ogr.Open("/vsimem/out.mbtiles")
assert out_ds is not None
out_lyr = out_ds.GetLayerByName("mylayer")
out_f = out_lyr.GetNextFeature()
# Second expected result is using the HAVE_MAKE_VALID code path with GEOS 3.8
# Third expected result is using the HAVE_MAKE_VALID code path with GEOS 3.10
if (
ogrtest.check_feature_geometry(
out_f,
"MULTIPOLYGON (((0 0,0.0 498980.920645632,498980.920645632 498980.920645632,498980.920645632 0.0,0 0)))",
)
!= 0
and ogrtest.check_feature_geometry(
out_f,
"MULTIPOLYGON (((97839.3962050267 498980.920645632,498980.920645632 498980.920645632,498980.920645632 0.0,0 0,0.0 498980.920645632,97839.3962050267 498980.920645632)))",
)
!= 0
and ogrtest.check_feature_geometry(
out_f,
"MULTIPOLYGON (((0.0 498980.920645632,97839.3962050267 498980.920645632,498980.920645632 498980.920645632,498980.920645632 0.0,0 0,0.0 498980.920645632)))",
)
!= 0
):
out_f.DumpReadable()
pytest.fail()
out_f = out_lyr.GetNextFeature()
# Second expected result is using the HAVE_MAKE_VALID code path
# Third expected result is using the HAVE_MAKE_VALID code path with GEOS 3.10
if (
ogrtest.check_feature_geometry(
out_f,
"MULTIPOLYGON (((0 0,0.0 498980.920645632,498980.920645632 498980.920645632,498980.920645632 0.0,0 0)),((997961.84129126 0.0,997961.84129126 997961.84129126,1995923.68258252 997961.84129126,997961.84129126 0.0)))",
)
!= 0
and ogrtest.check_feature_geometry(
out_f,
"MULTIPOLYGON (((997961.84129126 0.0,997961.84129126 997961.84129126,1995923.68258252 997961.84129126,997961.84129126 0.0)),((97839.3962050267 498980.920645632,498980.920645632 498980.920645632,498980.920645632 0.0,0 0,0.0 498980.920645632,97839.3962050267 498980.920645632)))",
)
!= 0
and ogrtest.check_feature_geometry(
out_f,
"MULTIPOLYGON (((997961.84129126 997961.84129126,1995923.68258252 997961.84129126,997961.84129126 0.0,997961.84129126 997961.84129126)),((0 0,0.0 498980.920645632,97839.3962050267 498980.920645632,498980.920645632 498980.920645632,498980.920645632 0.0,0 0)))",
)
!= 0
):
out_f.DumpReadable()
pytest.fail()
out_f = out_lyr.GetNextFeature()
if out_f is not None:
out_f.DumpReadable()
pytest.fail()
out_ds = None
gdal.Unlink("/vsimem/out.mbtiles")
###############################################################################
@pytest.mark.require_driver("SQLite")
@pytest.mark.require_geos
def test_ogr_mvt_write_conflicting_innner_ring(has_make_valid):
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0, gdal.GDT_Unknown)
lyr = src_ds.CreateLayer("mylayer")
lyr.CreateField(ogr.FieldDefn("field"))
f = ogr.Feature(lyr.GetLayerDefn())
# the second inner ring conflicts with the first one once transformed to integer coordinates
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"POLYGON((-500000 1000000,-510000 1000000,-510000 1010000,-500000 1000000),(-502000 1001000,-509000 1001000,-509000 1008500,-502000 1001000),(-502000 1000900,-509000 1000900,-509000 1000800,-502000 1000900))"
)
)
lyr.CreateFeature(f)
out_ds = gdal.VectorTranslate("/vsimem/out.mbtiles", src_ds)
assert out_ds is not None
out_ds = None
out_ds = ogr.Open("/vsimem/out.mbtiles")
assert out_ds is not None
out_lyr = out_ds.GetLayerByName("mylayer")
out_f = out_lyr.GetNextFeature()
if (
(
(not has_make_valid)
and ogrtest.check_feature_geometry(
out_f,
"MULTIPOLYGON (((-499898.164985052 1000102.07808325,-509987.852718695 1000102.07808325,-509987.852718695 1009886.01770375,-499898.164985052 1000102.07808325),(-502038.401777037 1001019.32242267,-509070.608379273 1008357.27713804,-509070.608379273 1001019.32242267,-502038.401777037 1001019.32242267)))",
)
!= 0
)
# Below is what we get with MakeValid() available
or (
has_make_valid
and ogrtest.check_feature_geometry(
out_f,
"MULTIPOLYGON (((-499898.164985052 1000102.07808325,-509987.852718695 1000102.07808325,-509987.852718695 1009886.01770375,-499898.164985052 1000102.07808325),(-502038.401777037 1001019.32242267,-509070.608379273 1008357.27713804,-509070.608379273 1001019.32242267,-509070.608379273 1000713.57430953,-502038.401777037 1001019.32242267)))",
)
!= 0
)
):
out_f.DumpReadable()
pytest.fail()
out_ds = None
gdal.Unlink("/vsimem/out.mbtiles")
###############################################################################
@pytest.mark.require_driver("SQLite")
@pytest.mark.require_geos
def test_ogr_mvt_write_limitations_max_size_polygon():
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0, gdal.GDT_Unknown)
lyr = src_ds.CreateLayer("mylayer")
lyr.CreateField(ogr.FieldDefn("field"))
f = ogr.Feature(lyr.GetLayerDefn())
f.SetField(
"field", "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
)
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"POLYGON((500000 1000000,510000 1000000,510000 1010000,500000 1000000),(503000 1003000,507000 1003000,507000 1005000,503000 1003000))"
)
)
lyr.CreateFeature(f)
out_ds = gdal.VectorTranslate(
"/vsimem/out.mbtiles", src_ds, datasetCreationOptions=["MAX_SIZE=100"]
)
assert out_ds is not None
out_ds = None
out_ds = ogr.Open("/vsimem/out.mbtiles")
assert out_ds is not None
out_lyr = out_ds.GetLayerByName("mylayer")
out_f = out_lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
out_f,
"MULTIPOLYGON (((498980.920645631 1007745.78091176,508764.860266133 1017529.72053227,508764.860266133 1007745.78091176,498980.920645631 1007745.78091176)))",
)
!= 0
):
out_f.DumpReadable()
pytest.fail()
out_ds = None
gdal.Unlink("/vsimem/out.mbtiles")
###############################################################################
@pytest.mark.require_driver("SQLite")
@pytest.mark.require_geos
def test_ogr_mvt_write_limitations_max_features():
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0, gdal.GDT_Unknown)
lyr = src_ds.CreateLayer("mylayer")
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(500000 1000000)"))
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(
ogr.CreateGeometryFromWkt(
"POLYGON((500000 1000000,510000 1000000,510000 1100000,500000 1000000))"
)
)
lyr.CreateFeature(f)
out_ds = gdal.VectorTranslate(
"/vsimem/out.mbtiles",
src_ds,
format="MVT",
datasetCreationOptions=["MAX_FEATURES=1"],
)
assert out_ds is not None
out_ds = None
out_ds = ogr.Open("/vsimem/out.mbtiles")
assert out_ds is not None
out_lyr = out_ds.GetLayerByName("mylayer")
out_f = out_lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
out_f,
"POLYGON ((499898.164985053 1000102.07808325,509987.852718696 1100081.71108026,509987.852718696 1000102.07808325,499898.164985053 1000102.07808325))",
)
!= 0
):
out_f.DumpReadable()
pytest.fail()
out_f = out_lyr.GetNextFeature()
if out_f is not None:
out_f.DumpReadable()
pytest.fail()
out_ds = None
gdal.Unlink("/vsimem/out.mbtiles")
###############################################################################
@pytest.mark.require_driver("SQLite")
@pytest.mark.require_geos
def test_ogr_mvt_write_custom_tiling_scheme():
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0, gdal.GDT_Unknown)
srs = osr.SpatialReference()
srs.SetFromUserInput("WGS84")
lyr = src_ds.CreateLayer("mylayer", srs=srs)
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("LINESTRING(12 71,13 72)"))
lyr.CreateFeature(f)
out_ds = gdal.VectorTranslate(
"/vsimem/out",
src_ds,
format="MVT",
datasetCreationOptions=["TILING_SCHEME=EPSG:3067,-548576,8388608,2097152"],
)
assert out_ds is not None
out_ds = None
out_ds = ogr.Open("/vsimem/out/1")
assert out_ds is not None
out_lyr = out_ds.GetLayerByName("mylayer")
assert out_lyr.GetSpatialRef().ExportToWkt().find("3067") >= 0
out_f = out_lyr.GetNextFeature()
if (
ogrtest.check_feature_geometry(
out_f, "MULTILINESTRING ((-40160 7944704,21024 8044800))"
)
!= 0
):
out_f.DumpReadable()
pytest.fail()
out_ds = None
gdal.RmdirRecursive("/vsimem/out")
###############################################################################
@pytest.mark.require_driver("SQLite")
@pytest.mark.require_geos
@gdaltest.disable_exceptions()
def test_ogr_mvt_write_errors():
# Raster creation attempt
if gdal.VSIStatL("/vsimem/foo") is not None:
gdal.RmdirRecursive("/vsimem/foo")
with gdaltest.error_handler():
ds = gdal.GetDriverByName("MVT").Create("/vsimem/foo", 1, 1)
assert ds is None
# should have mbtiles extension
gdal.RmdirRecursive("/vsimem/foo.bar")
with gdaltest.error_handler():
ds = ogr.GetDriverByName("MVT").CreateDataSource(
"/vsimem/foo.bar", options=["FORMAT=MBTILES"]
)
assert ds is None
# Cannot create temporary database
gdal.RmdirRecursive("/vsimem/foo")
with gdaltest.error_handler():
ds = ogr.GetDriverByName("MVT").CreateDataSource(
"/vsimem/foo", options=["TEMPORARY_DB=/i/do_not/exist.db"]
)
assert ds is None
# cannot create mbtiles file
with gdaltest.error_handler():
ds = ogr.GetDriverByName("MVT").CreateDataSource(
"/i/do_not/exist.mbtiles", options=["TEMPORARY_DB=/vsimem/temp.db"]
)
assert ds is None
gdal.Unlink("/vsimem/temp.db")
# invalid MINZOOM
gdal.RmdirRecursive("/vsimem/foo")
with gdaltest.error_handler():
ds = ogr.GetDriverByName("MVT").CreateDataSource(
"/vsimem/foo", options=["MINZOOM=-1"]
)
assert ds is None
with gdaltest.error_handler():
ds = ogr.GetDriverByName("MVT").CreateDataSource(
"/vsimem/foo", options=["MINZOOM=30"]
)
assert ds is None
# invalid MAXZOOM
gdal.RmdirRecursive("/vsimem/foo")
with gdaltest.error_handler():
ds = ogr.GetDriverByName("MVT").CreateDataSource(
"/vsimem/foo", options=["MAXZOOM=-1"]
)
assert ds is None
with gdaltest.error_handler():
ds = ogr.GetDriverByName("MVT").CreateDataSource(
"/vsimem/foo", options=["MAXZOOM=30"]
)
assert ds is None
# invalid MINZOOM vs MAXZOOM
gdal.RmdirRecursive("/vsimem/foo")
with gdaltest.error_handler():
ds = ogr.GetDriverByName("MVT").CreateDataSource(
"/vsimem/foo", options=["MINZOOM=1", "MAXZOOM=0"]
)
assert ds is None
# invalid MINZOOM for layer
gdal.RmdirRecursive("/vsimem/foo")
ds = ogr.GetDriverByName("MVT").CreateDataSource("/vsimem/foo")
with gdaltest.error_handler():
lyr = ds.CreateLayer("foo", options=["MINZOOM=-1"])
assert lyr is None
# invalid CONF
gdal.RmdirRecursive("/vsimem/foo")
gdal.FileFromMemBuffer("/vsimem/invalid.json", "foo bar")
with gdaltest.error_handler():
ds = ogr.GetDriverByName("MVT").CreateDataSource(
"/vsimem/foo", options=["CONF=/vsimem/invalid.json"]
)
gdal.Unlink("/vsimem/invalid.json")
assert ds is None
# TILING_SCHEME not allowed with MBTILES
with gdaltest.error_handler():
ds = ogr.GetDriverByName("MVT").CreateDataSource(
"/vsimem/foo.mbtiles", options=["TILING_SCHEME=EPSG:4326,-180,180,360"]
)
assert ds is None
# Invalid TILING_SCHEME
gdal.RmdirRecursive("/vsimem/foo")
with gdaltest.error_handler():
ds = ogr.GetDriverByName("MVT").CreateDataSource(
"/vsimem/foo", options=["TILING_SCHEME=EPSG:4326"]
)
assert ds is None
# Test failure in creating tile
gdal.RmdirRecursive("tmp/tmpmvt")
ds = ogr.GetDriverByName("MVT").CreateDataSource("tmp/tmpmvt")
gdal.RmdirRecursive("tmp/tmpmvt")
lyr = ds.CreateLayer("test")
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(0 0)"))
lyr.CreateFeature(f)
with gdaltest.error_handler():
ds = None
assert gdal.GetLastErrorMsg() != ""
gdal.RmdirRecursive("tmp/tmpmvt")
# Test failure in writing in temp db (multi-threaded)
gdal.RmdirRecursive("/vsimem/foo")
with gdaltest.config_option("OGR_MVT_REMOVE_TEMP_FILE", "NO"):
ds = ogr.GetDriverByName("MVT").CreateDataSource("/vsimem/foo")
temp_ds = ogr.Open("/vsimem/foo.temp.db", update=1)
temp_ds.ExecuteSQL("DROP TABLE temp")
temp_ds = None
gdal.Unlink("/vsimem/foo.temp.db")
lyr = ds.CreateLayer("test")
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("GEOMETRYCOLLECTION(POINT(0 0))"))
with gdaltest.error_handler():
lyr.CreateFeature(f)
ds = None
assert gdal.GetLastErrorMsg() != ""
gdal.RmdirRecursive("tmp/tmpmvt")
# Test failure in writing in temp db (single-threaded)
gdal.RmdirRecursive("/vsimem/foo")
with gdaltest.config_option("OGR_MVT_REMOVE_TEMP_FILE", "NO"):
with gdaltest.config_option("GDAL_NUM_THREADS", "1"):
ds = ogr.GetDriverByName("MVT").CreateDataSource("/vsimem/foo")
temp_ds = ogr.Open("/vsimem/foo.temp.db", update=1)
temp_ds.ExecuteSQL("DROP TABLE temp")
temp_ds = None
gdal.Unlink("/vsimem/foo.temp.db")
lyr = ds.CreateLayer("test")
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("GEOMETRYCOLLECTION(POINT(0 0))"))
with gdaltest.error_handler():
lyr.CreateFeature(f)
ds = None
assert gdal.GetLastErrorMsg() != ""
gdal.RmdirRecursive("tmp/tmpmvt")
# Test reprojection failure
gdal.RmdirRecursive("/vsimem/foo")
ds = ogr.GetDriverByName("MVT").CreateDataSource("/vsimem/foo")
with gdaltest.error_handler():
lyr = ds.CreateLayer("test", srs=osr.SpatialReference())
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(0 0)"))
lyr.CreateFeature(f)
ds = None
gdal.RmdirRecursive("/vsimem/foo")
###############################################################################
#
@pytest.mark.require_driver("SQLite")
@pytest.mark.require_geos
def test_ogr_mvt_write_reuse_temp_db():
src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0, gdal.GDT_Unknown)
lyr = src_ds.CreateLayer("mylayer")
f = ogr.Feature(lyr.GetLayerDefn())
f.SetGeometry(ogr.CreateGeometryFromWkt("LINESTRING(0 0,100000 100000,200000 0)"))
lyr.CreateFeature(f)
with gdaltest.config_option("OGR_MVT_REMOVE_TEMP_FILE", "NO"):
gdal.VectorTranslate("/vsimem/out", src_ds, format="MVT")
assert gdal.VSIStatL("/vsimem/out.temp.db") is not None
gdal.RmdirRecursive("/vsimem/out")
with gdaltest.config_option("OGR_MVT_REUSE_TEMP_FILE", "YES"):
gdal.VectorTranslate("/vsimem/out", src_ds, format="MVT")
out_ds = ogr.Open("/vsimem/out/5")
assert out_ds is not None
out_lyr = out_ds.GetLayerByName("mylayer")
out_f = out_lyr.GetNextFeature()
assert out_f is not None
out_ds = None
gdal.RmdirRecursive("/vsimem/out")
gdal.Unlink("/vsimem/out.temp.db")
###############################################################################
#