giga-spatial 0.6.2__py3-none-any.whl → 0.6.4__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: giga-spatial
3
- Version: 0.6.2
3
+ Version: 0.6.4
4
4
  Summary: A package for spatial data download & processing
5
5
  Home-page: https://github.com/unicef/giga-spatial
6
6
  Author: Utku Can Ozturk
@@ -51,17 +51,27 @@ Dynamic: summary
51
51
 
52
52
  # GigaSpatial
53
53
 
54
- # ![GigaSpatial logo](https://raw.githubusercontent.com/unicef/giga-spatial/main/docs/assets/logo.png)
55
- Giga is a UNICEF-ITU initiative to connect every school to the Internet and every young person to information, opportunity and choice.
56
- Giga maps schools' Internet access in real time, creates models for innovative financing, and supports governments contracting for connectivity.
54
+ ## About Giga
57
55
 
58
- ---
56
+ [Giga](https://giga.global/) is a UNICEF-ITU initiative to connect every school to the Internet and every young person to information, opportunity and choice.
57
+ Giga maps schools' Internet access in real time, creates models for innovative financing, and supports governments contracting for connectivity.
59
58
 
60
59
  ## About GigaSpatial
61
60
 
62
61
  **GigaSpatial** is a Python package developed as part of the Giga Applied Science Team to handle geospatial data efficiently. It provides tools for downloading, processing, and analyzing geospatial data, enabling users to work with datasets such as OpenStreetMap (OSM), Global Human Settlement Layer (GHSL), Microsoft Global Buildings, Google Open Buildings, and more. The package is designed to support Giga's mission by providing robust geospatial capabilities for mapping and analyzing school connectivity.
63
62
 
64
- ### Key Features
63
+ ## Installation
64
+
65
+ See the [installation docs](https://unicef.github.io/giga-spatial/getting-started/installation/) for all details. GigaSpatial requires Python 3.10 or above and depends on the following key packages:
66
+
67
+ - geopandas
68
+ - pandas
69
+ - shapely
70
+ - rasterio
71
+
72
+ We recommend using a virtual environment for installation. See the [installation docs](https://unicef.github.io/giga-spatial/getting-started/installation/) for more details.
73
+
74
+ ## Key Features
65
75
  - **Data Downloading**: Download geospatial data from various sources including GHSL, Microsoft Global Buildings, Google Open Buildings, OpenCellID, and HDX datasets.
66
76
  - **Data Processing**: Process and transform geospatial data, such as GeoTIFF files and vector data, with support for compression and efficient handling.
67
77
  - **View Generators**:
@@ -74,7 +84,7 @@ Giga maps schools' Internet access in real time, creates models for innovative f
74
84
  - Centralized configuration via environment variables or `.env` file
75
85
  - Easy setup of API keys and paths
76
86
 
77
- ### Supported Datasets
87
+ ## Supported Datasets
78
88
 
79
89
  The `gigaspatial` package supports data from the following providers:
80
90
 
@@ -84,7 +94,7 @@ The `gigaspatial` package supports data from the following providers:
84
94
 
85
95
  ---
86
96
 
87
- ### View Generators
97
+ ## View Generators
88
98
 
89
99
  The **view generators** in GigaSpatial are designed to enrich the spatial context of school locations and map data into grid or POI locations. This enables users to analyze and visualize geospatial data in meaningful ways.
90
100
 
@@ -1,6 +1,6 @@
1
- giga_spatial-0.6.2.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
2
- gigaspatial/__init__.py,sha256=jFlbxEJFS0G44LE-yXXVSwXACA1J_NyYDk5E20_2zpc,22
3
- gigaspatial/config.py,sha256=yMf1ofOU0_I6iKDqshiFSYmK6TDIVpPm1AZo4e2okHU,8166
1
+ giga_spatial-0.6.4.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
2
+ gigaspatial/__init__.py,sha256=WMmvm2Keb76yMz8OL_h4fKT34Xpi-1BVfCiTn2QGzz4,22
3
+ gigaspatial/config.py,sha256=PR6n6NDDD4560zWEbaFiYSitr9PAKik915cxCCMZNQc,8392
4
4
  gigaspatial/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  gigaspatial/core/io/__init__.py,sha256=y4QNWx6US1-adTuAO_NZwLmjzSQj25HNDL5hUGvEHZc,263
6
6
  gigaspatial/core/io/adls_data_store.py,sha256=Zv-D_8d_2h57HnCUTJb0JWWjXqR_0XH4F8Nu_UFZK9E,11975
@@ -19,29 +19,29 @@ gigaspatial/generators/zonal/geometry.py,sha256=XPcX5lT7X7Z1vn72sN-VKLb2hDP9F_w3
19
19
  gigaspatial/generators/zonal/mercator.py,sha256=R_KlaqF4lnc0cRqVfcNVO8i0Re21_6w7pnclVKSohcY,3125
20
20
  gigaspatial/grid/__init__.py,sha256=H8SnNAMDafJXJ9bUp2zU0Z3t6s8niqY5rGP5nFhnbLA,45
21
21
  gigaspatial/grid/mercator_tiles.py,sha256=Z_3M4sy1tyxywAo2wmBb6niBP3x-IWgwMkmUp8LOSDg,10492
22
- gigaspatial/handlers/__init__.py,sha256=gh6LIzqr2fASX4fEzQ20v1j_IhnYpym-lcgOkAGHrdE,1430
22
+ gigaspatial/handlers/__init__.py,sha256=R2rugXR5kF4lLkSO1fjpVDYK_jWdD8U2NbXbW71Ezv8,1523
23
23
  gigaspatial/handlers/base.py,sha256=rL94c3wDjsqzLp4na8FfYXW6tNjVGX6v4M-Ce4LrAro,26413
24
- gigaspatial/handlers/boundaries.py,sha256=hoO-b5MlFYwlCWogApcFyEx6OnxMJG29lqJurNGwOWg,11260
25
- gigaspatial/handlers/ghsl.py,sha256=YTXuhjSjNMl11nmgd6ZOtVMsJGg2PxDcZn4pu8aaypc,27749
26
- gigaspatial/handlers/giga.py,sha256=2aP1EenDAQXn-h-uCyuVxEVZvAFEvrL17_z0MiS8FDs,4867
24
+ gigaspatial/handlers/boundaries.py,sha256=UM0lFcTzy64ADdMnPOkzLGJ-OG5P7KyoZtA91GTWxYs,17242
25
+ gigaspatial/handlers/ghsl.py,sha256=GHao8lkmj1C0-QFqNwH9jr0Lqzu6NTj_7ooQdj1h6ok,27760
26
+ gigaspatial/handlers/giga.py,sha256=F5ZfcE37a24X-c6Xhyt72C9eZZbyN_gV7w_InxKFMQQ,28348
27
27
  gigaspatial/handlers/google_open_buildings.py,sha256=Liqk7qJhDtB4Ia4uhBe44LFcf-XVKBjRfj-pWlE5erY,16594
28
- gigaspatial/handlers/hdx.py,sha256=8VAEHfO8UgvEx6_Xdx0_vNsPqO18z5Iiq6TjuEbg05k,8789
28
+ gigaspatial/handlers/hdx.py,sha256=LTEs_xZF1yPhD8dAdZ_YN8Vcan7iB5_tZ8NjF_ip6u0,18001
29
29
  gigaspatial/handlers/mapbox_image.py,sha256=M_nkJ_b1PD8FG1ajVgSycCb0NRTAI_SLpHdzszNetKA,7786
30
- gigaspatial/handlers/maxar_image.py,sha256=g5YVGV-8JjeG9bGBOp7ZfKani22J4izXX4hnB9A99Jk,10272
30
+ gigaspatial/handlers/maxar_image.py,sha256=kcc8uGljQB0Yh0MKBA7lT7KwBbNZwFzuyBklR3db1P4,10204
31
31
  gigaspatial/handlers/microsoft_global_buildings.py,sha256=bQ5WHIv3v0wWrZZUbZkKPRjgdlqIxlK7CV_0zSvdrTw,20292
32
32
  gigaspatial/handlers/ookla_speedtest.py,sha256=EcvSAxJZ9GPfzYnT_C85Qgy2ecc9ndf70Pklk53OdC8,6506
33
33
  gigaspatial/handlers/opencellid.py,sha256=KuJqd-5-RO5ZzyDaBSrTgCK2ib5N_m3RUcPlX5heWwI,10683
34
34
  gigaspatial/handlers/osm.py,sha256=sLNMkOVh1v50jrWw7Z0-HILY5QTQjgKCHCeAfXj5jA8,14084
35
35
  gigaspatial/handlers/overture.py,sha256=lKeNw00v5Qia7LdWORuYihnlKEqxE9m38tdeRrvag9k,4218
36
- gigaspatial/handlers/rwi.py,sha256=g37bUd4Y02r8m53ZkjASCscg0mejMH5820HAh8PQZqQ,6032
36
+ gigaspatial/handlers/rwi.py,sha256=GDpQH9K96QZD3yezJOBiy5yZvYmrj4xbjUNSjYfNAh0,4875
37
37
  gigaspatial/handlers/unicef_georepo.py,sha256=ODYNvkU_UKgOHXT--0MqmJ4Uk6U1_mp9xgehbTzKpX8,31924
38
38
  gigaspatial/handlers/worldpop.py,sha256=oJ39NGajXi0rn829ZoFiaeG4_wavyPvljdActpxs12I,9850
39
39
  gigaspatial/processing/__init__.py,sha256=QDVL-QbLCrIb19lrajP7LrHNdGdnsLeGcvAs_jQpdRM,183
40
40
  gigaspatial/processing/geo.py,sha256=D-S3IlhQwLIxrCcxy6NhNmKLrOIjoRHfK_eZJGKpe2U,36947
41
41
  gigaspatial/processing/sat_images.py,sha256=YUbH5MFNzl6NX49Obk14WaFcr1s3SyGJIOk-kRpbBNg,1429
42
- gigaspatial/processing/tif_processor.py,sha256=4qCNx6ODtVsYm6lbSToIdUCrduIrPsWFBAABTQ2Vs24,17259
42
+ gigaspatial/processing/tif_processor.py,sha256=zqcP_ioo9KHNJ6H0uba4UghW4MToTRwq1iE-nZbb8zA,21101
43
43
  gigaspatial/processing/utils.py,sha256=HC85vGKQakxlkoQAkZmeAXWHsenAwTIRn7jPKUA7x20,1500
44
- giga_spatial-0.6.2.dist-info/METADATA,sha256=PXfUtURxAxnej1IDhF3MGbOOdC8MYgsGtRHHTiPjQSQ,7104
45
- giga_spatial-0.6.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
46
- giga_spatial-0.6.2.dist-info/top_level.txt,sha256=LZsccgw6H4zXT7m6Y4XChm-Y5LjHAwZ2hkGN_B3ExmI,12
47
- giga_spatial-0.6.2.dist-info/RECORD,,
44
+ giga_spatial-0.6.4.dist-info/METADATA,sha256=WQUWSdjlmfh09kkX20cgudrGHWmldXlNbh4DNjB0Xgo,7467
45
+ giga_spatial-0.6.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
46
+ giga_spatial-0.6.4.dist-info/top_level.txt,sha256=LZsccgw6H4zXT7m6Y4XChm-Y5LjHAwZ2hkGN_B3ExmI,12
47
+ giga_spatial-0.6.4.dist-info/RECORD,,
gigaspatial/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.6.2"
1
+ __version__ = "0.6.4"
gigaspatial/config.py CHANGED
@@ -32,6 +32,12 @@ class Config(BaseSettings):
32
32
  GIGA_SCHOOL_LOCATION_API_KEY: str = Field(
33
33
  default="", alias="GIGA_SCHOOL_LOCATION_API_KEY"
34
34
  )
35
+ GIGA_SCHOOL_PROFILE_API_KEY: str = Field(
36
+ default="", alias="GIGA_SCHOOL_PROFILE_API_KEY"
37
+ )
38
+ GIGA_SCHOOL_MEASUREMENTS_API_KEY: str = Field(
39
+ default="", alias="GIGA_SCHOOL_MEASUREMENTS_API_KEY"
40
+ )
35
41
 
36
42
  ROOT_DATA_DIR: Path = Field(
37
43
  default=Path("."),
@@ -31,10 +31,14 @@ from gigaspatial.handlers.opencellid import (
31
31
  OpenCellIDDownloader,
32
32
  OpenCellIDReader,
33
33
  )
34
- from gigaspatial.handlers.hdx import HDXConfig, HDXDownloader, HDXReader
35
- from gigaspatial.handlers.rwi import RWIConfig, RelativeWealthIndexDownloader
34
+ from gigaspatial.handlers.hdx import HDXConfig, HDXDownloader, HDXReader, HDXHandler
35
+ from gigaspatial.handlers.rwi import RWIConfig, RWIDownloader, RWIReader, RWIHandler
36
36
  from gigaspatial.handlers.unicef_georepo import (
37
37
  GeoRepoClient,
38
38
  get_country_boundaries_by_iso3,
39
39
  )
40
- from gigaspatial.handlers.giga import GigaSchoolLocationFetcher
40
+ from gigaspatial.handlers.giga import (
41
+ GigaSchoolLocationFetcher,
42
+ GigaSchoolProfileFetcher,
43
+ GigaSchoolMeasurementsFetcher,
44
+ )
@@ -4,10 +4,12 @@ import geopandas as gpd
4
4
  from pathlib import Path
5
5
  from urllib.error import HTTPError
6
6
  from shapely.geometry import Polygon, MultiPolygon, shape
7
+ import tempfile
7
8
  import pycountry
8
9
 
9
10
  from gigaspatial.core.io.data_store import DataStore
10
11
  from gigaspatial.core.io.readers import read_dataset
12
+ from gigaspatial.handlers.hdx import HDXConfig
11
13
  from gigaspatial.config import config
12
14
 
13
15
 
@@ -61,8 +63,31 @@ class AdminBoundaries(BaseModel):
61
63
  "name_en": "name_en",
62
64
  "country_code": "iso_3166_1_alpha_3",
63
65
  },
66
+ "geoBoundaries": {
67
+ "id": "shapeID",
68
+ "name": "shapeName",
69
+ "country_code": "shapeGroup",
70
+ },
64
71
  }
65
72
 
73
+ def to_geodataframe(self) -> gpd.GeoDataFrame:
74
+ """Convert the AdminBoundaries to a GeoDataFrame."""
75
+ if not self.boundaries:
76
+ if hasattr(self, "_empty_schema"):
77
+ columns = self._empty_schema
78
+ else:
79
+ columns = ["id", "name", "country_code", "geometry"]
80
+ if self.level > 0:
81
+ columns.append("parent_id")
82
+
83
+ return gpd.GeoDataFrame(columns=columns, geometry="geometry", crs=4326)
84
+
85
+ return gpd.GeoDataFrame(
86
+ [boundary.model_dump() for boundary in self.boundaries],
87
+ geometry="geometry",
88
+ crs=4326,
89
+ )
90
+
66
91
  @classmethod
67
92
  def get_schema_config(cls) -> Dict[str, Dict[str, str]]:
68
93
  """Return field mappings for different data sources"""
@@ -100,6 +125,7 @@ class AdminBoundaries(BaseModel):
100
125
  cls.logger.warning(
101
126
  f"Error loading GADM data for {country_code} at admin level {admin_level}: {str(e)}"
102
127
  )
128
+ cls.logger.info("Falling back to empty instance")
103
129
  return cls._create_empty_instance(country_code, admin_level, "gadm")
104
130
 
105
131
  @classmethod
@@ -138,6 +164,7 @@ class AdminBoundaries(BaseModel):
138
164
  cls.logger.warning(
139
165
  f"No data found at {path} for admin level {admin_level}: {str(e)}"
140
166
  )
167
+ cls.logger.info("Falling back to empty instance")
141
168
  return cls._create_empty_instance(None, admin_level, "internal")
142
169
 
143
170
  @classmethod
@@ -202,6 +229,69 @@ class AdminBoundaries(BaseModel):
202
229
 
203
230
  return cls(boundaries=boundaries, level=admin_level)
204
231
 
232
+ @classmethod
233
+ def from_geoboundaries(cls, country_code, admin_level: int = 0):
234
+ cls.logger.info(
235
+ f"Searching for geoBoundaries data for country: {country_code}, admin level: {admin_level}"
236
+ )
237
+
238
+ country_datasets = HDXConfig.search_datasets(
239
+ query=f'dataseries_name:"geoBoundaries - Subnational Administrative Boundaries" AND groups:"{country_code.lower()}"',
240
+ rows=1,
241
+ )
242
+ if not country_datasets:
243
+ cls.logger.error(f"No datasets found for country: {country_code}")
244
+ raise ValueError(
245
+ "No resources found for the specified country. Please check your search parameters and try again."
246
+ )
247
+
248
+ cls.logger.info(f"Found dataset: {country_datasets[0].get('title', 'Unknown')}")
249
+
250
+ resources = [
251
+ resource
252
+ for resource in country_datasets[0].get_resources()
253
+ if (
254
+ resource.data["name"]
255
+ == f"geoBoundaries-{country_code.upper()}-ADM{admin_level}.geojson"
256
+ )
257
+ ]
258
+
259
+ if not resources:
260
+ cls.logger.error(
261
+ f"No resources found for {country_code} at admin level {admin_level}"
262
+ )
263
+ raise ValueError(
264
+ "No resources found for the specified criteria. Please check your search parameters and try again."
265
+ )
266
+
267
+ cls.logger.info(f"Found resource: {resources[0].data.get('name', 'Unknown')}")
268
+
269
+ try:
270
+ cls.logger.info("Downloading and processing boundary data...")
271
+ with tempfile.TemporaryDirectory() as tmpdir:
272
+ url, local_path = resources[0].download(folder=tmpdir)
273
+ cls.logger.debug(f"Downloaded file to temporary path: {local_path}")
274
+ with open(local_path, "rb") as f:
275
+ gdf = gpd.read_file(f)
276
+
277
+ gdf = cls._map_fields(gdf, "geoBoundaries", admin_level)
278
+ boundaries = [
279
+ AdminBoundary(**row_dict) for row_dict in gdf.to_dict("records")
280
+ ]
281
+ cls.logger.info(
282
+ f"Successfully created {len(boundaries)} AdminBoundary objects"
283
+ )
284
+ return cls(boundaries=boundaries, level=admin_level)
285
+
286
+ except (ValueError, HTTPError, FileNotFoundError) as e:
287
+ cls.logger.warning(
288
+ f"Error loading geoBoundaries data for {country_code} at admin level {admin_level}: {str(e)}"
289
+ )
290
+ cls.logger.info("Falling back to empty instance")
291
+ return cls._create_empty_instance(
292
+ country_code, admin_level, "geoBoundaries"
293
+ )
294
+
205
295
  @classmethod
206
296
  def create(
207
297
  cls,
@@ -211,45 +301,126 @@ class AdminBoundaries(BaseModel):
211
301
  path: Optional[Union[str, "Path"]] = None,
212
302
  **kwargs,
213
303
  ) -> "AdminBoundaries":
214
- """Factory method to create AdminBoundaries instance from either GADM or data store."""
304
+ """Factory method to create AdminBoundaries instance from either GADM or data store.
305
+
306
+ Args:
307
+ country_code: ISO country code (2 or 3 letter) or country name
308
+ admin_level: Administrative level (0=country, 1=state/province, etc.)
309
+ data_store: Optional data store instance for loading from existing data
310
+ path: Optional path to data file (used with data_store)
311
+ **kwargs: Additional arguments passed to the underlying creation methods
312
+
313
+ Returns:
314
+ AdminBoundaries: Configured instance
315
+
316
+ Raises:
317
+ ValueError: If neither country_code nor (data_store, path) are provided,
318
+ or if country_code lookup fails
319
+
320
+ Example:
321
+ # From country code
322
+ boundaries = AdminBoundaries.create(country_code="USA", admin_level=1)
323
+
324
+ # From data store
325
+ boundaries = AdminBoundaries.create(data_store=store, path="data.shp")
326
+ """
215
327
  cls.logger.info(
216
- f"Creating AdminBoundaries instance. Country: {country_code}, admin level: {admin_level}, data_store provided: {data_store is not None}, path provided: {path is not None}"
328
+ f"Creating AdminBoundaries instance. Country: {country_code}, "
329
+ f"admin level: {admin_level}, data_store provided: {data_store is not None}, "
330
+ f"path provided: {path is not None}"
217
331
  )
218
- iso3_code = pycountry.countries.lookup(country_code).alpha_3
332
+
333
+ # Validate input parameters
334
+ if not country_code and not data_store:
335
+ raise ValueError("Either country_code or data_store must be provided.")
336
+
337
+ if data_store and not path and not country_code:
338
+ raise ValueError(
339
+ "If data_store is provided, either path or country_code must also be specified."
340
+ )
341
+
342
+ # Handle data store path first
219
343
  if data_store is not None:
220
- if path is None:
221
- if country_code is None:
222
- ValueError(
223
- "If data_store is provided, path or country_code must also be specified."
224
- )
344
+ iso3_code = None
345
+ if country_code:
346
+ try:
347
+ iso3_code = pycountry.countries.lookup(country_code).alpha_3
348
+ except LookupError as e:
349
+ raise ValueError(f"Invalid country code '{country_code}': {e}")
350
+
351
+ # Generate path if not provided
352
+ if path is None and iso3_code:
225
353
  path = config.get_admin_path(
226
354
  country_code=iso3_code,
227
355
  admin_level=admin_level,
228
356
  )
357
+
229
358
  return cls.from_data_store(data_store, path, admin_level, **kwargs)
230
- elif country_code is not None:
231
- from gigaspatial.handlers.unicef_georepo import GeoRepoClient
232
359
 
360
+ # Handle country code path
361
+ if country_code is not None:
233
362
  try:
234
- client = GeoRepoClient()
235
- if client.check_connection():
236
- cls.logger.info("GeoRepo connection successful.")
237
- return cls.from_georepo(
238
- iso3_code,
239
- admin_level=admin_level,
240
- )
241
- except ValueError as e:
363
+ iso3_code = pycountry.countries.lookup(country_code).alpha_3
364
+ except LookupError as e:
365
+ raise ValueError(f"Invalid country code '{country_code}': {e}")
366
+
367
+ # Try GeoRepo first
368
+ if cls._try_georepo(iso3_code, admin_level):
369
+ return cls.from_georepo(iso3_code, admin_level=admin_level)
370
+
371
+ # Fallback to GADM
372
+ try:
373
+ cls.logger.info("Attempting to load from GADM.")
374
+ return cls.from_gadm(iso3_code, admin_level, **kwargs)
375
+ except Exception as e:
242
376
  cls.logger.warning(
243
- f"GeoRepo initialization failed: {str(e)}. Falling back to GADM."
377
+ f"GADM loading failed: {e}. Falling back to geoBoundaries."
244
378
  )
379
+
380
+ # Final fallback to geoBoundaries
381
+ try:
382
+ return cls.from_geoboundaries(iso3_code, admin_level)
245
383
  except Exception as e:
246
- cls.logger.warning(f"GeoRepo error: {str(e)}. Falling back to GADM.")
384
+ cls.logger.error(f"All data sources failed. geoBoundaries error: {e}")
385
+ raise RuntimeError(
386
+ f"Failed to load administrative boundaries for {country_code} "
387
+ f"from all available sources (GeoRepo, GADM, geoBoundaries)."
388
+ ) from e
247
389
 
248
- return cls.from_gadm(iso3_code, admin_level, **kwargs)
249
- else:
250
- raise ValueError(
251
- "Either country_code or (data_store, path) must be provided."
252
- )
390
+ # This should never be reached due to validation above
391
+ raise ValueError("Unexpected error: no valid data source could be determined.")
392
+
393
+ @classmethod
394
+ def _try_georepo(cls, iso3_code: str, admin_level: int) -> bool:
395
+ """Helper method to test GeoRepo availability.
396
+
397
+ Args:
398
+ iso3_code: ISO3 country code
399
+ admin_level: Administrative level
400
+
401
+ Returns:
402
+ bool: True if GeoRepo is available and working, False otherwise
403
+ """
404
+ try:
405
+ from gigaspatial.handlers.unicef_georepo import GeoRepoClient
406
+
407
+ client = GeoRepoClient()
408
+ if client.check_connection():
409
+ cls.logger.info("GeoRepo connection successful.")
410
+ return True
411
+ else:
412
+ cls.logger.info("GeoRepo connection failed.")
413
+ return False
414
+
415
+ except ImportError:
416
+ cls.logger.info("GeoRepo client not available (import failed).")
417
+ return False
418
+ except ValueError as e:
419
+ cls.logger.warning(f"GeoRepo initialization failed: {e}")
420
+ return False
421
+ except Exception as e:
422
+ cls.logger.warning(f"GeoRepo error: {e}")
423
+ return False
253
424
 
254
425
  @classmethod
255
426
  def _create_empty_instance(
@@ -288,21 +459,3 @@ class AdminBoundaries(BaseModel):
288
459
  field_mapping[v] = k
289
460
 
290
461
  return gdf.rename(columns=field_mapping)
291
-
292
- def to_geodataframe(self) -> gpd.GeoDataFrame:
293
- """Convert the AdminBoundaries to a GeoDataFrame."""
294
- if not self.boundaries:
295
- if hasattr(self, "_empty_schema"):
296
- columns = self._empty_schema
297
- else:
298
- columns = ["id", "name", "country_code", "geometry"]
299
- if self.level > 0:
300
- columns.append("parent_id")
301
-
302
- return gpd.GeoDataFrame(columns=columns, geometry="geometry", crs=4326)
303
-
304
- return gpd.GeoDataFrame(
305
- [boundary.model_dump() for boundary in self.boundaries],
306
- geometry="geometry",
307
- crs=4326,
308
- )
@@ -74,8 +74,6 @@ class GHSLDataConfig(BaseHandlerConfig):
74
74
 
75
75
  def __post_init__(self):
76
76
  super().__post_init__()
77
- self.TILES_URL = self.TILES_URL.format(self.coord_system.value)
78
- self._load_tiles()
79
77
 
80
78
  def _load_tiles(self):
81
79
  """Load GHSL tiles from tiles shapefile."""
@@ -158,6 +156,9 @@ class GHSLDataConfig(BaseHandlerConfig):
158
156
  )
159
157
  self.coord_system = CoordSystem.Mollweide
160
158
 
159
+ self.TILES_URL = self.TILES_URL.format(self.coord_system.value)
160
+ self._load_tiles()
161
+
161
162
  return self
162
163
 
163
164
  @property
@@ -176,7 +177,7 @@ class GHSLDataConfig(BaseHandlerConfig):
176
177
  self, points: Iterable[Union[Point, tuple]], **kwargs
177
178
  ) -> List[dict]:
178
179
  """
179
- Return intersecting tiles for a list of points.
180
+ Return intersecting tiles f or a list of points.
180
181
  """
181
182
  return self._get_relevant_tiles(points)
182
183
 
@@ -240,8 +241,8 @@ class GHSLDataConfig(BaseHandlerConfig):
240
241
  ValueError: If the input `source` is not one of the supported types.
241
242
  """
242
243
  if isinstance(source, gpd.GeoDataFrame):
243
- if source.crs != "EPSG:4326":
244
- source = source.to_crs("EPSG:4326")
244
+ # if source.crs != "EPSG:4326":
245
+ # source = source.to_crs("EPSG:4326")
245
246
  search_geom = source.geometry.unary_union
246
247
  elif isinstance(
247
248
  source,
@@ -282,7 +283,7 @@ class GHSLDataConfig(BaseHandlerConfig):
282
283
  else ("3ss" if self.resolution == 100 else "30ss")
283
284
  )
284
285
  product_folder = f"{self.product}_GLOBE_{self.release}"
285
- product_name = f"{self.product}_E{self.year}_GLOBE_{self.release}_{self.coord_system}_{resolution_str}"
286
+ product_name = f"{self.product}_E{self.year}_GLOBE_{self.release}_{self.coord_system.value}_{resolution_str}"
286
287
  product_version = 2 if self.product == "GHS_SMOD" else 1
287
288
 
288
289
  return {