mapreader 1.1.0__tar.gz → 1.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mapreader-1.1.0 → mapreader-1.1.1}/PKG-INFO +4 -1
- {mapreader-1.1.0 → mapreader-1.1.1}/README.md +3 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/_version.py +3 -3
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/annotate/annotator.py +45 -49
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/classify/load_annotations.py +2 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/load/images.py +201 -20
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader.egg-info/PKG-INFO +4 -1
- {mapreader-1.1.0 → mapreader-1.1.1}/tests/test_annotator.py +10 -10
- {mapreader-1.1.0 → mapreader-1.1.1}/tests/test_sheet_downloader.py +154 -61
- {mapreader-1.1.0 → mapreader-1.1.1}/LICENSE +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/__init__.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/annotate/__init__.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/annotate/utils.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/classify/__init__.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/classify/classifier.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/classify/classifier_context.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/classify/custom_models.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/classify/datasets.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/download/__init__.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/download/data_structures.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/download/downloader.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/download/downloader_utils.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/download/sheet_downloader.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/download/tile_loading.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/download/tile_merging.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/load/__init__.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/load/geo_utils.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/load/loader.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/process/__init__.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/process/process.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/utils/__init__.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/utils/compute_and_save_stats.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader/utils/slice_parallel.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader.egg-info/SOURCES.txt +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader.egg-info/dependency_links.txt +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader.egg-info/entry_points.txt +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader.egg-info/not-zip-safe +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader.egg-info/requires.txt +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/mapreader.egg-info/top_level.txt +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/setup.cfg +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/setup.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/tests/test_geo_pipeline.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/tests/test_import.py +0 -0
- {mapreader-1.1.0 → mapreader-1.1.1}/versioneer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mapreader
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: A computer vision pipeline for the semantic exploration of maps/images at scale
|
|
5
5
|
Home-page: https://github.com/Living-with-machines/MapReader
|
|
6
6
|
Download-URL: https://github.com/Living-with-machines/MapReader/archive/refs/heads/main.zip
|
|
@@ -83,6 +83,9 @@ Requires-Dist: flake8<7.0.0,>=6.0.0; extra == "dev"
|
|
|
83
83
|
</a>
|
|
84
84
|
<a href="https://zenodo.org/badge/latestdoi/430661738"><img src="https://zenodo.org/badge/430661738.svg" alt="DOI"></a>
|
|
85
85
|
<br/>
|
|
86
|
+
<a href="https://codecov.io/github/Living-with-machines/MapReader" >
|
|
87
|
+
<img src="https://codecov.io/github/Living-with-machines/MapReader/graph/badge.svg?token=38GQ3O1GB5"/>
|
|
88
|
+
</a>
|
|
86
89
|
</p>
|
|
87
90
|
|
|
88
91
|
|
|
@@ -18,6 +18,9 @@
|
|
|
18
18
|
</a>
|
|
19
19
|
<a href="https://zenodo.org/badge/latestdoi/430661738"><img src="https://zenodo.org/badge/430661738.svg" alt="DOI"></a>
|
|
20
20
|
<br/>
|
|
21
|
+
<a href="https://codecov.io/github/Living-with-machines/MapReader" >
|
|
22
|
+
<img src="https://codecov.io/github/Living-with-machines/MapReader/graph/badge.svg?token=38GQ3O1GB5"/>
|
|
23
|
+
</a>
|
|
21
24
|
</p>
|
|
22
25
|
|
|
23
26
|
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "
|
|
11
|
+
"date": "2024-01-08T14:36:56+0000",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "1.1.
|
|
14
|
+
"full-revisionid": "8997b4d7fe7c82d5579ffd13daaa90e7253fc0c2",
|
|
15
|
+
"version": "1.1.1"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -22,8 +22,6 @@ from ..load.loader import load_patches
|
|
|
22
22
|
|
|
23
23
|
warnings.filterwarnings("ignore", category=UserWarning)
|
|
24
24
|
|
|
25
|
-
MAX_SIZE = 1000
|
|
26
|
-
|
|
27
25
|
_CENTER_LAYOUT = widgets.Layout(
|
|
28
26
|
display="flex", flex_flow="column", align_items="center"
|
|
29
27
|
)
|
|
@@ -64,8 +62,23 @@ class Annotator(pd.DataFrame):
|
|
|
64
62
|
sortby : str or None, optional
|
|
65
63
|
Name of the column to use to sort the patch DataFrame, by default None.
|
|
66
64
|
Default sort order is ``ascending=True``. Pass ``ascending=False`` keyword argument to sort in descending order.
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
ascending : bool, optional
|
|
66
|
+
Whether to sort the DataFrame in ascending order when using the ``sortby`` argument, by default True.
|
|
67
|
+
username : str or None, optional
|
|
68
|
+
Username to use when saving annotations file, by default None.
|
|
69
|
+
If not provided, a random string is generated.
|
|
70
|
+
task_name : str or None, optional
|
|
71
|
+
Name of the annotation task, by default None.
|
|
72
|
+
min_values : dict, optional
|
|
73
|
+
A dictionary consisting of column names (keys) and minimum values as floating point values (values), by default None.
|
|
74
|
+
max_values : dict, optional
|
|
75
|
+
A dictionary consisting of column names (keys) and maximum values as floating point values (values), by default None.
|
|
76
|
+
surrounding : int, optional
|
|
77
|
+
The number of surrounding images to show for context, by default 1.
|
|
78
|
+
max_size : int, optional
|
|
79
|
+
The size in pixels for the longest side to which constrain each patch image, by default 1000.
|
|
80
|
+
resize_to : int or None, optional
|
|
81
|
+
The size in pixels for the longest side to which resize each patch image, by default None.
|
|
69
82
|
|
|
70
83
|
Raises
|
|
71
84
|
------
|
|
@@ -79,21 +92,6 @@ class Annotator(pd.DataFrame):
|
|
|
79
92
|
If labels provided are not in the form of a list
|
|
80
93
|
SyntaxError
|
|
81
94
|
If labels provided are not in the form of a list
|
|
82
|
-
|
|
83
|
-
Notes
|
|
84
|
-
-----
|
|
85
|
-
|
|
86
|
-
Additional kwargs:
|
|
87
|
-
|
|
88
|
-
- ``username``: Username to use when saving annotations file. Default: Randomly generated string.
|
|
89
|
-
- ``task_name``: Name of the annotation task. Default: "task".
|
|
90
|
-
- ``min_values``: A dictionary consisting of column names (keys) and minimum values as floating point values (values). Default: {}.
|
|
91
|
-
- ``max_values``: A dictionary consisting of column names (keys) and maximum values as floating point values (values). Default: {}.
|
|
92
|
-
- ``buttons_per_row``: Number of buttons to display per row. Default: None.
|
|
93
|
-
- ``ascending``: Whether to sort the DataFrame in ascending order. Default: True.
|
|
94
|
-
- ``surrounding``: The number of surrounding images to show for context. Default: 1.
|
|
95
|
-
- ``max_size``: The size in pixels for the longest side to which constrain each patch image. Default: 1000.
|
|
96
|
-
- ``resize_to``: The size in pixels for the longest side to which resize each patch image. Default: None.
|
|
97
95
|
"""
|
|
98
96
|
|
|
99
97
|
def __init__(
|
|
@@ -111,7 +109,14 @@ class Annotator(pd.DataFrame):
|
|
|
111
109
|
auto_save: bool = True,
|
|
112
110
|
delimiter: str = ",",
|
|
113
111
|
sortby: str | None = None,
|
|
114
|
-
|
|
112
|
+
ascending: bool = True,
|
|
113
|
+
username: str | None = None,
|
|
114
|
+
task_name: str | None = None,
|
|
115
|
+
min_values: dict | None = None,
|
|
116
|
+
max_values: dict | None = None,
|
|
117
|
+
surrounding: int = 1,
|
|
118
|
+
max_size: int = 1000,
|
|
119
|
+
resize_to: int | None = None,
|
|
115
120
|
):
|
|
116
121
|
if labels is None:
|
|
117
122
|
labels = []
|
|
@@ -174,10 +179,6 @@ class Annotator(pd.DataFrame):
|
|
|
174
179
|
# Check for url column and add to patch dataframe
|
|
175
180
|
if "url" in parent_df.columns:
|
|
176
181
|
patch_df = patch_df.join(parent_df["url"], on="parent_id")
|
|
177
|
-
else:
|
|
178
|
-
raise ValueError(
|
|
179
|
-
"[ERROR] Metadata (parent data) should contain a 'url' column."
|
|
180
|
-
)
|
|
181
182
|
|
|
182
183
|
# Add label column if not present
|
|
183
184
|
if label_col not in patch_df.columns:
|
|
@@ -195,13 +196,12 @@ class Annotator(pd.DataFrame):
|
|
|
195
196
|
)
|
|
196
197
|
|
|
197
198
|
# Set up annotations file
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
"".join(
|
|
199
|
+
if not username:
|
|
200
|
+
username = "".join(
|
|
201
201
|
[random.choice(string.ascii_letters + string.digits) for n in range(30)]
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
|
|
202
|
+
)
|
|
203
|
+
if not task_name:
|
|
204
|
+
task_name = "task"
|
|
205
205
|
id = hashlib.md5(image_list.encode("utf-8")).hexdigest()
|
|
206
206
|
|
|
207
207
|
annotations_file = task_name.replace(" ", "_") + f"_#{username}#-{id}.csv"
|
|
@@ -269,9 +269,7 @@ class Annotator(pd.DataFrame):
|
|
|
269
269
|
# Sort by sortby column if provided
|
|
270
270
|
if isinstance(sortby, str):
|
|
271
271
|
if sortby in self.columns:
|
|
272
|
-
self.sort_values(
|
|
273
|
-
sortby, ascending=kwargs.get("ascending", True), inplace=True
|
|
274
|
-
)
|
|
272
|
+
self.sort_values(sortby, ascending=ascending, inplace=True)
|
|
275
273
|
else:
|
|
276
274
|
raise ValueError(f"[ERROR] {sortby} is not a column in the DataFrame.")
|
|
277
275
|
elif sortby is not None:
|
|
@@ -287,9 +285,8 @@ class Annotator(pd.DataFrame):
|
|
|
287
285
|
self.task_name = task_name
|
|
288
286
|
|
|
289
287
|
# set up for the annotator
|
|
290
|
-
self.
|
|
291
|
-
self.
|
|
292
|
-
self._max_values = kwargs.get("max_values", {}) # pixel_bounds = x0, y0, x1, y1
|
|
288
|
+
self._min_values = min_values or {}
|
|
289
|
+
self._max_values = max_values or {}
|
|
293
290
|
|
|
294
291
|
self.patch_width, self.patch_height = self.get_patch_size()
|
|
295
292
|
|
|
@@ -297,25 +294,24 @@ class Annotator(pd.DataFrame):
|
|
|
297
294
|
Path(annotations_dir).mkdir(parents=True, exist_ok=True)
|
|
298
295
|
|
|
299
296
|
# Set up standards for context display
|
|
300
|
-
self.surrounding =
|
|
301
|
-
self.max_size =
|
|
302
|
-
self.resize_to =
|
|
297
|
+
self.surrounding = surrounding
|
|
298
|
+
self.max_size = max_size
|
|
299
|
+
self.resize_to = resize_to
|
|
303
300
|
|
|
304
301
|
# set up buttons
|
|
305
302
|
self._buttons = []
|
|
306
303
|
|
|
307
304
|
# Set max buttons
|
|
308
|
-
if
|
|
309
|
-
if
|
|
310
|
-
|
|
311
|
-
self.buttons_per_row = 4
|
|
312
|
-
else:
|
|
313
|
-
self.buttons_per_row = 2
|
|
305
|
+
if (len(self._labels) % 2) == 0:
|
|
306
|
+
if len(self._labels) > 4:
|
|
307
|
+
self.buttons_per_row = 4
|
|
314
308
|
else:
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
309
|
+
self.buttons_per_row = 2
|
|
310
|
+
else:
|
|
311
|
+
if len(self._labels) == 3:
|
|
312
|
+
self.buttons_per_row = 3
|
|
313
|
+
else:
|
|
314
|
+
self.buttons_per_row = 5
|
|
319
315
|
|
|
320
316
|
# Set indices
|
|
321
317
|
self.current_index = -1
|
|
@@ -180,6 +180,8 @@ class AnnotationsLoader:
|
|
|
180
180
|
if os.path.isfile(annotations):
|
|
181
181
|
print(f'[INFO] Reading "{annotations}"')
|
|
182
182
|
annotations = pd.read_csv(annotations, sep=delimiter, index_col=0)
|
|
183
|
+
if annotations.index.name in ["name", "image_id"]:
|
|
184
|
+
annotations.reset_index(inplace=True, drop=False)
|
|
183
185
|
else:
|
|
184
186
|
raise ValueError(f'[ERROR] "{annotations}" cannot be found.')
|
|
185
187
|
|
|
@@ -7,11 +7,12 @@ except ImportError:
|
|
|
7
7
|
|
|
8
8
|
import os
|
|
9
9
|
import random
|
|
10
|
+
import re
|
|
10
11
|
import warnings
|
|
12
|
+
from ast import literal_eval
|
|
11
13
|
from glob import glob
|
|
12
14
|
from typing import Literal
|
|
13
15
|
|
|
14
|
-
import matplotlib.image as mpimg
|
|
15
16
|
import matplotlib.patches as patches
|
|
16
17
|
import matplotlib.pyplot as plt
|
|
17
18
|
import numpy as np
|
|
@@ -21,9 +22,13 @@ import rasterio
|
|
|
21
22
|
from PIL import Image, ImageStat
|
|
22
23
|
from pyproj import Transformer
|
|
23
24
|
from rasterio.plot import reshape_as_raster
|
|
24
|
-
from shapely
|
|
25
|
+
from shapely import wkt
|
|
26
|
+
from shapely.geometry import Polygon, box
|
|
25
27
|
from tqdm.auto import tqdm
|
|
26
28
|
|
|
29
|
+
from mapreader.download.data_structures import GridBoundingBox, GridIndex
|
|
30
|
+
from mapreader.download.downloader_utils import get_polygon_from_grid_bb
|
|
31
|
+
|
|
27
32
|
os.environ[
|
|
28
33
|
"USE_PYGEOS"
|
|
29
34
|
] = "0" # see here https://github.com/geopandas/geopandas/issues/2691
|
|
@@ -419,7 +424,7 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
|
|
|
419
424
|
data_series = metadata_df[metadata_df[image_id_col] == key].squeeze()
|
|
420
425
|
for column, item in data_series.items():
|
|
421
426
|
try:
|
|
422
|
-
self.images[tree_level][key][column] =
|
|
427
|
+
self.images[tree_level][key][column] = literal_eval(item)
|
|
423
428
|
except:
|
|
424
429
|
self.images[tree_level][key][column] = item
|
|
425
430
|
|
|
@@ -466,9 +471,12 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
|
|
|
466
471
|
plt.subplot(num_samples // 3 + 1, 3, i + 1)
|
|
467
472
|
img = Image.open(self.images[tree_level][image_id]["image_path"])
|
|
468
473
|
plt.title(image_id, size=8)
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
)
|
|
474
|
+
|
|
475
|
+
# check if grayscale
|
|
476
|
+
if len(img.getbands()) == 1:
|
|
477
|
+
plt.imshow(img, cmap="gray", vmin=0, vmax=255)
|
|
478
|
+
else:
|
|
479
|
+
plt.imshow(img)
|
|
472
480
|
plt.xticks([])
|
|
473
481
|
plt.yticks([])
|
|
474
482
|
|
|
@@ -509,6 +517,20 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
|
|
|
509
517
|
for image_id in image_ids:
|
|
510
518
|
self._add_shape_id(image_id=image_id)
|
|
511
519
|
|
|
520
|
+
def add_coords_from_grid_bb(self, verbose: bool = False) -> None:
|
|
521
|
+
print("[INFO] Adding coordinates, tree level: parent")
|
|
522
|
+
|
|
523
|
+
parent_list = self.list_parents()
|
|
524
|
+
|
|
525
|
+
for parent_id in parent_list:
|
|
526
|
+
if "grid_bb" not in self.parents[parent_id].keys():
|
|
527
|
+
print(
|
|
528
|
+
f"[WARNING] No grid bounding box found for {parent_id}. Suggestion: run add_metadata or add_geo_info." # noqa
|
|
529
|
+
)
|
|
530
|
+
continue
|
|
531
|
+
|
|
532
|
+
self._add_coords_from_grid_bb_id(image_id=parent_id, verbose=verbose)
|
|
533
|
+
|
|
512
534
|
def add_coord_increments(self, verbose: bool | None = False) -> None:
|
|
513
535
|
"""
|
|
514
536
|
Adds coordinate increments to each image at the parent level.
|
|
@@ -652,15 +674,42 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
|
|
|
652
674
|
tree_level = self._get_tree_level(image_id)
|
|
653
675
|
|
|
654
676
|
try:
|
|
655
|
-
|
|
677
|
+
img = Image.open(self.images[tree_level][image_id]["image_path"])
|
|
656
678
|
# shape = (hwc)
|
|
657
|
-
|
|
658
|
-
|
|
679
|
+
height = img.height
|
|
680
|
+
width = img.width
|
|
681
|
+
channels = len(img.getbands())
|
|
682
|
+
|
|
683
|
+
self.images[tree_level][image_id]["shape"] = (height, width, channels)
|
|
659
684
|
except OSError:
|
|
660
685
|
raise ValueError(
|
|
661
686
|
f'[ERROR] Problem with "{image_id}". Please either redownload or remove from list of images to load.'
|
|
662
687
|
)
|
|
663
688
|
|
|
689
|
+
def _add_coords_from_grid_bb_id(
|
|
690
|
+
self, image_id: int | str, verbose: bool = False
|
|
691
|
+
) -> None:
|
|
692
|
+
grid_bb = self.parents[image_id]["grid_bb"]
|
|
693
|
+
|
|
694
|
+
if isinstance(grid_bb, str):
|
|
695
|
+
cell1, cell2 = re.findall(r"\(.*?\)", grid_bb)
|
|
696
|
+
|
|
697
|
+
z1, x1, y1 = literal_eval(cell1)
|
|
698
|
+
z2, x2, y2 = literal_eval(cell2)
|
|
699
|
+
|
|
700
|
+
cell1 = GridIndex(x1, y1, z1)
|
|
701
|
+
cell2 = GridIndex(x2, y2, z2)
|
|
702
|
+
|
|
703
|
+
grid_bb = GridBoundingBox(cell1, cell2)
|
|
704
|
+
|
|
705
|
+
if isinstance(grid_bb, GridBoundingBox):
|
|
706
|
+
polygon = get_polygon_from_grid_bb(grid_bb)
|
|
707
|
+
coordinates = polygon.bounds
|
|
708
|
+
self.parents[image_id]["coordinates"] = coordinates
|
|
709
|
+
|
|
710
|
+
else:
|
|
711
|
+
raise ValueError(f"[ERROR] Unexpected grid_bb format for {image_id}.")
|
|
712
|
+
|
|
664
713
|
def _add_coord_increments_id(
|
|
665
714
|
self, image_id: int | str, verbose: bool | None = False
|
|
666
715
|
) -> None:
|
|
@@ -759,19 +808,19 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
|
|
|
759
808
|
self._add_coord_increments_id(parent_id)
|
|
760
809
|
|
|
761
810
|
# get min_x and min_y and pixel-wise dlon and dlat for parent image
|
|
762
|
-
parent_min_x = self.parents[
|
|
763
|
-
|
|
811
|
+
parent_min_x, parent_min_y, parent_max_x, parent_max_y = self.parents[
|
|
812
|
+
parent_id
|
|
813
|
+
]["coordinates"]
|
|
764
814
|
dlon = self.parents[parent_id]["dlon"]
|
|
765
815
|
dlat = self.parents[parent_id]["dlat"]
|
|
766
816
|
|
|
767
|
-
# get patch bounds
|
|
768
817
|
pixel_bounds = self.patches[image_id]["pixel_bounds"]
|
|
769
818
|
|
|
770
819
|
# get patch coords
|
|
771
820
|
min_x = (pixel_bounds[0] * dlon) + parent_min_x
|
|
772
|
-
min_y = (pixel_bounds[
|
|
821
|
+
min_y = parent_max_y - (pixel_bounds[3] * dlat)
|
|
773
822
|
max_x = (pixel_bounds[2] * dlon) + parent_min_x
|
|
774
|
-
max_y = (pixel_bounds[
|
|
823
|
+
max_y = parent_max_y - (pixel_bounds[1] * dlat)
|
|
775
824
|
|
|
776
825
|
self.patches[image_id]["coordinates"] = (min_x, min_y, max_x, max_y)
|
|
777
826
|
self.patches[image_id]["crs"] = self.parents[parent_id]["crs"]
|
|
@@ -837,6 +886,7 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
|
|
|
837
886
|
self._print_if_verbose(
|
|
838
887
|
f"[INFO] Reading 'coordinates' from {image_id}.", verbose
|
|
839
888
|
)
|
|
889
|
+
|
|
840
890
|
min_x, min_y, max_x, max_y = self.images[tree_level][image_id][
|
|
841
891
|
"coordinates"
|
|
842
892
|
]
|
|
@@ -1440,7 +1490,12 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
|
|
|
1440
1490
|
|
|
1441
1491
|
fig = plt.figure(figsize=figsize)
|
|
1442
1492
|
plt.axis("off")
|
|
1443
|
-
|
|
1493
|
+
|
|
1494
|
+
# check if grayscale
|
|
1495
|
+
if len(img.getbands()) == 1:
|
|
1496
|
+
plt.imshow(img, cmap="gray", vmin=0, vmax=255, zorder=1)
|
|
1497
|
+
else:
|
|
1498
|
+
plt.imshow(img, zorder=1)
|
|
1444
1499
|
|
|
1445
1500
|
if column_to_plot:
|
|
1446
1501
|
print(
|
|
@@ -1558,7 +1613,11 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
|
|
|
1558
1613
|
parent_path = parent_images[parent_id]["image_path"]
|
|
1559
1614
|
parent_image = Image.open(parent_path)
|
|
1560
1615
|
|
|
1561
|
-
|
|
1616
|
+
# check if grayscale
|
|
1617
|
+
if len(parent_image.getbands()) == 1:
|
|
1618
|
+
ax.imshow(parent_image, cmap="gray", vmin=0, vmax=255)
|
|
1619
|
+
else:
|
|
1620
|
+
ax.imshow(parent_image)
|
|
1562
1621
|
|
|
1563
1622
|
if save_kml_dir:
|
|
1564
1623
|
os.makedirs(save_kml_dir, exist_ok=True)
|
|
@@ -2076,6 +2135,116 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
|
|
|
2076
2135
|
tree_level = "parent" if bool(self.parents.get(image_id)) else "patch"
|
|
2077
2136
|
return tree_level
|
|
2078
2137
|
|
|
2138
|
+
def save_parents_as_geotiffs(
|
|
2139
|
+
self,
|
|
2140
|
+
rewrite: bool = False,
|
|
2141
|
+
verbose: bool = False,
|
|
2142
|
+
crs: str | None = None,
|
|
2143
|
+
) -> None:
|
|
2144
|
+
"""Save all parents in MapImages instance as geotiffs.
|
|
2145
|
+
|
|
2146
|
+
Parameters
|
|
2147
|
+
----------
|
|
2148
|
+
rewrite : bool, optional
|
|
2149
|
+
Whether to rewrite files if they already exist, by default False
|
|
2150
|
+
verbose : bool, optional
|
|
2151
|
+
Whether to print verbose outputs, by default False
|
|
2152
|
+
crs : str, optional
|
|
2153
|
+
The CRS of the coordinates.
|
|
2154
|
+
If None, the method will first look for ``crs`` in the parents dictionary and use those. If ``crs`` cannot be found in the dictionary, the method will use "EPSG:4326".
|
|
2155
|
+
By default None.
|
|
2156
|
+
"""
|
|
2157
|
+
|
|
2158
|
+
parents_list = self.list_parents()
|
|
2159
|
+
|
|
2160
|
+
for parent_id in tqdm(parents_list):
|
|
2161
|
+
self._save_parent_as_geotiff(parent_id, rewrite, verbose, crs)
|
|
2162
|
+
|
|
2163
|
+
def _save_parent_as_geotiff(
|
|
2164
|
+
self,
|
|
2165
|
+
parent_id: str,
|
|
2166
|
+
rewrite: bool = False,
|
|
2167
|
+
verbose: bool = False,
|
|
2168
|
+
crs: str | None = None,
|
|
2169
|
+
) -> None:
|
|
2170
|
+
"""Save a parent image as a geotiff.
|
|
2171
|
+
|
|
2172
|
+
Parameters
|
|
2173
|
+
----------
|
|
2174
|
+
parent_id : str
|
|
2175
|
+
The ID of the parent to write.
|
|
2176
|
+
rewrite : bool, optional
|
|
2177
|
+
Whether to rewrite files if they already exist, by default False
|
|
2178
|
+
verbose : bool, optional
|
|
2179
|
+
Whether to print verbose outputs, by default False
|
|
2180
|
+
crs : Optional[str], optional
|
|
2181
|
+
The CRS of the coordinates.
|
|
2182
|
+
If None, the method will first look for ``crs`` in the parents dictionary and use those. If ``crs`` cannot be found in the dictionary, the method will use "EPSG:4326".
|
|
2183
|
+
By default None.
|
|
2184
|
+
|
|
2185
|
+
Raises
|
|
2186
|
+
------
|
|
2187
|
+
ValueError
|
|
2188
|
+
If parent directory does not exist.
|
|
2189
|
+
"""
|
|
2190
|
+
|
|
2191
|
+
parent_path = self.parents[parent_id]["image_path"]
|
|
2192
|
+
parent_dir = os.path.dirname(parent_path)
|
|
2193
|
+
|
|
2194
|
+
if not os.path.exists(parent_dir):
|
|
2195
|
+
raise ValueError(f'[ERROR] Parent directory "{parent_dir}" does not exist.')
|
|
2196
|
+
|
|
2197
|
+
parent_id_no_ext = os.path.splitext(parent_id)[0]
|
|
2198
|
+
geotiff_path = f"{parent_dir}/{parent_id_no_ext}.tif"
|
|
2199
|
+
|
|
2200
|
+
self.parents[parent_id]["geotiff_path"] = geotiff_path
|
|
2201
|
+
|
|
2202
|
+
if os.path.isfile(f"{geotiff_path}"):
|
|
2203
|
+
if not rewrite:
|
|
2204
|
+
self._print_if_verbose(
|
|
2205
|
+
f"[INFO] File already exists: {geotiff_path}.", verbose
|
|
2206
|
+
)
|
|
2207
|
+
return
|
|
2208
|
+
|
|
2209
|
+
self._print_if_verbose(
|
|
2210
|
+
f"[INFO] Creating: {geotiff_path}.",
|
|
2211
|
+
verbose,
|
|
2212
|
+
)
|
|
2213
|
+
|
|
2214
|
+
if "shape" not in self.parents[parent_id].keys():
|
|
2215
|
+
self._add_shape_id(parent_id)
|
|
2216
|
+
height, width, channels = self.parents[parent_id]["shape"]
|
|
2217
|
+
|
|
2218
|
+
if "coordinates" not in self.parents[parent_id].keys():
|
|
2219
|
+
print(self.parents[parent_id].keys())
|
|
2220
|
+
raise ValueError(f"[ERROR] Cannot locate coordinates for {parent_id}")
|
|
2221
|
+
coords = self.parents[parent_id]["coordinates"]
|
|
2222
|
+
|
|
2223
|
+
if not crs:
|
|
2224
|
+
crs = self.parents[parent_id].get("crs", "EPSG:4326")
|
|
2225
|
+
|
|
2226
|
+
parent_affine = rasterio.transform.from_bounds(*coords, width, height)
|
|
2227
|
+
parent = Image.open(parent_path)
|
|
2228
|
+
|
|
2229
|
+
with rasterio.open(
|
|
2230
|
+
f"{geotiff_path}",
|
|
2231
|
+
"w",
|
|
2232
|
+
driver="GTiff",
|
|
2233
|
+
height=parent.height,
|
|
2234
|
+
width=parent.width,
|
|
2235
|
+
count=channels,
|
|
2236
|
+
transform=parent_affine,
|
|
2237
|
+
dtype="uint8",
|
|
2238
|
+
nodata=0,
|
|
2239
|
+
crs=crs,
|
|
2240
|
+
) as dst:
|
|
2241
|
+
if len(parent.getbands()) == 1:
|
|
2242
|
+
parent_array = np.array(parent)
|
|
2243
|
+
dst.write(parent_array, indexes=1)
|
|
2244
|
+
else:
|
|
2245
|
+
parent_array = reshape_as_raster(parent)
|
|
2246
|
+
dst.write(parent_array)
|
|
2247
|
+
|
|
2079
2248
|
def save_patches_as_geotiffs(
|
|
2080
2249
|
self,
|
|
2081
2250
|
rewrite: bool | None = False,
|
|
@@ -2152,10 +2321,12 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
|
|
|
2152
2321
|
verbose,
|
|
2153
2322
|
)
|
|
2154
2323
|
|
|
2324
|
+
# get shape
|
|
2155
2325
|
if "shape" not in self.patches[patch_id].keys():
|
|
2156
2326
|
self._add_shape_id(patch_id)
|
|
2157
2327
|
height, width, channels = self.patches[patch_id]["shape"]
|
|
2158
2328
|
|
|
2329
|
+
# get coords
|
|
2159
2330
|
if "coordinates" not in self.patches[patch_id].keys():
|
|
2160
2331
|
self._add_patch_coords_id(patch_id)
|
|
2161
2332
|
coords = self.patches[patch_id]["coordinates"]
|
|
@@ -2165,7 +2336,6 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
|
|
|
2165
2336
|
|
|
2166
2337
|
patch_affine = rasterio.transform.from_bounds(*coords, width, height)
|
|
2167
2338
|
patch = Image.open(patch_path)
|
|
2168
|
-
patch_array = reshape_as_raster(patch)
|
|
2169
2339
|
|
|
2170
2340
|
with rasterio.open(
|
|
2171
2341
|
f"{geotiff_path}",
|
|
@@ -2179,7 +2349,12 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
|
|
|
2179
2349
|
nodata=0,
|
|
2180
2350
|
crs=crs,
|
|
2181
2351
|
) as dst:
|
|
2182
|
-
|
|
2352
|
+
if len(patch.getbands()) == 1:
|
|
2353
|
+
patch_array = np.array(patch)
|
|
2354
|
+
dst.write(patch_array, indexes=1)
|
|
2355
|
+
else:
|
|
2356
|
+
patch_array = reshape_as_raster(patch)
|
|
2357
|
+
dst.write(patch_array)
|
|
2183
2358
|
|
|
2184
2359
|
def save_patches_to_geojson(
|
|
2185
2360
|
self,
|
|
@@ -2213,6 +2388,10 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
|
|
|
2213
2388
|
self.add_patch_polygons()
|
|
2214
2389
|
_, patch_df = self.convert_images()
|
|
2215
2390
|
|
|
2391
|
+
patch_df["polygon"] = patch_df["polygon"].apply(
|
|
2392
|
+
lambda x: x if isinstance(x, Polygon) else wkt.loads(x)
|
|
2393
|
+
)
|
|
2394
|
+
|
|
2216
2395
|
if not crs:
|
|
2217
2396
|
if "crs" in patch_df.columns:
|
|
2218
2397
|
if len(patch_df["crs"].unique()) == 1:
|
|
@@ -2220,14 +2399,16 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
|
|
|
2220
2399
|
else:
|
|
2221
2400
|
crs = "EPSG:4326"
|
|
2222
2401
|
|
|
2402
|
+
if "image_id" in patch_df.columns:
|
|
2403
|
+
patch_df.drop(columns=["image_id"], inplace=True)
|
|
2223
2404
|
patch_df.reset_index(names="image_id", inplace=True)
|
|
2224
2405
|
|
|
2225
2406
|
# drop pixel stats columns
|
|
2226
2407
|
patch_df.drop(columns=patch_df.filter(like="pixel", axis=1), inplace=True)
|
|
2227
|
-
#
|
|
2408
|
+
# change tuple columns to strings
|
|
2228
2409
|
for col in patch_df.columns:
|
|
2229
2410
|
if isinstance(patch_df[col][0], tuple):
|
|
2230
|
-
patch_df
|
|
2411
|
+
patch_df[col] = patch_df[col].apply(str)
|
|
2231
2412
|
|
|
2232
2413
|
geo_patch_df = geopd.GeoDataFrame(patch_df, geometry="polygon", crs=crs)
|
|
2233
2414
|
geo_patch_df.to_file(geojson_fname, driver="GeoJSON")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mapreader
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: A computer vision pipeline for the semantic exploration of maps/images at scale
|
|
5
5
|
Home-page: https://github.com/Living-with-machines/MapReader
|
|
6
6
|
Download-URL: https://github.com/Living-with-machines/MapReader/archive/refs/heads/main.zip
|
|
@@ -83,6 +83,9 @@ Requires-Dist: flake8<7.0.0,>=6.0.0; extra == "dev"
|
|
|
83
83
|
</a>
|
|
84
84
|
<a href="https://zenodo.org/badge/latestdoi/430661738"><img src="https://zenodo.org/badge/430661738.svg" alt="DOI"></a>
|
|
85
85
|
<br/>
|
|
86
|
+
<a href="https://codecov.io/github/Living-with-machines/MapReader" >
|
|
87
|
+
<img src="https://codecov.io/github/Living-with-machines/MapReader/graph/badge.svg?token=38GQ3O1GB5"/>
|
|
88
|
+
</a>
|
|
86
89
|
</p>
|
|
87
90
|
|
|
88
91
|
|
|
@@ -211,16 +211,6 @@ def test_init_dfs_value_error(load_dfs):
|
|
|
211
211
|
)
|
|
212
212
|
|
|
213
213
|
|
|
214
|
-
def test_no_url_col(load_dfs):
|
|
215
|
-
parent_df, patch_df, _ = load_dfs
|
|
216
|
-
parent_df = parent_df.drop(columns=["url"])
|
|
217
|
-
with pytest.raises(ValueError, match="should contain a 'url' column"):
|
|
218
|
-
Annotator(
|
|
219
|
-
patch_df=patch_df,
|
|
220
|
-
parent_df=parent_df,
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
|
|
224
214
|
def test_no_image_path_col(load_dfs):
|
|
225
215
|
parent_df, patch_df, _ = load_dfs
|
|
226
216
|
patch_df = patch_df.drop(columns=["image_path"])
|
|
@@ -255,3 +245,13 @@ def test_fpaths_metadata_filenotfound_error(load_dfs, sample_dir):
|
|
|
255
245
|
parent_paths=f"{sample_dir}/cropped_74488689.png",
|
|
256
246
|
metadata_path="fake_df.csv",
|
|
257
247
|
)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def test_unknown_arg_error(load_dfs):
|
|
251
|
+
parent_df, patch_df, _ = load_dfs
|
|
252
|
+
with pytest.raises(TypeError):
|
|
253
|
+
Annotator(
|
|
254
|
+
patch_df=patch_df,
|
|
255
|
+
parent_df=parent_df,
|
|
256
|
+
fake_arg=1,
|
|
257
|
+
)
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
from ast import literal_eval
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
7
|
+
import pandas as pd
|
|
6
8
|
import pytest
|
|
9
|
+
from pytest import approx
|
|
7
10
|
from shapely.geometry import LineString, MultiPolygon, Polygon
|
|
8
11
|
|
|
9
12
|
from mapreader import SheetDownloader
|
|
@@ -286,11 +289,54 @@ def test_download_all(sheet_downloader, tmp_path):
|
|
|
286
289
|
sd.download_all_map_sheets(maps_path, metadata_fname)
|
|
287
290
|
assert os.path.exists(f"{maps_path}/map_102352861.png")
|
|
288
291
|
assert os.path.exists(f"{maps_path}/{metadata_fname}")
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
assert
|
|
292
|
-
|
|
293
|
-
|
|
292
|
+
df = pd.read_csv(f"{maps_path}/{metadata_fname}", sep=",", index_col=0)
|
|
293
|
+
assert len(df) == 4
|
|
294
|
+
assert list(df.columns) == [
|
|
295
|
+
"name",
|
|
296
|
+
"url",
|
|
297
|
+
"coordinates",
|
|
298
|
+
"crs",
|
|
299
|
+
"published_date",
|
|
300
|
+
"grid_bb",
|
|
301
|
+
]
|
|
302
|
+
assert list(df["name"]) == [
|
|
303
|
+
"map_101602026.png",
|
|
304
|
+
"map_101602038.png",
|
|
305
|
+
"map_102352861.png",
|
|
306
|
+
"map_91617032.png",
|
|
307
|
+
]
|
|
308
|
+
# test coords
|
|
309
|
+
assert literal_eval(df.loc[0, "coordinates"]) == approx(
|
|
310
|
+
(-1.0546875, 53.33087298301705, -0.703125, 53.54030739150021), rel=1e-6
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
sd.get_grid_bb(14)
|
|
314
|
+
maps_path = tmp_path / "test_maps_14/"
|
|
315
|
+
metadata_fname = "test_metadata.csv"
|
|
316
|
+
sd.download_all_map_sheets(maps_path, metadata_fname)
|
|
317
|
+
assert os.path.exists(f"{maps_path}/map_102352861.png")
|
|
318
|
+
assert os.path.exists(f"{maps_path}/{metadata_fname}")
|
|
319
|
+
df = pd.read_csv(f"{maps_path}/{metadata_fname}", sep=",", index_col=0)
|
|
320
|
+
assert len(df) == 4
|
|
321
|
+
assert list(df.columns) == [
|
|
322
|
+
"name",
|
|
323
|
+
"url",
|
|
324
|
+
"coordinates",
|
|
325
|
+
"crs",
|
|
326
|
+
"published_date",
|
|
327
|
+
"grid_bb",
|
|
328
|
+
]
|
|
329
|
+
assert list(df["name"]) == [
|
|
330
|
+
"map_101602026.png",
|
|
331
|
+
"map_101602038.png",
|
|
332
|
+
"map_102352861.png",
|
|
333
|
+
"map_91617032.png",
|
|
334
|
+
]
|
|
335
|
+
# test coords
|
|
336
|
+
assert literal_eval(df.loc[0, "coordinates"]) == approx(
|
|
337
|
+
(-0.98876953125, 53.448806835427575, -0.90087890625, 53.48804553605621),
|
|
338
|
+
rel=1e-6,
|
|
339
|
+
)
|
|
294
340
|
|
|
295
341
|
|
|
296
342
|
def test_download_all_kwargs(sheet_downloader, tmp_path):
|
|
@@ -304,12 +350,27 @@ def test_download_all_kwargs(sheet_downloader, tmp_path):
|
|
|
304
350
|
}
|
|
305
351
|
sd.download_all_map_sheets(maps_path, metadata_fname, **kwargs)
|
|
306
352
|
assert os.path.exists(f"{maps_path}/{metadata_fname}")
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
assert
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
353
|
+
df = pd.read_csv(f"{maps_path}/{metadata_fname}", sep=",", index_col=0)
|
|
354
|
+
assert len(df) == 4
|
|
355
|
+
assert list(df.columns) == [
|
|
356
|
+
"name",
|
|
357
|
+
"url",
|
|
358
|
+
"coordinates",
|
|
359
|
+
"crs",
|
|
360
|
+
"published_date",
|
|
361
|
+
"grid_bb",
|
|
362
|
+
"test1",
|
|
363
|
+
"test2",
|
|
364
|
+
]
|
|
365
|
+
assert list(df["name"]) == [
|
|
366
|
+
"map_101602026.png",
|
|
367
|
+
"map_101602038.png",
|
|
368
|
+
"map_102352861.png",
|
|
369
|
+
"map_91617032.png",
|
|
370
|
+
]
|
|
371
|
+
assert df.loc[3, "published_date"] == 2021
|
|
372
|
+
assert df.loc[3, "test1"] == "test"
|
|
373
|
+
assert df.loc[3, "test2"] == "Six_Inch_GB_WFS.132"
|
|
313
374
|
|
|
314
375
|
|
|
315
376
|
def test_download_by_wfs_ids(sheet_downloader, tmp_path):
|
|
@@ -322,27 +383,32 @@ def test_download_by_wfs_ids(sheet_downloader, tmp_path):
|
|
|
322
383
|
) # test single wfs_id
|
|
323
384
|
assert os.path.exists(f"{maps_path}/map_101602026.png")
|
|
324
385
|
assert os.path.exists(f"{maps_path}/{metadata_fname}")
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
assert
|
|
328
|
-
|
|
329
|
-
|
|
386
|
+
df = pd.read_csv(f"{maps_path}/{metadata_fname}", sep=",", index_col=0)
|
|
387
|
+
assert len(df) == 1
|
|
388
|
+
assert list(df.columns) == [
|
|
389
|
+
"name",
|
|
390
|
+
"url",
|
|
391
|
+
"coordinates",
|
|
392
|
+
"crs",
|
|
393
|
+
"published_date",
|
|
394
|
+
"grid_bb",
|
|
395
|
+
]
|
|
396
|
+
assert df.loc[0, "name"] == "map_101602026.png"
|
|
330
397
|
|
|
331
398
|
sd.download_map_sheets_by_wfs_ids(
|
|
332
399
|
[16320, 16321], maps_path, metadata_fname
|
|
333
400
|
) # test list of wfs_ids
|
|
334
401
|
assert os.path.exists(f"{maps_path}/map_101602038.png")
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
assert
|
|
338
|
-
|
|
402
|
+
df = pd.read_csv(f"{maps_path}/{metadata_fname}", sep=",", index_col=0)
|
|
403
|
+
assert len(df) == 2 # should have only downloaded/added one extra map
|
|
404
|
+
assert df.loc[1, "name"] == "map_101602038.png"
|
|
339
405
|
sd.download_map_sheets_by_wfs_ids(
|
|
340
406
|
16320, maps_path, metadata_fname, overwrite=True
|
|
341
407
|
) # test overwrite
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
assert
|
|
345
|
-
assert
|
|
408
|
+
df = pd.read_csv(f"{maps_path}/{metadata_fname}", sep=",", index_col=0)
|
|
409
|
+
assert len(df) == 2
|
|
410
|
+
assert df.loc[0, "name"] == "map_101602026.png"
|
|
411
|
+
assert df.loc[1, "name"] == "map_101602038.png"
|
|
346
412
|
|
|
347
413
|
|
|
348
414
|
def test_download_by_wfs_ids_errors(sheet_downloader, tmp_path):
|
|
@@ -378,19 +444,25 @@ def test_download_by_polygon(sheet_downloader, tmp_path):
|
|
|
378
444
|
) # test mode = 'within'
|
|
379
445
|
assert os.path.exists(f"{maps_path}/map_101602026.png")
|
|
380
446
|
assert os.path.exists(f"{maps_path}/{metadata_fname}")
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
assert
|
|
384
|
-
|
|
385
|
-
|
|
447
|
+
df = pd.read_csv(f"{maps_path}/{metadata_fname}", sep=",", index_col=0)
|
|
448
|
+
assert len(df) == 1
|
|
449
|
+
assert list(df.columns) == [
|
|
450
|
+
"name",
|
|
451
|
+
"url",
|
|
452
|
+
"coordinates",
|
|
453
|
+
"crs",
|
|
454
|
+
"published_date",
|
|
455
|
+
"grid_bb",
|
|
456
|
+
]
|
|
457
|
+
assert df.loc[0, "name"] == "map_101602026.png"
|
|
386
458
|
|
|
387
459
|
sd.download_map_sheets_by_polygon(
|
|
388
460
|
polygon, maps_path, metadata_fname, mode="intersects"
|
|
389
461
|
) # test mode = 'intersects', now 2 maps
|
|
390
462
|
assert os.path.exists(f"{maps_path}/map_101602038.png")
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
assert
|
|
463
|
+
df = pd.read_csv(f"{maps_path}/{metadata_fname}", sep=",", index_col=0)
|
|
464
|
+
assert len(df) == 2 # should have only downloaded/added one extra map
|
|
465
|
+
assert df.loc[1, "name"] == "map_101602038.png"
|
|
394
466
|
|
|
395
467
|
|
|
396
468
|
def test_download_by_polygon_errors(sheet_downloader, tmp_path):
|
|
@@ -417,11 +489,17 @@ def test_download_by_coords(sheet_downloader, tmp_path):
|
|
|
417
489
|
sd.download_map_sheets_by_coordinates((-0.99, 53.43), maps_path, metadata_fname)
|
|
418
490
|
assert os.path.exists(f"{maps_path}/map_101602038.png")
|
|
419
491
|
assert os.path.exists(f"{maps_path}/{metadata_fname}")
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
assert
|
|
423
|
-
|
|
424
|
-
|
|
492
|
+
df = pd.read_csv(f"{maps_path}/{metadata_fname}", sep=",", index_col=0)
|
|
493
|
+
assert len(df) == 1
|
|
494
|
+
assert list(df.columns) == [
|
|
495
|
+
"name",
|
|
496
|
+
"url",
|
|
497
|
+
"coordinates",
|
|
498
|
+
"crs",
|
|
499
|
+
"published_date",
|
|
500
|
+
"grid_bb",
|
|
501
|
+
]
|
|
502
|
+
assert df.loc[0, "name"] == "map_101602038.png"
|
|
425
503
|
|
|
426
504
|
|
|
427
505
|
def test_download_by_coords_errors(sheet_downloader, tmp_path):
|
|
@@ -442,11 +520,17 @@ def test_download_by_line(sheet_downloader, tmp_path):
|
|
|
442
520
|
sd.download_map_sheets_by_line(line, maps_path, metadata_fname)
|
|
443
521
|
assert os.path.exists(f"{maps_path}/map_101602026.png")
|
|
444
522
|
assert os.path.exists(f"{maps_path}/{metadata_fname}")
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
assert
|
|
448
|
-
|
|
449
|
-
|
|
523
|
+
df = pd.read_csv(f"{maps_path}/{metadata_fname}", sep=",", index_col=0)
|
|
524
|
+
assert len(df) == 2
|
|
525
|
+
assert list(df.columns) == [
|
|
526
|
+
"name",
|
|
527
|
+
"url",
|
|
528
|
+
"coordinates",
|
|
529
|
+
"crs",
|
|
530
|
+
"published_date",
|
|
531
|
+
"grid_bb",
|
|
532
|
+
]
|
|
533
|
+
assert list(df["name"]) == ["map_101602026.png", "map_101602038.png"]
|
|
450
534
|
|
|
451
535
|
|
|
452
536
|
def test_download_by_line_errors(sheet_downloader, tmp_path):
|
|
@@ -469,30 +553,34 @@ def test_download_by_string(sheet_downloader, tmp_path):
|
|
|
469
553
|
) # test w/ keys list
|
|
470
554
|
assert os.path.exists(f"{maps_path}/map_91617032.png")
|
|
471
555
|
assert os.path.exists(f"{maps_path}/{metadata_fname}")
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
assert
|
|
475
|
-
|
|
476
|
-
|
|
556
|
+
df = pd.read_csv(f"{maps_path}/{metadata_fname}", sep=",", index_col=0)
|
|
557
|
+
assert len(df) == 1
|
|
558
|
+
assert list(df.columns) == [
|
|
559
|
+
"name",
|
|
560
|
+
"url",
|
|
561
|
+
"coordinates",
|
|
562
|
+
"crs",
|
|
563
|
+
"published_date",
|
|
564
|
+
"grid_bb",
|
|
565
|
+
]
|
|
566
|
+
assert df.loc[0, "name"] == "map_91617032.png"
|
|
477
567
|
|
|
478
568
|
sd.download_map_sheets_by_string(
|
|
479
569
|
"Six_Inch_GB_WFS.16320", "id", maps_path, metadata_fname
|
|
480
570
|
) # test append + w/ keys as string
|
|
481
571
|
assert os.path.exists(f"{maps_path}/map_101602026.png")
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
assert
|
|
485
|
-
assert csv[2].startswith("1,map_101602026.png")
|
|
572
|
+
df = pd.read_csv(f"{maps_path}/{metadata_fname}", sep=",", index_col=0)
|
|
573
|
+
assert len(df) == 2
|
|
574
|
+
assert df.loc[1, "name"] == "map_101602026.png"
|
|
486
575
|
|
|
487
576
|
sd.download_map_sheets_by_string(
|
|
488
577
|
"III.SW", path_save=maps_path, metadata_fname=metadata_fname
|
|
489
578
|
) # test w/ no keys
|
|
490
579
|
assert os.path.exists(f"{maps_path}/map_101602038.png")
|
|
491
580
|
assert os.path.exists(f"{maps_path}/{metadata_fname}")
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
assert
|
|
495
|
-
assert csv[3].startswith("2,map_101602038.png")
|
|
581
|
+
df = pd.read_csv(f"{maps_path}/{metadata_fname}", sep=",", index_col=0)
|
|
582
|
+
assert len(df) == 3
|
|
583
|
+
assert df.loc[2, "name"] == "map_101602038.png"
|
|
496
584
|
|
|
497
585
|
|
|
498
586
|
def test_download_by_string_value_errors(sheet_downloader, tmp_path):
|
|
@@ -531,12 +619,17 @@ def test_download_by_queries(sheet_downloader, tmp_path):
|
|
|
531
619
|
assert os.path.exists(f"{maps_path}/map_101602026.png")
|
|
532
620
|
assert os.path.exists(f"{maps_path}/map_91617032.png")
|
|
533
621
|
assert os.path.exists(f"{maps_path}/{metadata_fname}")
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
assert
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
622
|
+
df = pd.read_csv(f"{maps_path}/{metadata_fname}", sep=",", index_col=0)
|
|
623
|
+
assert len(df) == 2
|
|
624
|
+
assert list(df.columns) == [
|
|
625
|
+
"name",
|
|
626
|
+
"url",
|
|
627
|
+
"coordinates",
|
|
628
|
+
"crs",
|
|
629
|
+
"published_date",
|
|
630
|
+
"grid_bb",
|
|
631
|
+
]
|
|
632
|
+
assert list(df["name"]) == ["map_101602026.png", "map_91617032.png"]
|
|
540
633
|
|
|
541
634
|
|
|
542
635
|
def test_download_by_queries_errors(sheet_downloader, tmp_path):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|