Source code for aequilibrae.utils.spatialite_utils
import logging
import os
import shutil
import urllib
import warnings
from os.path import join, basename
from pathlib import Path
from sqlite3 import Connection, register_adapter
from tempfile import gettempdir
from typing import Optional
from zipfile import ZipFile
import numpy as np
from aequilibrae.log import global_logger
from aequilibrae.utils.db_utils import has_table, safe_connect
from aequilibrae.utils.qgis_utils import inside_qgis
# Setup adapaters so that we can read/write numpy types directly to DB
register_adapter(np.int64, int)
register_adapter(np.int32, int)
register_adapter(np.float32, float)
register_adapter(np.float64, float)
register_adapter(np.object_, str)
[docs]
def is_windows():
return os.name == "nt"
[docs]
def is_not_windows():
return os.name != "nt"
[docs]
def connect_spatialite(path_to_file: os.PathLike, missing_ok: bool = False) -> Connection:
if inside_qgis:
import qgis
return qgis.utils.spatialite_connect(path_to_file)
ensure_spatialite_binaries()
return _connect_spatialite(path_to_file, missing_ok)
def _connect_spatialite(path_to_file: os.PathLike, missing_ok: bool = False):
conn = safe_connect(path_to_file, missing_ok)
conn.enable_load_extension(True)
conn.load_extension("mod_spatialite")
return conn
[docs]
def is_spatialite(conn):
return has_table(conn, "geometry_columns")
[docs]
def ensure_spatialite_binaries(directory: Optional[os.PathLike] = None) -> None:
if is_not_windows():
return
directory = directory or gettempdir()
if not _dll_already_exists(directory):
_download_and_extract_spatialite(directory)
# Update path and proj_lib env vars
if directory not in os.environ["PATH"]:
os.environ["PATH"] = directory + os.pathsep + os.environ["PATH"]
if "PROJ_LIB" not in os.environ:
os.environ["PROJ_LIB"] = directory
try:
# We need to have the proj.db file in place.
# The easiest one on Windows is in the public user. On Linux it should not be necessary
# See why: https://www.gaia-gis.it/fossil/libspatialite/wiki?name=PROJ.6
projdb_dir = "C:/Users/Public/spatialite/proj"
Path(projdb_dir).mkdir(parents=True, exist_ok=True)
if os.path.isfile(join(projdb_dir, "proj.db")):
return
shutil.copyfile(join(directory, "proj.db"), join(projdb_dir, "proj.db"))
except Exception as e:
msg = f"Could not put the proj.db file in the expected place. {e.args}"
warnings.warn(msg)
global_logger.warning(msg)
def _dll_already_exists(d: os.PathLike) -> bool:
return os.path.exists(join(d, "mod_spatialite.dll"))
def _download_and_extract_spatialite(directory: os.PathLike) -> None:
url = "https://github.com/AequilibraE/aequilibrae/releases/download/V.0.7.5/mod_spatialite-5.0.1-win-amd64.zip"
zip_file = join(directory, basename(url))
Path(directory).mkdir(exist_ok=True, parents=True)
urllib.request.urlretrieve(url, zip_file)
ZipFile(zip_file).extractall(directory)
os.remove(zip_file)
[docs]
def spatialize_db(conn, logger=None):
logger = logger or logging.getLogger("aequilibrae")
logger.info("Adding Spatialite infrastructure to the database")
if not inside_qgis and not is_spatialite(conn):
try:
conn.execute("SELECT InitSpatialMetaData();")
conn.commit()
except Exception as e:
logger.error("Problem with spatialite", e.args)
raise e
if not is_spatialite(conn):
raise RuntimeError("Something went wrong")