File: test_wkb.py

package info (click to toggle)
python-shapely 2.1.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 2,528 kB
  • sloc: python: 18,648; ansic: 6,615; makefile: 88; sh: 62
file content (176 lines) | stat: -rw-r--r-- 5,558 bytes parent folder | download | duplicates (3)
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"
    )