Module water_security.utils.geo

Expand source code
import os
import urllib.request
import xml.etree.ElementTree as ET
from math import sqrt
from typing import TypedDict

import haversine as hs
import requests
from tqdm import tqdm
from tqdm.notebook import tqdm

TWO_TO_THREE_LETTER_CODE = {
    "AF": "AFG",
    "AX": "ALA",
    "AL": "ALB",
    "DZ": "DZA",
    "AS": "ASM",
    "AD": "AND",
    "AO": "AGO",
    "AI": "AIA",
    "AQ": "ATA",
    "AG": "ATG",
    "AR": "ARG",
    "AM": "ARM",
    "AW": "ABW",
    "AU": "AUS",
    "AT": "AUT",
    "AZ": "AZE",
    "BS": "BHS",
    "BH": "BHR",
    "BD": "BGD",
    "BB": "BRB",
    "BY": "BLR",
    "BE": "BEL",
    "BZ": "BLZ",
    "BJ": "BEN",
    "BM": "BMU",
    "BT": "BTN",
    "BO": "BOL",
    "BA": "BIH",
    "BW": "BWA",
    "BV": "BVT",
    "BR": "BRA",
    "IO": "IOT",
    "BN": "BRN",
    "BG": "BGR",
    "BF": "BFA",
    "BI": "BDI",
    "KH": "KHM",
    "CM": "CMR",
    "CA": "CAN",
    "CV": "CPV",
    "KY": "CYM",
    "CF": "CAF",
    "TD": "TCD",
    "CL": "CHL",
    "CN": "CHN",
    "CX": "CXR",
    "CC": "CCK",
    "CO": "COL",
    "KM": "COM",
    "CG": "COG",
    "CD": "COD",
    "CK": "COK",
    "CR": "CRI",
    "CI": "CIV",
    "HR": "HRV",
    "CU": "CUB",
    "CY": "CYP",
    "CZ": "CZE",
    "DK": "DNK",
    "DJ": "DJI",
    "DM": "DMA",
    "DO": "DOM",
    "EC": "ECU",
    "EG": "EGY",
    "SV": "SLV",
    "GQ": "GNQ",
    "ER": "ERI",
    "EE": "EST",
    "ET": "ETH",
    "FK": "FLK",
    "FO": "FRO",
    "FJ": "FJI",
    "FI": "FIN",
    "FR": "FRA",
    "GF": "GUF",
    "PF": "PYF",
    "TF": "ATF",
    "GA": "GAB",
    "GM": "GMB",
    "GE": "GEO",
    "DE": "DEU",
    "GH": "GHA",
    "GI": "GIB",
    "GR": "GRC",
    "GL": "GRL",
    "GD": "GRD",
    "GP": "GLP",
    "GU": "GUM",
    "GT": "GTM",
    "GG": "GGY",
    "GN": "GIN",
    "GW": "GNB",
    "GY": "GUY",
    "HT": "HTI",
    "HM": "HMD",
    "VA": "VAT",
    "HN": "HND",
    "HK": "HKG",
    "HU": "HUN",
    "IS": "ISL",
    "IN": "IND",
    "ID": "IDN",
    "IR": "IRN",
    "IQ": "IRQ",
    "IE": "IRL",
    "IM": "IMN",
    "IL": "ISR",
    "IT": "ITA",
    "JM": "JAM",
    "JP": "JPN",
    "JE": "JEY",
    "JO": "JOR",
    "KZ": "KAZ",
    "KE": "KEN",
    "KI": "KIR",
    "KP": "PRK",
    "KR": "KOR",
    "KW": "KWT",
    "KG": "KGZ",
    "LA": "LAO",
    "LV": "LVA",
    "LB": "LBN",
    "LS": "LSO",
    "LR": "LBR",
    "LY": "LBY",
    "LI": "LIE",
    "LT": "LTU",
    "LU": "LUX",
    "MO": "MAC",
    "MK": "MKD",
    "MG": "MDG",
    "MW": "MWI",
    "MY": "MYS",
    "MV": "MDV",
    "ML": "MLI",
    "MT": "MLT",
    "MH": "MHL",
    "MQ": "MTQ",
    "MR": "MRT",
    "MU": "MUS",
    "YT": "MYT",
    "MX": "MEX",
    "FM": "FSM",
    "MD": "MDA",
    "MC": "MCO",
    "MN": "MNG",
    "ME": "MNE",
    "MS": "MSR",
    "MA": "MAR",
    "MZ": "MOZ",
    "MM": "MMR",
    "NA": "NAM",
    "NR": "NRU",
    "NP": "NPL",
    "NL": "NLD",
    "AN": "ANT",
    "NC": "NCL",
    "NZ": "NZL",
    "NI": "NIC",
    "NE": "NER",
    "NG": "NGA",
    "NU": "NIU",
    "NF": "NFK",
    "MP": "MNP",
    "NO": "NOR",
    "OM": "OMN",
    "PK": "PAK",
    "PW": "PLW",
    "PS": "PSE",
    "PA": "PAN",
    "PG": "PNG",
    "PY": "PRY",
    "PE": "PER",
    "PH": "PHL",
    "PN": "PCN",
    "PL": "POL",
    "PT": "PRT",
    "PR": "PRI",
    "QA": "QAT",
    "RE": "REU",
    "RO": "ROU",
    "RU": "RUS",
    "RW": "RWA",
    "BL": "BLM",
    "SH": "SHN",
    "KN": "KNA",
    "LC": "LCA",
    "MF": "MAF",
    "PM": "SPM",
    "VC": "VCT",
    "WS": "WSM",
    "SM": "SMR",
    "ST": "STP",
    "SA": "SAU",
    "SN": "SEN",
    "RS": "SRB",
    "SC": "SYC",
    "SL": "SLE",
    "SG": "SGP",
    "SK": "SVK",
    "SI": "SVN",
    "SB": "SLB",
    "SO": "SOM",
    "ZA": "ZAF",
    "GS": "SGS",
    "ES": "ESP",
    "LK": "LKA",
    "SD": "SDN",
    "SR": "SUR",
    "SJ": "SJM",
    "SZ": "SWZ",
    "SE": "SWE",
    "CH": "CHE",
    "SY": "SYR",
    "TW": "TWN",
    "TJ": "TJK",
    "TZ": "TZA",
    "TH": "THA",
    "TL": "TLS",
    "TG": "TGO",
    "TK": "TKL",
    "TO": "TON",
    "TT": "TTO",
    "TN": "TUN",
    "TR": "TUR",
    "TM": "TKM",
    "TC": "TCA",
    "TV": "TUV",
    "UG": "UGA",
    "UA": "UKR",
    "AE": "ARE",
    "GB": "GBR",
    "US": "USA",
    "UM": "UMI",
    "UY": "URY",
    "UZ": "UZB",
    "VU": "VUT",
    "VE": "VEN",
    "VN": "VNM",
    "VG": "VGB",
    "VI": "VIR",
    "WF": "WLF",
    "EH": "ESH",
    "YE": "YEM",
    "ZM": "ZMB",
    "ZW": "ZWE",
}


class PlaceInfo(TypedDict):
    city: str
    country: str
    code: str


def get_elevation(latitude: float, longitude: float):
    """
    Returns coarse modeled elevation from gtopo30 for a specific latitude and longitude
    """
    req = requests.get(
        f"http://api.geonames.org/gtopo30?lat={latitude}&lng={longitude}&username=vaslem"
    )
    return int(req.text)


def get_place(latitude: float, longitude: float) -> PlaceInfo:
    """
    Returns city, country and country 3-letter code, given latitude and longitude
    Raises if the supplied coordinates cannot be matched to a specific country (eg if those refer to sea area)
    """
    req = requests.get(
        f"http://api.geonames.org/findNearbyPlaceName?lat={latitude}&lng={longitude}&cities=cities15000&username=vaslem"
    )
    tree = ET.fromstring(req.text)
    geoname = tree.find("geoname")
    try:
        city = geoname.find("toponymName").text
        country = geoname.find("countryName").text
        code = geoname.find("countryCode").text
        return {
            "city": city,
            "country": country,
            "code": TWO_TO_THREE_LETTER_CODE[code],
        }
    except AttributeError:
        raise


def is_close(loc1, loc2, thres: float = 3) -> bool:
    """
    Accepts 2 points defined by 2 coordinates (iterables of size 2) and based on a distance threshold (in km),
    returns whether those points are close or not to each other
    """
    return hs.haversine(loc1, loc2) < thres


import rasterio
import numpy as np

## Answer to
## https://stackoverflow.com/questions/60127026/python-how-do-i-get-the-pixel-values-from-an-geotiff-map-by-coordinate
def get_coordinate_pixel(
    map: str, lat: float, lon: float, pixels_width: int, pixels_height: int, crs="WGS84"
) -> np.ndarray:
    """
    Given a geotif map, open it,
    read the pixels values that correspond to the provided latitude and longitude,
    with a specific bounding box pixels_width, pixels_height, and return them
    """
    # open map
    with rasterio.open(map, crs=crs) as dataset:
        # get pixel x+y of the coordinate
        py, px = dataset.index(lat, lon)
        # create 1x1px window of the pixel
        window = rasterio.windows.Window(
            px - pixels_width // 2, py - pixels_height // 2, pixels_width, pixels_height
        )
        # read rgb values of the window
        return dataset.read(window=window)


CHECKED_DENSITY_SOURCE = False


def check_1k_population_density_source():
    global CHECKED_DENSITY_SOURCE
    from data.unlabeled import POPULATION_DENSITY_PATH, POPULATION_DENSITY_URL

    if not CHECKED_DENSITY_SOURCE:
        try:
            get_coordinate_pixel(
                POPULATION_DENSITY_PATH, 0, 0, pixels_width=3, pixels_height=3
            )

            rasterio.open(POPULATION_DENSITY_PATH)
            CHECKED_DENSITY_SOURCE = True
        except:
            pass
    if not CHECKED_DENSITY_SOURCE:
        print("Population Density Map is not available, it will be downloaded")
        from utils.download import download_url

        try:
            return download_url(POPULATION_DENSITY_URL, POPULATION_DENSITY_PATH)
        except KeyboardInterrupt:
            try:
                os.remove(POPULATION_DENSITY_PATH)
            except:
                pass


def get_average_1k_population_density(longitude: float, latitude: float) -> int:
    """
    Based on provided longitude and latitude, get the median population density, as computed
    in a 7x7 square around the pixel of the population density geotif map located in  POPULATION_DENSITY_PATH.
    """
    check_1k_population_density_source()
    from data.unlabeled import POPULATION_DENSITY_PATH

    oret = get_coordinate_pixel(
        POPULATION_DENSITY_PATH, latitude, longitude, pixels_width=7, pixels_height=7
    ).squeeze()
    cnt = 3
    ret = oret
    while cnt:
        res = np.median(ret[ret > 0])
        if res > 1:
            return res
        cnt -= 1
        ret = ret[1:-1, -1:1]
    return np.median(oret[oret > 0])

Functions

def check_1k_population_density_source()
Expand source code
def check_1k_population_density_source():
    global CHECKED_DENSITY_SOURCE
    from data.unlabeled import POPULATION_DENSITY_PATH, POPULATION_DENSITY_URL

    if not CHECKED_DENSITY_SOURCE:
        try:
            get_coordinate_pixel(
                POPULATION_DENSITY_PATH, 0, 0, pixels_width=3, pixels_height=3
            )

            rasterio.open(POPULATION_DENSITY_PATH)
            CHECKED_DENSITY_SOURCE = True
        except:
            pass
    if not CHECKED_DENSITY_SOURCE:
        print("Population Density Map is not available, it will be downloaded")
        from utils.download import download_url

        try:
            return download_url(POPULATION_DENSITY_URL, POPULATION_DENSITY_PATH)
        except KeyboardInterrupt:
            try:
                os.remove(POPULATION_DENSITY_PATH)
            except:
                pass
def get_average_1k_population_density(longitude: float, latitude: float) ‑> int

Based on provided longitude and latitude, get the median population density, as computed in a 7x7 square around the pixel of the population density geotif map located in POPULATION_DENSITY_PATH.

Expand source code
def get_average_1k_population_density(longitude: float, latitude: float) -> int:
    """
    Based on provided longitude and latitude, get the median population density, as computed
    in a 7x7 square around the pixel of the population density geotif map located in  POPULATION_DENSITY_PATH.
    """
    check_1k_population_density_source()
    from data.unlabeled import POPULATION_DENSITY_PATH

    oret = get_coordinate_pixel(
        POPULATION_DENSITY_PATH, latitude, longitude, pixels_width=7, pixels_height=7
    ).squeeze()
    cnt = 3
    ret = oret
    while cnt:
        res = np.median(ret[ret > 0])
        if res > 1:
            return res
        cnt -= 1
        ret = ret[1:-1, -1:1]
    return np.median(oret[oret > 0])
def get_coordinate_pixel(map: str, lat: float, lon: float, pixels_width: int, pixels_height: int, crs='WGS84') ‑> numpy.ndarray

Given a geotif map, open it, read the pixels values that correspond to the provided latitude and longitude, with a specific bounding box pixels_width, pixels_height, and return them

Expand source code
def get_coordinate_pixel(
    map: str, lat: float, lon: float, pixels_width: int, pixels_height: int, crs="WGS84"
) -> np.ndarray:
    """
    Given a geotif map, open it,
    read the pixels values that correspond to the provided latitude and longitude,
    with a specific bounding box pixels_width, pixels_height, and return them
    """
    # open map
    with rasterio.open(map, crs=crs) as dataset:
        # get pixel x+y of the coordinate
        py, px = dataset.index(lat, lon)
        # create 1x1px window of the pixel
        window = rasterio.windows.Window(
            px - pixels_width // 2, py - pixels_height // 2, pixels_width, pixels_height
        )
        # read rgb values of the window
        return dataset.read(window=window)
def get_elevation(latitude: float, longitude: float)

Returns coarse modeled elevation from gtopo30 for a specific latitude and longitude

Expand source code
def get_elevation(latitude: float, longitude: float):
    """
    Returns coarse modeled elevation from gtopo30 for a specific latitude and longitude
    """
    req = requests.get(
        f"http://api.geonames.org/gtopo30?lat={latitude}&lng={longitude}&username=vaslem"
    )
    return int(req.text)
def get_place(latitude: float, longitude: float) ‑> PlaceInfo

Returns city, country and country 3-letter code, given latitude and longitude Raises if the supplied coordinates cannot be matched to a specific country (eg if those refer to sea area)

Expand source code
def get_place(latitude: float, longitude: float) -> PlaceInfo:
    """
    Returns city, country and country 3-letter code, given latitude and longitude
    Raises if the supplied coordinates cannot be matched to a specific country (eg if those refer to sea area)
    """
    req = requests.get(
        f"http://api.geonames.org/findNearbyPlaceName?lat={latitude}&lng={longitude}&cities=cities15000&username=vaslem"
    )
    tree = ET.fromstring(req.text)
    geoname = tree.find("geoname")
    try:
        city = geoname.find("toponymName").text
        country = geoname.find("countryName").text
        code = geoname.find("countryCode").text
        return {
            "city": city,
            "country": country,
            "code": TWO_TO_THREE_LETTER_CODE[code],
        }
    except AttributeError:
        raise
def is_close(loc1, loc2, thres: float = 3) ‑> bool

Accepts 2 points defined by 2 coordinates (iterables of size 2) and based on a distance threshold (in km), returns whether those points are close or not to each other

Expand source code
def is_close(loc1, loc2, thres: float = 3) -> bool:
    """
    Accepts 2 points defined by 2 coordinates (iterables of size 2) and based on a distance threshold (in km),
    returns whether those points are close or not to each other
    """
    return hs.haversine(loc1, loc2) < thres

Classes

class PlaceInfo (*args, **kwargs)

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

Expand source code
class PlaceInfo(TypedDict):
    city: str
    country: str
    code: str

Ancestors

  • builtins.dict

Class variables

var city : str
var code : str
var country : str