1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
import binascii
import math
import struct
import sys
import pytest
from shapely import wkt
from shapely.geometry import Point
from shapely.tests.legacy.conftest import shapely20_todo
from shapely.wkb import dump, dumps, load, loads
@pytest.fixture(scope="module")
def some_point():
return Point(1.2, 3.4)
def bin2hex(value):
return binascii.b2a_hex(value).upper().decode("utf-8")
def hex2bin(value):
return binascii.a2b_hex(value)
def hostorder(fmt, value):
"""Re-pack a hex WKB value to native endianness if needed
This routine does not understand WKB format, so it must be provided a
struct module format string, without initial indicator character ("@=<>!"),
which will be interpreted as big- or little-endian with standard sizes
depending on the endian flag in the first byte of the value.
"""
if fmt and fmt[0] in "@=<>!":
raise ValueError("Initial indicator character, one of @=<>!, in fmt")
if not fmt or fmt[0] not in "cbB":
raise ValueError("Missing endian flag in fmt")
(hexendian,) = struct.unpack(fmt[0], hex2bin(value[:2]))
hexorder = {0: ">", 1: "<"}[hexendian]
sysorder = {"little": "<", "big": ">"}[sys.byteorder]
if hexorder == sysorder:
return value # Nothing to do
return bin2hex(
struct.pack(
sysorder + fmt,
{">": 0, "<": 1}[sysorder],
*struct.unpack(hexorder + fmt, hex2bin(value))[1:],
)
)
def test_dumps_srid(some_point):
result = dumps(some_point)
assert bin2hex(result) == hostorder(
"BIdd", "0101000000333333333333F33F3333333333330B40"
)
result = dumps(some_point, srid=4326)
assert bin2hex(result) == hostorder(
"BIIdd", "0101000020E6100000333333333333F33F3333333333330B40"
)
def test_dumps_endianness(some_point):
result = dumps(some_point)
assert bin2hex(result) == hostorder(
"BIdd", "0101000000333333333333F33F3333333333330B40"
)
result = dumps(some_point, big_endian=False)
assert bin2hex(result) == "0101000000333333333333F33F3333333333330B40"
result = dumps(some_point, big_endian=True)
assert bin2hex(result) == "00000000013FF3333333333333400B333333333333"
def test_dumps_hex(some_point):
result = dumps(some_point, hex=True)
assert result == hostorder("BIdd", "0101000000333333333333F33F3333333333330B40")
def test_loads_srid():
# load a geometry which includes an srid
geom = loads(hex2bin("0101000020E6100000333333333333F33F3333333333330B40"))
assert isinstance(geom, Point)
assert geom.coords[:] == [(1.2, 3.4)]
# by default srid is not exported
result = dumps(geom)
assert bin2hex(result) == hostorder(
"BIdd", "0101000000333333333333F33F3333333333330B40"
)
# include the srid in the output
result = dumps(geom, include_srid=True)
assert bin2hex(result) == hostorder(
"BIIdd", "0101000020E6100000333333333333F33F3333333333330B40"
)
# replace geometry srid with another
result = dumps(geom, srid=27700)
assert bin2hex(result) == hostorder(
"BIIdd", "0101000020346C0000333333333333F33F3333333333330B40"
)
def test_loads_hex(some_point):
assert loads(dumps(some_point, hex=True), hex=True) == some_point
def test_dump_load_binary(some_point, tmpdir):
file = tmpdir.join("test.wkb")
with open(file, "wb") as file_pointer:
dump(some_point, file_pointer)
with open(file, "rb") as file_pointer:
restored = load(file_pointer)
assert some_point == restored
def test_dump_load_hex(some_point, tmpdir):
file = tmpdir.join("test.wkb")
with open(file, "w") as file_pointer:
dump(some_point, file_pointer, hex=True)
with open(file) as file_pointer:
restored = load(file_pointer, hex=True)
assert some_point == restored
# pygeos handles both bytes and str
@shapely20_todo
def test_dump_hex_load_binary(some_point, tmpdir):
"""Asserts that reading a binary file as text (hex mode) fails."""
file = tmpdir.join("test.wkb")
with open(file, "w") as file_pointer:
dump(some_point, file_pointer, hex=True)
with pytest.raises(TypeError):
with open(file, "rb") as file_pointer:
load(file_pointer)
def test_dump_binary_load_hex(some_point, tmpdir):
"""Asserts that reading a text file (hex mode) as binary fails."""
file = tmpdir.join("test.wkb")
with open(file, "wb") as file_pointer:
dump(some_point, file_pointer)
# TODO(shapely-2.0) on windows this doesn't seem to error with pygeos,
# but you get back a point with garbage coordinates
if sys.platform == "win32":
with open(file) as file_pointer:
restored = load(file_pointer, hex=True)
assert some_point != restored
return
with pytest.raises((UnicodeEncodeError, UnicodeDecodeError)):
with open(file) as file_pointer:
load(file_pointer, hex=True)
def test_point_empty():
g = wkt.loads("POINT EMPTY")
result = dumps(g, big_endian=False)
# Use math.isnan for second part of the WKB representation there are
# many byte representations for NaN)
assert result[: -2 * 8] == b"\x01\x01\x00\x00\x00"
coords = struct.unpack("<2d", result[-2 * 8 :])
assert len(coords) == 2
assert all(math.isnan(val) for val in coords)
def test_point_z_empty():
g = wkt.loads("POINT Z EMPTY")
assert g.wkb_hex == hostorder(
"BIddd", "0101000080000000000000F87F000000000000F87F000000000000F87F"
)
|