-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Added new functions and updated README
- Added locate function from coordinates - Added get_metadata function - Improved search function - Improved code documentation - Updated README
- Loading branch information
Showing
5 changed files
with
452 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,111 @@ | ||
# SIGPAC-Tools | ||
SIGPAC-Tools is a custom Python library that provides the user a comfortable client to retrieve data from SIGPAC api, saving the user from downloading and storing heavy files. | ||
# [SIGPAC-Tools](https://github.com/KhaosResearch/sigpac-tools) | ||
|
||
![Python >=3.10](https://img.shields.io/badge/python-%3E=3.10-blue.svg) | ||
![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg) | ||
![Status: Active](https://img.shields.io/badge/Status-Active-00aa00.svg) | ||
![Code style: Ruff](https://img.shields.io/badge/code%20style-Ruff-aa0000.svg) | ||
|
||
|
||
SIGPAC-Tools is a Python library that provides the user a set of tools to work with the SIGPAC data. The library is designed to be easy to use and to provide the user the ability to work with the data in a simple way. | ||
|
||
This library is designed to be used in conjunction with the SIGPAC data provided by the Spanish government. The data is available in the [SIGPAC website](https://sigpac.mapa.gob.es/fega/visor/). | ||
|
||
## Installation | ||
|
||
To install the library, you can use the following command: | ||
|
||
```bash | ||
git clone https://github.com/KhaosResearch/sigpac-tools | ||
python -m pip install ./sigpac-tools | ||
``` | ||
|
||
or you can install it directly from the repository: | ||
|
||
```bash | ||
python -m pip install https://github.com/KhaosResearch/sigpac-tools | ||
``` | ||
|
||
## Usage | ||
|
||
Once the library is installed, you can use it in your Python code. The library provides a set of tools to work with the SIGPAC data, these are the main features: | ||
|
||
### Search for an specific plot | ||
|
||
You can search for an specific plot using the `search` function in the `search` module. This function will return a list of plots that match the search criteria. | ||
|
||
```python | ||
from sigpac_tools.search import search | ||
|
||
data = { | ||
"community": 1, | ||
"province": 1, | ||
"municipality": 8, | ||
"polygon": 8, | ||
"parcel": 572 | ||
} | ||
|
||
geojson = search(data) | ||
``` | ||
|
||
It is important to note that the `search` function will return a GeoJSON object with the information of the plots that match the search criteria. The `data` dictionary must contain at least the 'province' or the 'community' key to return a valid result. Note that all the keys must be in lower case. | ||
|
||
### Get the information of a plot | ||
|
||
You can get the information of a plot using the `get_metadata` function in the `anotate` module. This function will return a dictionary with the information of the plot, given the same data as the `search` function. | ||
|
||
```python | ||
from sigpac_tools.anotate import get_metadata | ||
|
||
layer = "parcela" | ||
data = { | ||
"community": 1, | ||
"province": 1, | ||
"municipality": 8, | ||
"polygon": 8, | ||
"parcel": 572, | ||
# "enclosure": 1, | ||
# "aggregate": 0, | ||
# "zone": 0 | ||
} | ||
|
||
metadata = get_metadata( | ||
layer, | ||
data | ||
) | ||
``` | ||
|
||
> `enclosure` is only necesary if the layer is "recinto". `aggregate` and `zone` are optional and depend on the location of the parcel or enclosure searched for. | ||
### Get the geometry of a plot | ||
|
||
Given the coordinates of the plot, that may be gathered from the function `search`, you can get the geometry of the plot using the `geometry_from_coords` function in the `locate` module. This function will return a GeoJSON object with the geometry of the plot. | ||
|
||
The user may specify a reference parcel or enclosure to get the geometry of and so on the function will return the geometry of that specific parcel or enclosure if it exists. If the reference parcel or enclosure does not exist, the function will return `None`. | ||
|
||
If the reference parcel or enclosure is not provided, the function will return a Feature Collection with all the parcels that match the search criteria. | ||
|
||
```python | ||
from sigpac_tools.locate import geometry_from_coords | ||
|
||
layer = "parcela" | ||
lat = 37.384 | ||
lng = -4.98 | ||
reference = None | ||
|
||
geometry = geometry_from_coords( | ||
layer, | ||
lat, | ||
lng, | ||
reference | ||
) | ||
``` | ||
|
||
## Acknowledgements | ||
|
||
This inspired by the JavaScript [SIGPAC client](https://github.com/dan96ct/sigpac-client) made by Daniel Cebrián. | ||
|
||
## License | ||
|
||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. | ||
|
||
Copyright 2023 Khaos Research, all rights reserved. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import requests | ||
import structlog | ||
|
||
from sigpac_tools._globals import BASE_URL | ||
|
||
logger = structlog.get_logger() | ||
|
||
|
||
def __query( | ||
layer: str, | ||
province: int, | ||
municipality: int, | ||
polygon: int, | ||
parcel: int, | ||
enclosure: int, | ||
aggregate: int, | ||
zone: int, | ||
) -> dict: | ||
"""Queries the SIGPAC database for the given location parameters and layer | ||
Parameters | ||
---------- | ||
layer : str | ||
Layer to search from ("parcela", "recinto") | ||
province : int | ||
Province code | ||
municipality : int | ||
Municipality code | ||
polygon : int | ||
Polygon code | ||
parcel : int | ||
Parcel code | ||
enclosure : int | ||
Enclosure code | ||
aggregate : int | ||
Aggregate code | ||
zone : int | ||
Zone code | ||
Returns | ||
------- | ||
dict | ||
Dictionary with the metadata of the SIGPAC database | ||
Raises | ||
------ | ||
KeyError | ||
If the layer is not supported | ||
""" | ||
match layer.lower(): | ||
case "parcela": | ||
logger.info("Searching for the parcel specified") | ||
url = f"{BASE_URL}/fega/ServiciosVisorSigpac/LayerInfo" | ||
params = { | ||
"layer": layer, | ||
"id": f"{province},{municipality},{aggregate},{zone},{polygon},{parcel}", | ||
} | ||
response = requests.get(url, params=params) | ||
return response.json() | ||
case "recinto": | ||
logger.info("Searching for the enclosure specified") | ||
url = f"{BASE_URL}/fega/ServiciosVisorSigpac/LayerInfo" | ||
params = { | ||
"layer": layer, | ||
"id": f"{province},{municipality},{aggregate},{zone},{polygon},{parcel},{enclosure}", | ||
} | ||
response = requests.get(url, params=params) | ||
print(response.url) | ||
return response.json() | ||
case _: | ||
logger.error( | ||
"Layer not supported. Supported layers: ['parcela', 'recinto']" | ||
) | ||
raise KeyError( | ||
"Layer not supported. Supported layers: ['parcela', 'recinto']" | ||
) | ||
|
||
|
||
def get_metadata(layer: str, data: dict): | ||
"""Get the metadata of the given location from the SIGPAC database | ||
It searches for the metadata of the given location in the SIGPAC database. The search can be done by specifying the layer, province, municipality, polygon and parcel. | ||
The level of detail of the search can be increased by specifying the layer to search between "parcela" and "recinto". | ||
Parameters | ||
---------- | ||
layer : str | ||
Layer to search from ("parcela", "recinto") | ||
data : dict | ||
Dictionary with the data of the location to search. It must be a dictionary with the following keys: [ province, municipality, aggregate, zone, polygon, parcel ] | ||
Returns | ||
------- | ||
dict | ||
Dictionary with the metadata of the SIGPAC database | ||
Raises | ||
------ | ||
ValueError | ||
If the layer, province, municipality, polygon or parcel is not specified | ||
KeyError | ||
If the layer is not supported | ||
""" | ||
if not layer: | ||
logger.error("Layer not specified") | ||
raise ValueError("Layer not specified") | ||
elif layer not in ["parcela", "recinto"]: | ||
logger.error("Layer not supported. Supported layers: ['parcela', 'recinto']") | ||
raise KeyError("Layer not supported. Supported layers: ['parcela', 'recinto']") | ||
|
||
prov = data.get("province", None) | ||
muni = data.get("municipality", None) | ||
polg = data.get("polygon", None) | ||
parc = data.get("parcel", None) | ||
encl = data.get("enclosure", None) | ||
aggr = data.get("aggregate", 0) | ||
zone = data.get("zone", 0) | ||
|
||
if not prov: | ||
logger.error("Province not specified") | ||
raise ValueError("Province not specified") | ||
if not muni: | ||
logger.error("Municipality not specified") | ||
raise ValueError("Municipality not specified") | ||
if not polg: | ||
logger.error("Polygon not specified") | ||
raise ValueError("Polygon not specified") | ||
if not parc: | ||
logger.error("Parcel not specified") | ||
raise ValueError("Parcel not specified") | ||
if not encl and layer == "recinto": | ||
logger.error("Enclosure not specified") | ||
raise ValueError("Enclosure not specified") | ||
|
||
logger.info(f"Searching for the metadata of the location (province {prov}, municipality {muni}, polygon {polg}, parcel {parc}) in the SIGPAC database...") | ||
return __query( | ||
layer=layer, | ||
province=prov, | ||
municipality=muni, | ||
polygon=polg, | ||
parcel=parc, | ||
enclosure=encl, | ||
aggregate=aggr, | ||
zone=zone, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import requests | ||
import structlog | ||
|
||
from sigpac_tools._globals import BASE_URL | ||
from sigpac_tools.utils import lng_lat_to_tile, transform_coords | ||
|
||
logger = structlog.get_logger() | ||
|
||
|
||
def __locate_in_feature_collection( | ||
reference: int, layer: str, featureCollection: dict | ||
) -> dict | None: | ||
"""Locates the given reference in the feature collection given the layer to search from | ||
Parameters | ||
---------- | ||
reference : int | ||
Reference to search | ||
layer : str | ||
Layer to search from ("parcela", "recinto") | ||
featureCollection : dict | ||
Geojson feature collection to search from | ||
Returns: | ||
dict | None | ||
Geojson geometry of the found reference. If not found, returns `None` | ||
""" | ||
for feature in featureCollection["features"]: | ||
if feature["properties"][layer] == reference: | ||
transform_coords(feature) | ||
return feature["geometry"] | ||
return None | ||
|
||
|
||
def geometry_from_coords(layer: str, lat: float, lon: float, reference: int) -> dict: | ||
"""Gets the geometry of the given coordinates and reference in the given layer | ||
Parameters | ||
---------- | ||
layer : str | ||
Layer to search from ("parcela", "recinto") | ||
lat : float | ||
Latitude of the location | ||
lon : float | ||
Longitude of the location | ||
reference : int | ||
Reference to search for | ||
Returns | ||
------- | ||
dict | ||
Geojson geometry of the found reference | ||
Raises | ||
------ | ||
ValueError | ||
If the layer, latitude, longitude or reference is not specified | ||
KeyError | ||
If the layer is not supported | ||
""" | ||
if not layer or not lat or not lon or not reference: | ||
logger.error("Layer, latitude, longitude or reference not specified") | ||
raise ValueError("Layer, latitude, longitude or reference not specified") | ||
|
||
tile_x, tile_y = lng_lat_to_tile(lon, lat, 15) | ||
|
||
response = requests.get( | ||
f"{BASE_URL}/vectorsdg/vector/{layer}@3857/15.{tile_x}.{tile_y}.geojson" | ||
) | ||
|
||
geojson_features = response.json() | ||
|
||
if layer in ["parcela", "recinto"]: | ||
logger.info(f"Searching for reference {reference} in the layer {layer}...") | ||
result = __locate_in_feature_collection( | ||
reference=reference, layer=layer, featureCollection=geojson_features | ||
) | ||
if not result: | ||
logger.warning( | ||
f"Reference '{reference}' not found in the layer '{layer}' at coordinates ({lat}, {lon})" | ||
) | ||
return result | ||
|
||
else: | ||
logger.error( | ||
f'Layer "{layer}" not supported. Supported layers: "parcela", "recinto"' | ||
) | ||
raise KeyError( | ||
f'Layer "{layer}" not supported. Supported layers: "parcela", "recinto"' | ||
) |
Oops, something went wrong.