mapreader 1.1.4__tar.gz → 1.2.0__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.
Files changed (45) hide show
  1. {mapreader-1.1.4 → mapreader-1.2.0}/PKG-INFO +11 -9
  2. {mapreader-1.1.4 → mapreader-1.2.0}/README.md +1 -1
  3. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/_version.py +3 -3
  4. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/annotate/utils.py +1 -1
  5. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/classify/classifier.py +3 -2
  6. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/download/downloader.py +18 -7
  7. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/download/sheet_downloader.py +73 -10
  8. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/load/geo_utils.py +3 -5
  9. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/load/images.py +121 -19
  10. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader.egg-info/PKG-INFO +11 -9
  11. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader.egg-info/requires.txt +8 -6
  12. {mapreader-1.1.4 → mapreader-1.2.0}/setup.py +10 -9
  13. {mapreader-1.1.4 → mapreader-1.2.0}/LICENSE +0 -0
  14. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/__init__.py +0 -0
  15. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/annotate/__init__.py +0 -0
  16. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/annotate/annotator.py +0 -0
  17. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/classify/__init__.py +0 -0
  18. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/classify/custom_models.py +0 -0
  19. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/classify/datasets.py +0 -0
  20. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/classify/load_annotations.py +0 -0
  21. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/download/__init__.py +0 -0
  22. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/download/data_structures.py +0 -0
  23. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/download/downloader_utils.py +0 -0
  24. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/download/tile_loading.py +0 -0
  25. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/download/tile_merging.py +0 -0
  26. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/load/__init__.py +0 -0
  27. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/load/loader.py +0 -0
  28. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/process/__init__.py +0 -0
  29. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/process/post_process.py +0 -0
  30. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/process/process.py +0 -0
  31. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/utils/__init__.py +0 -0
  32. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/utils/compute_and_save_stats.py +0 -0
  33. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader/utils/slice_parallel.py +0 -0
  34. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader.egg-info/SOURCES.txt +0 -0
  35. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader.egg-info/dependency_links.txt +0 -0
  36. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader.egg-info/entry_points.txt +0 -0
  37. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader.egg-info/not-zip-safe +0 -0
  38. {mapreader-1.1.4 → mapreader-1.2.0}/mapreader.egg-info/top_level.txt +0 -0
  39. {mapreader-1.1.4 → mapreader-1.2.0}/setup.cfg +0 -0
  40. {mapreader-1.1.4 → mapreader-1.2.0}/tests/test_annotator.py +0 -0
  41. {mapreader-1.1.4 → mapreader-1.2.0}/tests/test_geo_pipeline.py +0 -0
  42. {mapreader-1.1.4 → mapreader-1.2.0}/tests/test_import.py +0 -0
  43. {mapreader-1.1.4 → mapreader-1.2.0}/tests/test_post_processing.py +0 -0
  44. {mapreader-1.1.4 → mapreader-1.2.0}/tests/test_sheet_downloader.py +0 -0
  45. {mapreader-1.1.4 → mapreader-1.2.0}/versioneer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mapreader
3
- Version: 1.1.4
3
+ Version: 1.2.0
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
@@ -24,7 +24,7 @@ Classifier: Operating System :: OS Independent
24
24
  Classifier: Topic :: Software Development
25
25
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
26
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
27
- Requires-Python: >=3.7, <3.11
27
+ Requires-Python: >=3.9
28
28
  Description-Content-Type: text/markdown
29
29
  License-File: LICENSE
30
30
  Requires-Dist: matplotlib<4.0.0,>=3.5.0
@@ -37,8 +37,8 @@ Requires-Dist: Shapely<3.0.0,>=2.0.0
37
37
  Requires-Dist: nest-asyncio<2.0.0,>=1.5.1
38
38
  Requires-Dist: scikit-image>=0.18.3
39
39
  Requires-Dist: scikit-learn<2.0.0,>=1.0.1
40
- Requires-Dist: torch<2.0.0,>=1.10.0
41
- Requires-Dist: torchvision<0.12.1,>=0.11.1
40
+ Requires-Dist: torch>=1.10.0
41
+ Requires-Dist: torchvision<0.17.3,>=0.11.1
42
42
  Requires-Dist: jupyter<2.0.0,>=1.0.0
43
43
  Requires-Dist: ipykernel<7.0.0,>=6.5.1
44
44
  Requires-Dist: ipywidgets<9.0.0,>=8.0.0
@@ -55,13 +55,15 @@ Requires-Dist: tqdm<5.0.0
55
55
  Requires-Dist: torchinfo<2.0.0
56
56
  Requires-Dist: openpyxl<4.0.0
57
57
  Requires-Dist: geopandas<1.0.0
58
+ Requires-Dist: pyogrio>=0.7.2
59
+ Requires-Dist: cartopy>=0.22.0
58
60
  Provides-Extra: dev
59
- Requires-Dist: pytest<8.0.0; extra == "dev"
60
- Requires-Dist: pytest-cov<5.0.0,>=4.1.0; extra == "dev"
61
+ Requires-Dist: pytest<9.0.0; extra == "dev"
62
+ Requires-Dist: pytest-cov<6.0.0,>=4.1.0; extra == "dev"
61
63
  Requires-Dist: timm<1.0.0; extra == "dev"
62
64
  Requires-Dist: transformers<5.0.0; extra == "dev"
63
- Requires-Dist: black<24.0.0,>=23.7.0; extra == "dev"
64
- Requires-Dist: flake8<7.0.0,>=6.0.0; extra == "dev"
65
+ Requires-Dist: black<25.0.0,>=23.7.0; extra == "dev"
66
+ Requires-Dist: flake8<8.0.0,>=6.0.0; extra == "dev"
65
67
 
66
68
  <div align="center">
67
69
  <br>
@@ -132,7 +134,7 @@ The MapReader documentation can be found at https://mapreader.readthedocs.io/en/
132
134
  - Geospatial images (i.e. maps)
133
135
  - Non-geospatial images
134
136
 
135
- **Developers and contributors** may also want to refer to the [API documentation](https://mapreader.readthedocs.io/en/latest/api/index.html) and [Contribution guide](https://mapreader.readthedocs.io/en/latest/Contribution-guide.html) for guidance on how to contribute to the MapReader package.
137
+ **Developers and contributors** may also want to refer to the [API documentation](https://mapreader.readthedocs.io/en/latest/api/index.html) and [Contribution guide](https://mapreader.readthedocs.io/en/latest/Contribution-guide/Contribution-guide.html) for guidance on how to contribute to the MapReader package.
136
138
 
137
139
  **Join our Slack workspace!**
138
140
  Please fill out [this form](https://forms.gle/dXjECHZQkwrZ3Xpt9) to receive an invitation to the Slack workspace.
@@ -67,7 +67,7 @@ The MapReader documentation can be found at https://mapreader.readthedocs.io/en/
67
67
  - Geospatial images (i.e. maps)
68
68
  - Non-geospatial images
69
69
 
70
- **Developers and contributors** may also want to refer to the [API documentation](https://mapreader.readthedocs.io/en/latest/api/index.html) and [Contribution guide](https://mapreader.readthedocs.io/en/latest/Contribution-guide.html) for guidance on how to contribute to the MapReader package.
70
+ **Developers and contributors** may also want to refer to the [API documentation](https://mapreader.readthedocs.io/en/latest/api/index.html) and [Contribution guide](https://mapreader.readthedocs.io/en/latest/Contribution-guide/Contribution-guide.html) for guidance on how to contribute to the MapReader package.
71
71
 
72
72
  **Join our Slack workspace!**
73
73
  Please fill out [this form](https://forms.gle/dXjECHZQkwrZ3Xpt9) to receive an invitation to the Slack workspace.
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-02-22T16:00:21+0000",
11
+ "date": "2024-04-08T09:00:00+0100",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "4ae6026f4694f1995359fd77a26daf042f64e32a",
15
- "version": "1.1.4"
14
+ "full-revisionid": "e9d119ffa6946e723f7977ddd7add0c9bcce34d8",
15
+ "version": "1.2.0"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -95,7 +95,7 @@ def prepare_data(
95
95
  ).values
96
96
  if random_state in ["random"]:
97
97
  df = df.groupby("pixel_groups").sample(
98
- n=10, random_state=random.randint(0, 1e6)
98
+ n=10, random_state=random.randint(0, int(1e6))
99
99
  )
100
100
  else:
101
101
  df = df.groupby("pixel_groups").sample(n=10, random_state=random_state)
@@ -7,8 +7,9 @@ import random
7
7
  import socket
8
8
  import sys
9
9
  import time
10
+ from collections.abc import Hashable, Iterable
10
11
  from datetime import datetime
11
- from typing import Any, Hashable, Iterable
12
+ from typing import Any
12
13
 
13
14
  import joblib
14
15
  import matplotlib.pyplot as plt
@@ -167,7 +168,7 @@ class ClassifierContainer:
167
168
  if not os.path.exists("./tmp_checkpoints"):
168
169
  os.makedirs("./tmp_checkpoints")
169
170
  self.tmp_save_filename = (
170
- f"./tmp_checkpoints/tmp_{random.randint(0, 1e10)}_checkpoint.pkl"
171
+ f"./tmp_checkpoints/tmp_{random.randint(0, int(1e10))}_checkpoint.pkl"
171
172
  )
172
173
 
173
174
  # add colors for printing/logging
@@ -61,20 +61,24 @@ class Downloader:
61
61
  """
62
62
  self.merger = TileMerger(output_folder=path_save, show_progress=False)
63
63
 
64
- def _check_map_exists(self, grid_bb: GridBoundingBox) -> bool:
64
+ def _check_map_exists(self, grid_bb: GridBoundingBox, map_name: str | None) -> bool:
65
65
  """
66
66
  Checks if a map is already saved.
67
67
 
68
68
  Parameters
69
69
  ----------
70
70
  grid_bb : GridBoundingBox
71
+ The grid bounding box of the map
72
+ map_name : str, optional
73
+ Name to use when saving the map, by default None
71
74
 
72
75
  Returns
73
76
  -------
74
77
  bool
75
78
  True if file exists, False if not.
76
79
  """
77
- map_name = self.merger._get_output_name(grid_bb)
80
+ if map_name is None:
81
+ map_name = self.merger._get_output_name(grid_bb)
78
82
  path_save = self.merger.output_folder
79
83
  if os.path.exists(f"{path_save}{map_name}.png"):
80
84
  print(
@@ -83,22 +87,26 @@ class Downloader:
83
87
  return True
84
88
  return False
85
89
 
86
- def _download_map(self, grid_bb: GridBoundingBox) -> bool:
90
+ def _download_map(self, grid_bb: GridBoundingBox, map_name: str | None) -> bool:
87
91
  """
88
92
  Downloads a map contained within a grid bounding box.
89
93
 
90
94
  Parameters
91
95
  ----------
92
96
  grid_bb : GridBoundingBox
97
+ The grid bounding box of the map
98
+ map_name : str, optional
99
+ Name to use when saving the map, by default None
93
100
 
94
101
  Returns
95
102
  -------
96
103
  bool
97
104
  True if map was successfully downloaded, False if not.
98
105
  """
99
- map_name = self.merger._get_output_name(grid_bb)
106
+ if map_name is None:
107
+ map_name = self.merger._get_output_name(grid_bb)
100
108
  self.downloader.download_tiles(grid_bb, download_in_parallel=False)
101
- success = self.merger.merge(grid_bb)
109
+ success = self.merger.merge(grid_bb, map_name)
102
110
  if success:
103
111
  print(f'[INFO] Downloaded "{map_name}.png"')
104
112
  else:
@@ -113,6 +121,7 @@ class Downloader:
113
121
  zoom_level: int | None = 14,
114
122
  path_save: str | None = "maps",
115
123
  overwrite: bool | None = False,
124
+ map_name: str | None = None,
116
125
  ) -> None:
117
126
  """
118
127
  Downloads a map contained within a polygon.
@@ -127,6 +136,8 @@ class Downloader:
127
136
  Path to save map sheets, by default "maps"
128
137
  overwrite : bool, optional
129
138
  Whether to overwrite existing maps, by default ``False``.
139
+ map_name : str, optional
140
+ Name to use when saving the map, by default None
130
141
  """
131
142
 
132
143
  assert isinstance(
@@ -147,6 +158,6 @@ Please pass polygon as shapely.geometry.Polygon object.\n\
147
158
  self._initialise_downloader()
148
159
  self._initialise_merger(f"{path_save}/")
149
160
  if not overwrite:
150
- if self._check_map_exists(grid_bb):
161
+ if self._check_map_exists(grid_bb, map_name):
151
162
  return
152
- self._download_map(grid_bb)
163
+ self._download_map(grid_bb, map_name)
@@ -559,7 +559,7 @@ class SheetDownloader:
559
559
  return False
560
560
  return False
561
561
 
562
- def _download_map(self, feature: dict) -> bool:
562
+ def _download_map(self, feature: dict, download_in_parallel: bool = True) -> bool:
563
563
  """
564
564
  Downloads a single map sheet and saves as png file.
565
565
 
@@ -573,7 +573,9 @@ class SheetDownloader:
573
573
  True if map was downloaded successfully, False if not.
574
574
  """
575
575
  map_name = str("map_" + feature["properties"]["IMAGE"])
576
- self.downloader.download_tiles(feature["grid_bb"])
576
+ self.downloader.download_tiles(
577
+ feature["grid_bb"], download_in_parallel=download_in_parallel
578
+ )
577
579
  success = self.merger.merge(feature["grid_bb"], map_name)
578
580
  if success:
579
581
  print(f'[INFO] Downloaded "{map_name}.png"')
@@ -681,6 +683,7 @@ class SheetDownloader:
681
683
  path_save: str | None = "maps",
682
684
  metadata_fname: str | None = "metadata.csv",
683
685
  overwrite: bool | None = False,
686
+ download_in_parallel: bool = True,
684
687
  **kwargs: dict | None,
685
688
  ):
686
689
  """Download map sheets from a list of features.
@@ -695,6 +698,8 @@ class SheetDownloader:
695
698
  Name to use for metadata file, by default "metadata.csv"
696
699
  overwrite : bool, optional
697
700
  Whether to overwrite existing maps, by default ``False``.
701
+ download_in_parallel : bool, optional
702
+ Whether to download tiles in parallel, by default ``True``.
698
703
  **kwargs : dict, optional
699
704
  Keyword arguments to pass to the ``_save_metadata()`` method.
700
705
  """
@@ -703,7 +708,9 @@ class SheetDownloader:
703
708
  if not overwrite:
704
709
  if self._check_map_sheet_exists(feature):
705
710
  continue
706
- success = self._download_map(feature)
711
+ success = self._download_map(
712
+ feature, download_in_parallel=download_in_parallel
713
+ )
707
714
  if success:
708
715
  metadata_path = f"{path_save}/{metadata_fname}"
709
716
  self._save_metadata(
@@ -715,6 +722,7 @@ class SheetDownloader:
715
722
  path_save: str | None = "maps",
716
723
  metadata_fname: str | None = "metadata.csv",
717
724
  overwrite: bool | None = False,
725
+ download_in_parallel: bool = True,
718
726
  **kwargs: dict | None,
719
727
  ) -> None:
720
728
  """
@@ -728,6 +736,8 @@ class SheetDownloader:
728
736
  Name to use for metadata file, by default "metadata.csv"
729
737
  overwrite : bool, optional
730
738
  Whether to overwrite existing maps, by default ``False``.
739
+ download_in_parallel : bool, optional
740
+ Whether to download tiles in parallel, by default ``True``.
731
741
  **kwargs : dict, optional
732
742
  Keyword arguments to pass to the ``_download_map_sheets()`` method.
733
743
  """
@@ -739,7 +749,12 @@ class SheetDownloader:
739
749
 
740
750
  features = self.features
741
751
  self._download_map_sheets(
742
- features, path_save, metadata_fname, overwrite, **kwargs
752
+ features,
753
+ path_save,
754
+ metadata_fname,
755
+ overwrite,
756
+ download_in_parallel=download_in_parallel,
757
+ **kwargs,
743
758
  )
744
759
 
745
760
  def download_map_sheets_by_wfs_ids(
@@ -748,6 +763,7 @@ class SheetDownloader:
748
763
  path_save: str | None = "maps",
749
764
  metadata_fname: str | None = "metadata.csv",
750
765
  overwrite: bool | None = False,
766
+ download_in_parallel: bool = True,
751
767
  **kwargs: dict | None,
752
768
  ) -> None:
753
769
  """
@@ -763,6 +779,8 @@ class SheetDownloader:
763
779
  Name to use for metadata file, by default "metadata.csv"
764
780
  overwrite : bool, optional
765
781
  Whether to overwrite existing maps, by default ``False``.
782
+ download_in_parallel : bool, optional
783
+ Whether to download tiles in parallel, by default ``True``.
766
784
  **kwargs : dict, optional
767
785
  Keyword arguments to pass to the ``_download_map_sheets()`` method.
768
786
  """
@@ -794,7 +812,12 @@ class SheetDownloader:
794
812
  features.append(feature)
795
813
 
796
814
  self._download_map_sheets(
797
- features, path_save, metadata_fname, overwrite, **kwargs
815
+ features,
816
+ path_save,
817
+ metadata_fname,
818
+ overwrite,
819
+ download_in_parallel=download_in_parallel,
820
+ **kwargs,
798
821
  )
799
822
 
800
823
  def download_map_sheets_by_polygon(
@@ -804,6 +827,7 @@ class SheetDownloader:
804
827
  metadata_fname: str | None = "metadata.csv",
805
828
  mode: str | None = "within",
806
829
  overwrite: bool | None = False,
830
+ download_in_parallel: bool = True,
807
831
  **kwargs: dict | None,
808
832
  ) -> None:
809
833
  """
@@ -824,6 +848,8 @@ class SheetDownloader:
824
848
  By default "within".
825
849
  overwrite : bool, optional
826
850
  Whether to overwrite existing maps, by default ``False``.
851
+ download_in_parallel : bool, optional
852
+ Whether to download tiles in parallel, by default ``True``.
827
853
  **kwargs : dict, optional
828
854
  Keyword arguments to pass to the ``_download_map_sheets()`` method.
829
855
 
@@ -869,7 +895,12 @@ class SheetDownloader:
869
895
  features.append(feature)
870
896
 
871
897
  self._download_map_sheets(
872
- features, path_save, metadata_fname, overwrite, **kwargs
898
+ features,
899
+ path_save,
900
+ metadata_fname,
901
+ overwrite,
902
+ download_in_parallel=download_in_parallel,
903
+ **kwargs,
873
904
  )
874
905
 
875
906
  def download_map_sheets_by_coordinates(
@@ -878,6 +909,7 @@ class SheetDownloader:
878
909
  path_save: str | None = "maps",
879
910
  metadata_fname: str | None = "metadata.csv",
880
911
  overwrite: bool | None = False,
912
+ download_in_parallel: bool = True,
881
913
  **kwargs: dict | None,
882
914
  ) -> None:
883
915
  """
@@ -894,6 +926,8 @@ class SheetDownloader:
894
926
  Name to use for metadata file, by default "metadata.csv"
895
927
  overwrite : bool, optional
896
928
  Whether to overwrite existing maps, by default ``False``.
929
+ download_in_parallel : bool, optional
930
+ Whether to download tiles in parallel, by default ``True``.
897
931
  **kwargs : dict, optional
898
932
  Keyword arguments to pass to the ``_download_map_sheets()`` method.
899
933
  """
@@ -922,7 +956,12 @@ class SheetDownloader:
922
956
  features.append(feature)
923
957
 
924
958
  self._download_map_sheets(
925
- features, path_save, metadata_fname, overwrite, **kwargs
959
+ features,
960
+ path_save,
961
+ metadata_fname,
962
+ overwrite,
963
+ download_in_parallel=download_in_parallel,
964
+ **kwargs,
926
965
  )
927
966
 
928
967
  def download_map_sheets_by_line(
@@ -931,6 +970,7 @@ class SheetDownloader:
931
970
  path_save: str | None = "maps",
932
971
  metadata_fname: str | None = "metadata.csv",
933
972
  overwrite: bool | None = False,
973
+ download_in_parallel: bool = True,
934
974
  **kwargs: dict | None,
935
975
  ) -> None:
936
976
  """
@@ -946,6 +986,8 @@ class SheetDownloader:
946
986
  Name to use for metadata file, by default "metadata.csv"
947
987
  overwrite : bool, optional
948
988
  Whether to overwrite existing maps, by default ``False``
989
+ download_in_parallel : bool, optional
990
+ Whether to download tiles in parallel, by default ``True``.
949
991
  **kwargs : dict, optional
950
992
  Keyword arguments to pass to the ``_download_map_sheets()`` method.
951
993
 
@@ -980,7 +1022,12 @@ class SheetDownloader:
980
1022
  features.append(feature)
981
1023
 
982
1024
  self._download_map_sheets(
983
- features, path_save, metadata_fname, overwrite, **kwargs
1025
+ features,
1026
+ path_save,
1027
+ metadata_fname,
1028
+ overwrite,
1029
+ download_in_parallel=download_in_parallel,
1030
+ **kwargs,
984
1031
  )
985
1032
 
986
1033
  def download_map_sheets_by_string(
@@ -990,6 +1037,7 @@ class SheetDownloader:
990
1037
  path_save: str | None = "maps",
991
1038
  metadata_fname: str | None = "metadata.csv",
992
1039
  overwrite: bool | None = False,
1040
+ download_in_parallel: bool = True,
993
1041
  **kwargs: dict | None,
994
1042
  ) -> None:
995
1043
  """
@@ -1013,6 +1061,8 @@ class SheetDownloader:
1013
1061
  Name to use for metadata file, by default "metadata.csv"
1014
1062
  overwrite : bool, optional
1015
1063
  Whether to overwrite existing maps, by default ``False``.
1064
+ download_in_parallel : bool, optional
1065
+ Whether to download tiles in parallel, by default ``True``.
1016
1066
  **kwargs : dict, optional
1017
1067
  Keyword arguments to pass to the ``_download_map_sheets()`` method.
1018
1068
 
@@ -1053,7 +1103,12 @@ class SheetDownloader:
1053
1103
  features.append(feature)
1054
1104
 
1055
1105
  self._download_map_sheets(
1056
- features, path_save, metadata_fname, overwrite, **kwargs
1106
+ features,
1107
+ path_save,
1108
+ metadata_fname,
1109
+ overwrite,
1110
+ download_in_parallel=download_in_parallel,
1111
+ **kwargs,
1057
1112
  )
1058
1113
 
1059
1114
  def download_map_sheets_by_queries(
@@ -1061,6 +1116,7 @@ class SheetDownloader:
1061
1116
  path_save: str | None = "maps",
1062
1117
  metadata_fname: str | None = "metadata.csv",
1063
1118
  overwrite: bool | None = False,
1119
+ download_in_parallel: bool = True,
1064
1120
  **kwargs: dict | None,
1065
1121
  ) -> None:
1066
1122
  """
@@ -1074,6 +1130,8 @@ class SheetDownloader:
1074
1130
  Name to use for metadata file, by default "metadata.csv"
1075
1131
  overwrite : bool, optional
1076
1132
  Whether to overwrite existing maps, by default ``False``.
1133
+ download_in_parallel : bool, optional
1134
+ Whether to download tiles in parallel, by default ``True``.
1077
1135
  **kwargs : dict, optional
1078
1136
  Keyword arguments to pass to the ``_download_map_sheets()`` method.
1079
1137
  """
@@ -1088,7 +1146,12 @@ class SheetDownloader:
1088
1146
 
1089
1147
  features = self.found_queries
1090
1148
  self._download_map_sheets(
1091
- features, path_save, metadata_fname, overwrite, **kwargs
1149
+ features,
1150
+ path_save,
1151
+ metadata_fname,
1152
+ overwrite,
1153
+ download_in_parallel=download_in_parallel,
1154
+ **kwargs,
1092
1155
  )
1093
1156
 
1094
1157
  def hist_published_dates(self, **kwargs) -> None:
@@ -59,17 +59,15 @@ def reproject_geo_info(image_path, target_crs="EPSG:4326", calc_size_in_m=False)
59
59
  tiff_shape, tiff_proj, tiff_coord = extractGeoInfo(image_path)
60
60
 
61
61
  # Coordinate transformation: proj1 ---> proj2
62
- transformer = Transformer.from_crs(tiff_proj, target_crs)
63
- ymin, xmin = transformer.transform(tiff_coord[0], tiff_coord[1])
64
- ymax, xmax = transformer.transform(tiff_coord[2], tiff_coord[3])
65
- coord = (xmin, ymin, xmax, ymax)
66
-
62
+ transformer = Transformer.from_crs(tiff_proj, target_crs, always_xy=True)
63
+ coord = transformer.transform_bounds(*tiff_coord)
67
64
  print(f"[INFO] New CRS: {target_crs}")
68
65
  print("[INFO] Reprojected coordinates: {:.4f} {:.4f} {:.4f} {:.4f}".format(*coord))
69
66
 
70
67
  height, width, _ = tiff_shape
71
68
 
72
69
  # Calculate the size of image in meters
70
+ xmin, ymin, xmax, ymax = coord
73
71
  if calc_size_in_m:
74
72
  if calc_size_in_m in ["geodesic", "gd"]:
75
73
  bottom = geodesic((ymin, xmin), (ymin, xmax)).meters
@@ -985,6 +985,7 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
985
985
  tree_level: str | None = "parent",
986
986
  path_save: str | None = None,
987
987
  add_to_parents: bool | None = True,
988
+ square_cuts: bool | None = False,
988
989
  resize_factor: bool | None = False,
989
990
  output_format: str | None = "png",
990
991
  rewrite: bool | None = False,
@@ -1011,6 +1012,9 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
1011
1012
  add_to_parents : bool, optional
1012
1013
  If True, patches will be added to the MapImages instance's
1013
1014
  ``images`` dictionary, by default ``True``.
1015
+ square_cuts : bool, optional
1016
+ If True, all patches will have the same number of pixels in
1017
+ x and y, by default ``False``.
1014
1018
  resize_factor : bool, optional
1015
1019
  If True, resize the images before patchifying, by default ``False``.
1016
1020
  output_format : str, optional
@@ -1058,16 +1062,33 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
1058
1062
  original_patch_size / mean_pixel_height
1059
1063
  ) ## check this is correct - should patch be different size in x and y?
1060
1064
 
1061
- self._patchify_by_pixel(
1062
- image_id=image_id,
1063
- patch_size=patch_size,
1064
- path_save=path_save,
1065
- add_to_parents=add_to_parents,
1066
- resize_factor=resize_factor,
1067
- output_format=output_format,
1068
- rewrite=rewrite,
1069
- verbose=verbose,
1070
- )
1065
+ if square_cuts:
1066
+ print(
1067
+ "[WARNING] Square cuts is deprecated as of version 1.1.3 and will soon be removed."
1068
+ )
1069
+
1070
+ self._patchify_by_pixel_square(
1071
+ image_id=image_id,
1072
+ patch_size=patch_size,
1073
+ path_save=path_save,
1074
+ add_to_parents=add_to_parents,
1075
+ resize_factor=resize_factor,
1076
+ output_format=output_format,
1077
+ rewrite=rewrite,
1078
+ verbose=verbose,
1079
+ )
1080
+
1081
+ else:
1082
+ self._patchify_by_pixel(
1083
+ image_id=image_id,
1084
+ patch_size=patch_size,
1085
+ path_save=path_save,
1086
+ add_to_parents=add_to_parents,
1087
+ resize_factor=resize_factor,
1088
+ output_format=output_format,
1089
+ rewrite=rewrite,
1090
+ verbose=verbose,
1091
+ )
1071
1092
 
1072
1093
  def _patchify_by_pixel(
1073
1094
  self,
@@ -1165,6 +1186,94 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
1165
1186
  self._add_patch_coords_id(patch_id)
1166
1187
  self._add_patch_polygons_id(patch_id)
1167
1188
 
1189
+ def _patchify_by_pixel_square(
1190
+ self,
1191
+ image_id: str,
1192
+ patch_size: int,
1193
+ path_save: str,
1194
+ add_to_parents: bool | None = True,
1195
+ resize_factor: bool | None = False,
1196
+ output_format: str | None = "png",
1197
+ rewrite: bool | None = False,
1198
+ verbose: bool | None = False,
1199
+ ):
1200
+ """Patchify one image and (if ``add_to_parents=True``) add the patch to the MapImages instance's ``images`` dictionary.
1201
+ Use square cuts for patches at edges.
1202
+
1203
+ Parameters
1204
+ ----------
1205
+ image_id : str
1206
+ The ID of the image to patchify
1207
+ patch_size : int
1208
+ Number of pixels in both x and y to use for slicing
1209
+ path_save : str
1210
+ Directory to save the patches.
1211
+ add_to_parents : bool, optional
1212
+ If True, patches will be added to the MapImages instance's
1213
+ ``images`` dictionary, by default ``True``.
1214
+ resize_factor : bool, optional
1215
+ If True, resize the images before patchifying, by default ``False``.
1216
+ output_format : str, optional
1217
+ Format to use when writing image files, by default ``"png"``.
1218
+ rewrite : bool, optional
1219
+ If True, existing patches will be rewritten, by default ``False``.
1220
+ verbose : bool, optional
1221
+ If True, progress updates will be printed throughout, by default
1222
+ ``False``.
1223
+ """
1224
+ tree_level = self._get_tree_level(image_id)
1225
+
1226
+ parent_path = self.images[tree_level][image_id]["image_path"]
1227
+ img = Image.open(parent_path)
1228
+
1229
+ if resize_factor:
1230
+ original_height, original_width = img.height, img.width
1231
+ img = img.resize(
1232
+ (
1233
+ int(original_width / resize_factor),
1234
+ int(original_height / resize_factor),
1235
+ )
1236
+ )
1237
+
1238
+ height, width = img.height, img.width
1239
+
1240
+ for x in range(0, width, patch_size):
1241
+ for y in range(0, height, patch_size):
1242
+ max_x = min(x + patch_size, width)
1243
+ max_y = min(y + patch_size, height)
1244
+
1245
+ # move min_x and min_y back a bit so the patch is square
1246
+ min_x = x - (patch_size - (max_x - x))
1247
+ min_y = y - (patch_size - (max_y - y))
1248
+
1249
+ patch_id = f"patch-{min_x}-{min_y}-{max_x}-{max_y}-#{image_id}#.{output_format}"
1250
+ patch_path = os.path.join(path_save, patch_id)
1251
+ patch_path = os.path.abspath(patch_path)
1252
+
1253
+ if os.path.isfile(patch_path) and not rewrite:
1254
+ self._print_if_verbose(
1255
+ f"[INFO] File already exists: {patch_path}.", verbose
1256
+ )
1257
+
1258
+ else:
1259
+ self._print_if_verbose(
1260
+ f'[INFO] Creating "{patch_id}". Number of pixels in x,y: {max_x - min_x},{max_y - min_y}.',
1261
+ verbose,
1262
+ )
1263
+
1264
+ patch = img.crop((min_x, min_y, max_x, max_y))
1265
+ patch.save(patch_path, output_format)
1266
+
1267
+ if add_to_parents:
1268
+ self._images_constructor(
1269
+ image_path=patch_path,
1270
+ parent_path=parent_path,
1271
+ tree_level="patch",
1272
+ pixel_bounds=(min_x, min_y, max_x, max_y),
1273
+ )
1274
+ self._add_patch_coords_id(patch_id)
1275
+ self._add_patch_polygons_id(patch_id)
1276
+
1168
1277
  def _add_patch_to_parent(self, patch_id: str) -> None:
1169
1278
  """
1170
1279
  Add patch to parent.
@@ -2108,15 +2217,8 @@ See https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes for mor
2108
2217
  tiff_proj = tiff_src.crs.to_string()
2109
2218
  # Coordinate transformation: proj1 ---> proj2
2110
2219
  # tiff is "lat, lon" instead of "x, y"
2111
- transformer = Transformer.from_crs(tiff_proj, target_crs)
2112
- ymin, xmin = transformer.transform(
2113
- tiff_src.bounds.left, tiff_src.bounds.bottom
2114
- )
2115
- ymax, xmax = transformer.transform(
2116
- tiff_src.bounds.right, tiff_src.bounds.top
2117
- )
2118
- # New projected coordinates
2119
- coords = (xmin, ymin, xmax, ymax)
2220
+ transformer = Transformer.from_crs(tiff_proj, target_crs, always_xy=True)
2221
+ coords = transformer.transform_bounds(*tiff_src.bounds)
2120
2222
  self.parents[image_id]["coordinates"] = coords
2121
2223
  self.parents[image_id]["crs"] = target_crs
2122
2224
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mapreader
3
- Version: 1.1.4
3
+ Version: 1.2.0
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
@@ -24,7 +24,7 @@ Classifier: Operating System :: OS Independent
24
24
  Classifier: Topic :: Software Development
25
25
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
26
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
27
- Requires-Python: >=3.7, <3.11
27
+ Requires-Python: >=3.9
28
28
  Description-Content-Type: text/markdown
29
29
  License-File: LICENSE
30
30
  Requires-Dist: matplotlib<4.0.0,>=3.5.0
@@ -37,8 +37,8 @@ Requires-Dist: Shapely<3.0.0,>=2.0.0
37
37
  Requires-Dist: nest-asyncio<2.0.0,>=1.5.1
38
38
  Requires-Dist: scikit-image>=0.18.3
39
39
  Requires-Dist: scikit-learn<2.0.0,>=1.0.1
40
- Requires-Dist: torch<2.0.0,>=1.10.0
41
- Requires-Dist: torchvision<0.12.1,>=0.11.1
40
+ Requires-Dist: torch>=1.10.0
41
+ Requires-Dist: torchvision<0.17.3,>=0.11.1
42
42
  Requires-Dist: jupyter<2.0.0,>=1.0.0
43
43
  Requires-Dist: ipykernel<7.0.0,>=6.5.1
44
44
  Requires-Dist: ipywidgets<9.0.0,>=8.0.0
@@ -55,13 +55,15 @@ Requires-Dist: tqdm<5.0.0
55
55
  Requires-Dist: torchinfo<2.0.0
56
56
  Requires-Dist: openpyxl<4.0.0
57
57
  Requires-Dist: geopandas<1.0.0
58
+ Requires-Dist: pyogrio>=0.7.2
59
+ Requires-Dist: cartopy>=0.22.0
58
60
  Provides-Extra: dev
59
- Requires-Dist: pytest<8.0.0; extra == "dev"
60
- Requires-Dist: pytest-cov<5.0.0,>=4.1.0; extra == "dev"
61
+ Requires-Dist: pytest<9.0.0; extra == "dev"
62
+ Requires-Dist: pytest-cov<6.0.0,>=4.1.0; extra == "dev"
61
63
  Requires-Dist: timm<1.0.0; extra == "dev"
62
64
  Requires-Dist: transformers<5.0.0; extra == "dev"
63
- Requires-Dist: black<24.0.0,>=23.7.0; extra == "dev"
64
- Requires-Dist: flake8<7.0.0,>=6.0.0; extra == "dev"
65
+ Requires-Dist: black<25.0.0,>=23.7.0; extra == "dev"
66
+ Requires-Dist: flake8<8.0.0,>=6.0.0; extra == "dev"
65
67
 
66
68
  <div align="center">
67
69
  <br>
@@ -132,7 +134,7 @@ The MapReader documentation can be found at https://mapreader.readthedocs.io/en/
132
134
  - Geospatial images (i.e. maps)
133
135
  - Non-geospatial images
134
136
 
135
- **Developers and contributors** may also want to refer to the [API documentation](https://mapreader.readthedocs.io/en/latest/api/index.html) and [Contribution guide](https://mapreader.readthedocs.io/en/latest/Contribution-guide.html) for guidance on how to contribute to the MapReader package.
137
+ **Developers and contributors** may also want to refer to the [API documentation](https://mapreader.readthedocs.io/en/latest/api/index.html) and [Contribution guide](https://mapreader.readthedocs.io/en/latest/Contribution-guide/Contribution-guide.html) for guidance on how to contribute to the MapReader package.
136
138
 
137
139
  **Join our Slack workspace!**
138
140
  Please fill out [this form](https://forms.gle/dXjECHZQkwrZ3Xpt9) to receive an invitation to the Slack workspace.
@@ -8,8 +8,8 @@ Shapely<3.0.0,>=2.0.0
8
8
  nest-asyncio<2.0.0,>=1.5.1
9
9
  scikit-image>=0.18.3
10
10
  scikit-learn<2.0.0,>=1.0.1
11
- torch<2.0.0,>=1.10.0
12
- torchvision<0.12.1,>=0.11.1
11
+ torch>=1.10.0
12
+ torchvision<0.17.3,>=0.11.1
13
13
  jupyter<2.0.0,>=1.0.0
14
14
  ipykernel<7.0.0,>=6.5.1
15
15
  ipywidgets<9.0.0,>=8.0.0
@@ -26,11 +26,13 @@ tqdm<5.0.0
26
26
  torchinfo<2.0.0
27
27
  openpyxl<4.0.0
28
28
  geopandas<1.0.0
29
+ pyogrio>=0.7.2
30
+ cartopy>=0.22.0
29
31
 
30
32
  [dev]
31
- pytest<8.0.0
32
- pytest-cov<5.0.0,>=4.1.0
33
+ pytest<9.0.0
34
+ pytest-cov<6.0.0,>=4.1.0
33
35
  timm<1.0.0
34
36
  transformers<5.0.0
35
- black<24.0.0,>=23.7.0
36
- flake8<7.0.0,>=6.0.0
37
+ black<25.0.0,>=23.7.0
38
+ flake8<8.0.0,>=6.0.0
@@ -26,7 +26,7 @@ setuptools.setup(
26
26
  packages=setuptools.find_packages(),
27
27
  include_package_data=True,
28
28
  platforms="OS Independent",
29
- python_requires=">=3.7, <3.11",
29
+ python_requires=">=3.9",
30
30
  install_requires=[
31
31
  "matplotlib>=3.5.0,<4.0.0",
32
32
  "numpy>=1.21.5,<2.0.0",
@@ -38,14 +38,13 @@ setuptools.setup(
38
38
  "nest-asyncio>=1.5.1,<2.0.0",
39
39
  "scikit-image>=0.18.3",
40
40
  "scikit-learn>=1.0.1,<2.0.0",
41
- "torch>=1.10.0,<2.0.0",
42
- "torchvision>=0.11.1,<0.12.1",
41
+ "torch>=1.10.0",
42
+ "torchvision>=0.11.1,<0.17.3",
43
43
  "jupyter>=1.0.0,<2.0.0",
44
44
  "ipykernel>=6.5.1,<7.0.0",
45
45
  "ipywidgets>=8.0.0,<9.0.0",
46
46
  "ipyannotate==0.1.0-beta.0",
47
47
  "Cython>=0.29.24,<0.30.0",
48
- # "proj>=0.2.0,<0.3.0",
49
48
  "PyYAML>=6.0,<7.0",
50
49
  "tensorboard>=2.7.0,<3.0.0",
51
50
  "parhugin>=0.0.3,<0.0.4",
@@ -57,16 +56,18 @@ setuptools.setup(
57
56
  "torchinfo<2.0.0",
58
57
  "openpyxl<4.0.0",
59
58
  "geopandas<1.0.0",
59
+ "pyogrio>=0.7.2",
60
+ "cartopy>=0.22.0",
60
61
  ],
61
62
  extras_require={
62
63
  "dev": [
63
- "pytest<8.0.0",
64
- "pytest-cov>=4.1.0,<5.0.0",
64
+ "pytest<9.0.0",
65
+ "pytest-cov>=4.1.0,<6.0.0",
65
66
  "timm<1.0.0",
66
67
  "transformers<5.0.0",
67
- "black>=23.7.0,<24.0.0",
68
- "flake8>=6.0.0,<7.0.0",
69
- ]
68
+ "black>=23.7.0,<25.0.0",
69
+ "flake8>=6.0.0,<8.0.0",
70
+ ],
70
71
  },
71
72
  classifiers=[
72
73
  "Development Status :: 3 - Alpha",
File without changes
File without changes
File without changes