apb_extra_osgeo_utils

Package apb_extra_osgeo_utils

Modules to add common functionality to OSGEO GDAL. Requires GDAL library C previously installed (see how here https://gdal.org/download.html#binaries).

Requires GDAL library version 3.6<=3.9 installed.

To install:

pip install apb_extra_osgeo_utils

Documentation here apb_extra_osgeo_utils

   1#  coding=utf-8
   2#
   3#  Author: Ernesto Arredondo Martinez (ernestone@gmail.com)
   4#  Created: 7/6/19 18:23
   5#  Last modified: 7/6/19 14:27
   6#  Copyright (c) 2019
   7"""
   8.. include:: ../README.md
   9"""
  10
  11import logging
  12import os
  13import re
  14from collections import OrderedDict, namedtuple
  15
  16import math
  17from osgeo import ogr, osr
  18from osgeo.ogr import ODsCCreateLayer, OLCAlterFieldDefn, OLCCreateField, ODsCTransactions, \
  19    ODsCDeleteLayer, OLCTransactions, Geometry, ODrCCreateDataSource, GeomFieldDefn
  20
  21from apb_extra_utils import misc as utils
  22
  23SIMPLIFY_TOLERANCE = 1e-6
  24DRIVERS_GDAL_NOT_DELETE_LAYERS = ('POSTGRESQL',)
  25DRIVERS_OLD_SRS_AXIS_MAPPING_4326 = ('GEOJSON', 'GPKG', 'POSTGRESQL')
  26PREFFIX_GEOMS_LAYERS_GDAL = 'geom_'
  27PREFFIX_GEOMS_LAYERS_GDAL_CSV = '_WKT'
  28
  29print_debug = logging.debug
  30print_warning = logging.warning
  31
  32
  33def srs_ref_from_epsg_code(code_epsg: int, old_axis_mapping=False) -> osr.SpatialReference:
  34    """
  35
  36    Args:
  37        code_epsg (int):
  38        old_axis_mapping (bool=False): Por cambio entre GDAL2.0 y GDAL3.0 se (vease
  39    https://github.com/OSGeo/gdal/blob/master/gdal/MIGRATION_GUIDE.TXT) por defecto se utiliza el mapeo de ejes
  40    clasico LONG,LAT
  41
  42    Returns:
  43        srs (osr.SpatialReference)
  44    """
  45    srs = osr.SpatialReference()
  46    if old_axis_mapping:
  47        try:
  48            from osgeo.osr import OAMS_TRADITIONAL_GIS_ORDER
  49            print_debug("OAMS_TRADITIONAL_GIS_ORDER")
  50            srs.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
  51        except ImportError:
  52            print_warning("OAMS_TRADITIONAL_GIS_ORDER not available")
  53
  54    ret = srs.ImportFromEPSG(code_epsg)
  55    if ret != 0:
  56        raise Warning("No se puede retornar un osgeo.osr.SpatialReference EPSG para el codigo '{}'!!".format(code_epsg))
  57
  58    return srs
  59
  60
  61def layer_gdal_from_file(path_file: str,
  62                         nom_driver: str = 'GeoJSON',
  63                         nom_geom: str = None,
  64                         default_srs_epsg_code: int = 4326,
  65                         default_order_long_lat: bool = True):
  66    """
  67
  68    Args:
  69        path_file (str):
  70        nom_driver (str='GeoJSON'):
  71        nom_geom (str=None): si se informa devolverá la layer solo con la geometria especificada
  72        default_srs_epsg_code (int=4326): codigo del sistema de coordenadas que asignará por defecto si la layer
  73                                          NO tiene sistema definido
  74        default_order_long_lat (bool=True): si no viene informado el SRS y se usa el default EPSG4326 entonces se supone
  75                                          por defecto que las geometria vienen en orden antiguo LONGITUD, LATITUD
  76
  77    Returns:
  78        layer_gdal (osgeo.ogr.Layer), nom_layer (str), datasource_gdal (osgeo.ogr.DataSource)
  79    """
  80    drvr = ogr.GetDriverByName(nom_driver)
  81
  82    nom_layer, ext = utils.split_ext_file(os.path.basename(path_file))
  83    if ext.lower().endswith("zip"):
  84        path_file = "/vsizip/{}".format(path_file)
  85
  86    ds_vector_file = drvr.Open(path_file, 0)
  87
  88    a_layer = None
  89    if ds_vector_file:
  90        a_layer = ds_vector_file.GetLayer(0)
  91
  92        if nom_geom:
  93            lay_def = a_layer.GetLayerDefn()
  94            nom_geom = nom_geom.strip().upper()
  95
  96            if lay_def.GetGeomFieldCount() > 1:
  97                idx_geom = lay_def.GetGeomFieldIndex(nom_geom)
  98                if idx_geom < 0:
  99                    raise Exception("El fichero vectorial '{}' no contiene una geometria con el nombre '{}'".format(
 100                        ds_vector_file.GetName(), nom_geom))
 101                elif idx_geom > 0:
 102                    # Convertimos a layer en MEMORY para poder cambiar estructura
 103                    ds_mem = ds_gdal_memory()
 104                    a_layer = create_layer_from_layer_gdal_on_ds_gdal(ds_mem, a_layer, nom_layer, nom_geom,
 105                                                                      exclude_cols_geoms=False, null_geoms=True)
 106                    if a_layer:
 107                        ds_vector_file = ds_mem
 108
 109            elif not a_layer.GetGeometryColumn() and lay_def.GetGeomFieldDefn(0):
 110                lay_def.GetGeomFieldDefn(0).SetName(nom_geom)
 111
 112        if a_layer:
 113            # Por si no carga el SRS se asigna el :default_srs_epsg_code (defecto epsg:4326)
 114            for gf in geom_fields_layer_gdal(a_layer):
 115                if not gf.GetSpatialRef():
 116                    gf.SetSpatialRef(srs_ref_from_epsg_code(default_srs_epsg_code, default_order_long_lat))
 117
 118    return a_layer, nom_layer, ds_vector_file
 119
 120
 121def datasource_gdal_vector_file(nom_driver_gdal, nom_ds, a_dir, create=None, from_zip=False, **create_options):
 122    """
 123    Crea datasource gdal para driver de tipo fichero
 124    Args:
 125        nom_driver_gdal (str):
 126        nom_ds (str):
 127        a_dir (str):
 128        create (bool=None): Por defecto crea el datasource si este no existe y se abre sin problemas previamente.
 129                        Si False entonces NO lo crea en ningún caso, y si True lo creará sin intentar abrirlo
 130        from_zip (bool=False):
 131        **create_options: lista claves valores con opciones de creacion para el datasource de GDAL
 132                        p.e.: NameField="SEQENTITAT", DescriptionField="SW_URN"
 133
 134    Returns:
 135        datasource_gdal (osgeo.ogr.DataSource), overwrited (bool)
 136    """
 137    driver, exts_driver = driver_gdal(nom_driver_gdal)
 138    if not exts_driver:
 139        raise Exception("ERROR! - El driver GDAL {} no es de tipo fichero vectorial".format(driver))
 140
 141    base_path_file = os.path.normpath(os.path.join(a_dir, nom_ds.strip().lower()))
 142    _, ext_base = os.path.splitext(base_path_file)
 143
 144    vsi_prefix = ""
 145    if from_zip:
 146        ext_file = "zip"
 147        vsi_prefix = "/vsizip/"
 148    else:
 149        if "topojson" in (ext.lower() for ext in exts_driver):
 150            ext_file = "topojson"
 151        else:
 152            ext_file = exts_driver[0]
 153
 154    if (os.path.exists(base_path_file) and os.path.isfile(
 155            base_path_file)) or ext_base.lower() == f'.{ext_file.lower()}':
 156        path_file = base_path_file
 157    else:
 158        path_file = "{}.{}".format(base_path_file, ext_file)
 159
 160    overwrited = False
 161    datasource_gdal = None
 162    if not create and os.path.exists(path_file):
 163        open_path_file = "{}{}".format(vsi_prefix, path_file)
 164        datasource_gdal = driver.Open(open_path_file, 1)
 165        if datasource_gdal is None:
 166            datasource_gdal = driver.Open(open_path_file)
 167
 168    if datasource_gdal:
 169        overwrited = True
 170
 171    if create or (create is not False and datasource_gdal is None and driver.TestCapability(ODrCCreateDataSource)):
 172        datasource_gdal = driver.CreateDataSource(path_file,
 173                                                  list("{}={}".format(opt, val) for opt, val in create_options.items()))
 174
 175    return datasource_gdal, overwrited
 176
 177
 178def driver_gdal(nom_driver):
 179    """
 180    Devuelve el driver GDAL solicitado y si es de tipo fichero vectorial tambien devuelve la extension
 181    Si no coincide exactamente devuelve el que tiene nombre más parecido.
 182    Verificar con drivers_ogr_gdal_disponibles() los disponibles
 183
 184    Args:
 185        nom_driver:
 186    Returns:
 187        driver_gdal (osgeo.ogr.Driver), exts_driver (list)
 188    """
 189    driver_gdal = ogr.GetDriverByName(nom_driver)
 190    exts_drvr = driver_gdal.GetMetadata().get('DMD_EXTENSIONS', "").split(" ")
 191
 192    return driver_gdal, exts_drvr
 193
 194
 195def ds_gdal_memory():
 196    """
 197    Devuelve un osgeo.ogr.DataSource de tipo "memory"
 198
 199    Returns:
 200        datasource_gdal (osgeo.ogr.DataSource)
 201    """
 202    return ogr.GetDriverByName("memory").CreateDataSource("")
 203
 204
 205def set_create_option_list_for_layer_gdal(layer_gdal, drvr_name="GPKG", **extra_opt_list):
 206    """
 207    Devuelve la lista de create options GDAL a partir de una layer, el driver para el que se quiere crear y options
 208    pasadas por defecto
 209
 210    Args:
 211        layer_gdal (ogr.Layer):
 212        drvr_name (str="GPKG"): nombre de driver GDAL
 213        extra_opt_list: lista pares claves-valores que se corresponden con option create list del driver GDAL
 214
 215    Returns:
 216        opt_list (dict)
 217    """
 218    opt_list = set_create_option_list_for_driver_gdal(drvr_name, **extra_opt_list)
 219
 220    opt_geom_name = "GEOMETRY_NAME"
 221    if opt_geom_name not in opt_list:
 222        nom_geom_src = layer_gdal.GetGeometryColumn()
 223        if nom_geom_src:
 224            opt_list[opt_geom_name] = "{}={}".format(opt_geom_name, nom_geom_src)
 225
 226    return opt_list
 227
 228
 229def set_create_option_list_for_driver_gdal(drvr_name="GPKG", **extra_opt_list):
 230    """
 231    Devuelve la lista de create options GDAL a partir de una layer, el driver para el que se quiere crear y options
 232    pasadas por defecto
 233
 234    Args:
 235        drvr_name (str="GPKG"): nombre de driver GDAL
 236        extra_opt_list: lista pares claves-valores que se corresponden con option create list del driver GDAL
 237
 238    Returns:
 239        opt_list (dict)
 240    """
 241    # Se quitan las options que no son de creacion para el driver especificado
 242    drvr, exts_drvr = driver_gdal(drvr_name)
 243    if not drvr:
 244        Exception("!ERROR! - El nombre de driver '{}' no es un driver GDAL disponible".format(drvr_name))
 245
 246    opt_list = {k.upper(): v.upper() for k, v in extra_opt_list.items()}
 247
 248    if not "FID" in opt_list and drvr.name == 'GPKG':
 249        opt_list["FID"] = 'FID=FID_GPKG'
 250
 251    if drvr.name == "GeoJSON":
 252        if "WRITE_BBOX" not in opt_list:
 253            opt_list["WRITE_BBOX"] = 'WRITE_BBOX=YES'
 254
 255    if drvr.name == "CSV":
 256        if "CREATE_CSVT" not in opt_list:
 257            opt_list["CREATE_CSVT"] = 'CREATE_CSVT=YES'
 258        if "GEOMETRY" not in opt_list:
 259            opt_list["GEOMETRY"] = 'GEOMETRY=AS_WKT'
 260
 261    if drvr.GetMetadataItem('DS_LAYER_CREATIONOPTIONLIST'):
 262        list_opts_drvr = drvr.GetMetadataItem('DS_LAYER_CREATIONOPTIONLIST')
 263        keys_opt_list = list(opt_list.keys())
 264        for n_opt in keys_opt_list:
 265            if list_opts_drvr.find(n_opt) < 0:
 266                opt_list.pop(n_opt)
 267
 268        if "SPATIAL_INDEX" not in opt_list and \
 269                list_opts_drvr.find("SPATIAL_INDEX") >= 0 and drvr.name != 'PostgreSQL':
 270            opt_list["SPATIAL_INDEX"] = 'SPATIAL_INDEX=YES'
 271
 272    return opt_list
 273
 274
 275def copy_layer_gdal_to_ds_gdal(layer_src, ds_gdal_dest, nom_layer=None, nom_geom=None,
 276                               overwrite=True, **extra_opt_list):
 277    """
 278    Copia una layer_gdal a otro datasource gdal
 279
 280    Args:
 281        layer_src:
 282        ds_gdal_dest:
 283        nom_layer (str=None): OPC - Si no viene informado cogerá el nombre de la layer
 284        nom_geom (str=None): OPC - Si se informa se cogerá como el nombre de la geometria de la nueva layer
 285        overwrite (bool=True):
 286        **extra_opt_list (str): Lista claves-valores de opciones para copylayer o createLayer
 287                                del driver de ds_gdal indicado
 288
 289    Returns:
 290        layer_dest (ogr.layer)
 291    """
 292    if not nom_layer:
 293        nom_layer = layer_src.GetName()
 294    nom_layer = nom_layer.strip().lower()
 295
 296    layer_dest = ds_gdal_dest.GetLayerByName(nom_layer)
 297    if layer_dest:
 298        if not overwrite:
 299            return layer_dest
 300        else:
 301            ds_gdal_dest.DeleteLayer(nom_layer)
 302
 303    drvr_name = ds_gdal_dest.GetDriver().GetName()
 304
 305    extra_opt_list["IDENTIFIER"] = "IDENTIFIER={}".format(nom_layer)
 306
 307    opt_list = set_create_option_list_for_layer_gdal(layer_src, drvr_name=drvr_name,
 308                                                     **{k.upper(): v.upper() for k, v in extra_opt_list.items()})
 309
 310    layer_dest = ds_gdal_dest.CopyLayer(layer_src, nom_layer, list(opt_list.values()))
 311
 312    if layer_dest:
 313        if not layer_dest.GetGeometryColumn() and layer_dest.GetLayerDefn().GetGeomFieldDefn(0) and \
 314                (layer_src.GetGeometryColumn() or nom_geom):
 315            if not nom_geom:
 316                nom_geom = layer_src.GetGeometryColumn()
 317            else:
 318                nom_geom = nom_geom.upper()
 319
 320            layer_dest.GetLayerDefn().GetGeomFieldDefn(0).SetName(nom_geom)
 321
 322        if drvr_name == "GPKG":
 323            create_spatial_index_layer_gpkg(ds_gdal_dest, nom_layer)
 324
 325    return layer_dest
 326
 327
 328def layer_gtype_from_geoms(layer_gdal, nom_geom=None):
 329    """
 330    A partir de la 1º geometria informada de una layer_gdal devuelve el tipo de geometria que es. Si no encuentra
 331    devuelve el geom_type de la layer (layer_gdal.GetGeomType())
 332
 333    Args:
 334        layer_gdal (osgeo.ogr.Layer):
 335        nom_geom (str=None): Nombre geometria
 336
 337    Returns:
 338        geom_type (int=layer_gdal.GetGeomType()): Si no encuentra devuelve 0 por defecto (GEOMETRY)
 339    """
 340    idx_geom = 0
 341    if nom_geom:
 342        idx_geom = layer_gdal.GetLayerDefn().GetGeomFieldIndex(nom_geom)
 343
 344    if idx_geom >= 0:
 345        layer_gdal.ResetReading()
 346        return next((f.GetGeomFieldRef(idx_geom).GetGeometryType()
 347                     for f in layer_gdal if f.GetGeomFieldRef(idx_geom)),
 348                    layer_gdal.GetGeomType())
 349
 350
 351def srs_for_layer(layer_src, nom_geom_src=None):
 352    """
 353    Devuelve el SpatialReference de la layer_gdal
 354    Args:
 355        layer_src (ogr.Layer)
 356        nom_geom_src (str=None): Nombre geometria
 357
 358    Returns:
 359        srs_lyr_src (osgeo.osr.SpatialReference)
 360    """
 361    srs_lyr_src = layer_src.GetSpatialRef()
 362    layer_src_def = layer_src.GetLayerDefn()
 363
 364    if act_geom_field := layer_src_def.GetGeomFieldDefn(0):
 365        if nom_geom_src:
 366            idx_act_geom_field = layer_src_def.GetGeomFieldIndex(nom_geom_src)
 367            if idx_act_geom_field >= 0:
 368                act_geom_field = layer_src_def.GetGeomFieldDefn(idx_act_geom_field)
 369
 370        if act_geom_field.GetSpatialRef():
 371            srs_lyr_src = act_geom_field.GetSpatialRef()
 372
 373    return srs_lyr_src
 374
 375
 376def create_layer_from_layer_gdal_on_ds_gdal(ds_gdal_dest, layer_src, nom_layer=None, nom_geom_src=None, sel_camps=None,
 377                                            exclude_cols_geoms=True, tolerance_simplify=None, null_geoms=False,
 378                                            gtype_layer_from_geoms=True, epsg_code_dest=None,
 379                                            epsg_code_src_default=4326, old_axis_mapping_srs=None, **extra_opt_list):
 380    """
 381    Crea nuevo layer a partir de layer_gdal.
 382
 383    Args:
 384        ds_gdal_dest (ogr.DataSource):
 385        layer_src (ogr.Layer):
 386        nom_layer (str=None):
 387        nom_geom_src (str=None): Si el nombre no se corresponde con ninguna de las geometrias de la layer_src se utilizará
 388                            como ALIAS de la geometria 0 (la defecto) de la nueva layer resultante
 389        sel_camps (list=None): OPC - Lista de campos a escoger de la layer original
 390        exclude_cols_geoms (bool=True): Por defecto de la lista de columnas alfanuméricas (no geometrias) excluirá las
 391                                       columnas que hagan referencia a alguna de las geometrias
 392        tolerance_simplify (float=None): Tolerancia (distancia minima) en unidades del srs de la layer
 393             Mirar método Simplify() sobre osgeo.ogr.Geometry
 394        null_geoms (bool=False): Por defecto no grabará las filas que la geometria principal (nom_geom) es nula
 395        gtype_layer_from_geoms (bool=True): Por defecto, si gtype de layer origen == 0 (GEOMETRY) deducirá el tipo de
 396                                geometria (POINT, LINE o POLYGON) a partir de la primera informada
 397                                encontrada en la layer_origen
 398        epsg_code_dest (int=None): Codigo EPSG para le que se transformarán las geometrias desde el SRS original
 399        epsg_code_src_default (int=4326): Codigo EPSG que se usará para las layer_src que NO tengan SRS asignado
 400        old_axis_mapping_srs (bool=None): setea si quieres usar old mapping axes en (LONG, LAT) (GDAL >3.2)  
 401        **extra_opt_list (str): Lista claves-valores de opciones para createLayer del driver de ds_gdal indicado
 402
 403    Returns:
 404        layer_out (ogr.Layer)
 405    """
 406    desc_ds_gdal = ds_gdal_dest.GetDescription()
 407    drvr_name = ds_gdal_dest.GetDriver().GetName().upper()
 408    print_debug("Inici crear layer '{}' en ds_gdal '{}'".format(
 409        (nom_layer if nom_layer else layer_src.GetName()).upper(),
 410        desc_ds_gdal if desc_ds_gdal else drvr_name))
 411
 412    geoms_src = geoms_layer_gdal(layer_src)
 413    camps_src = cols_layer_gdal(layer_src)
 414
 415    if nom_geom_src:
 416        nom_geom_src = nom_geom_src.upper()
 417        if len(geoms_src) > 1:
 418            if nom_geom_src not in map(lambda ng: ng.upper(), geoms_src):
 419                raise Exception("Argumento :NOM_GEOM = '{}' erróneo ya que "
 420                                "LAYER_GDAL original no contiene dicha geometria".format(nom_geom_src))
 421        elif len(geoms_src) == 0:
 422            raise Exception("Argumento :NOM_GEOM = '{}' erróneo ya que "
 423                            "LAYER_GDAL original no contiene geometrias".format(nom_geom_src))
 424
 425    if sel_camps:
 426        sel_camps = {ng.upper() for ng in sel_camps}
 427        if not sel_camps.issubset(camps_src):
 428            raise Exception("Argumento :SEL_CAMPS = '[{}]' erróneo ya que "
 429                            "LAYER_GDAL no contiene alguno de los campos indicados".format(",".join(sel_camps)))
 430    else:
 431        sel_camps = set()
 432
 433    if not nom_layer:
 434        nom_layer = layer_src.GetName()
 435    else:
 436        nom_layer = nom_layer.strip()
 437
 438    nom_layer = nom_layer.lower()
 439
 440    gtype = layer_src.GetGeomType()
 441    srs_lyr_src = layer_src.GetSpatialRef()
 442
 443    layer_src_def = layer_src.GetLayerDefn()
 444    act_geom_field = layer_src_def.GetGeomFieldDefn(0)
 445    nom_geom_out = None
 446
 447    if act_geom_field:
 448        if nom_geom_src:
 449            idx_act_geom_field = layer_src_def.GetGeomFieldIndex(nom_geom_src)
 450            if idx_act_geom_field >= 0:
 451                act_geom_field = layer_src_def.GetGeomFieldDefn(idx_act_geom_field)
 452                nom_geom_out = fix_affix_geom_name_layer_gdal(nom_geom_src, layer_src)
 453
 454        gtype = act_geom_field.GetType()
 455        if gtype_layer_from_geoms and not gtype:
 456            gtype = layer_gtype_from_geoms(layer_src, nom_geom_src)
 457
 458        if act_geom_field.GetSpatialRef():
 459            srs_lyr_src = act_geom_field.GetSpatialRef()
 460
 461    if sel_camps:
 462        sel_camps = {c.strip().upper() for c in sel_camps}
 463
 464    geoms_src_minus_affix = geoms_layer_gdal(layer_src, PREFFIX_GEOMS_LAYERS_GDAL)
 465    if exclude_cols_geoms:
 466        if sel_camps:
 467            sel_camps.difference_update(geoms_src_minus_affix)
 468        else:
 469            sel_camps = cols_layer_gdal(layer_src).difference(geoms_src_minus_affix)
 470
 471    srs_lyr_dest = None
 472    if not srs_lyr_src and epsg_code_src_default:
 473        srs_lyr_src = srs_ref_from_epsg_code(epsg_code_src_default, old_axis_mapping=old_axis_mapping_srs)
 474    if srs_lyr_src:
 475        if old_axis_mapping_srs is None and epsg_code_dest == 4326:
 476            old_axis_mapping_srs = drvr_name in DRIVERS_OLD_SRS_AXIS_MAPPING_4326
 477
 478        if epsg_code_dest:
 479            srs_lyr_dest = srs_ref_from_epsg_code(epsg_code_dest, old_axis_mapping=old_axis_mapping_srs)
 480        else:
 481            srs_lyr_dest = srs_lyr_src
 482
 483    if ds_gdal_dest.TestCapability(ODsCCreateLayer):
 484        # To avoid message "Warning"
 485        if ds_gdal_dest.TestCapability(ODsCDeleteLayer) and ds_gdal_dest.GetLayerByName(nom_layer):
 486            ds_gdal_dest.DeleteLayer(nom_layer)
 487
 488        layer_out, nom_layer = create_layer_on_ds_gdal(ds_gdal_dest, nom_layer, nom_geom_out, gtype, srs_lyr_dest,
 489                                                       **extra_opt_list)
 490    else:
 491        layer_out = ds_gdal_dest.GetLayer(0)
 492
 493    geom_field_out = None
 494    if layer_out.TestCapability(OLCAlterFieldDefn):
 495        layer_out_def = layer_out.GetLayerDefn()
 496        geom_field_out = layer_out_def.GetGeomFieldDefn(0)
 497        if geom_field_out:
 498            if not nom_geom_out:
 499                nom_geom_out = act_geom_field.GetNameRef()
 500                if not nom_geom_out:
 501                    nom_geom_out = "GEOMETRY"
 502            geom_field_out.SetName(nom_geom_out)
 503
 504    if layer_out.TestCapability(OLCCreateField):
 505        for fd in fields_layer_gdal(layer_src):
 506            nom_fd = fd.GetNameRef().upper()
 507            if layer_out.FindFieldIndex(nom_fd, True) < 0 and (not sel_camps or nom_fd in sel_camps) and \
 508                    nom_fd not in (gn.upper() for gn in geoms_src_minus_affix):
 509                layer_out.CreateField(fd)
 510
 511        for gfd in geom_fields_layer_gdal(layer_src):
 512            nom_gfd = fix_affix_geom_name_layer_gdal(gfd.GetNameRef(), layer_src).upper()
 513            if (not sel_camps or nom_gfd in sel_camps) and \
 514                    (nom_gfd not in geoms_src or not exclude_cols_geoms) and \
 515                    nom_gfd != nom_geom_out:
 516                gfd_def_src = layer_src_def.GetGeomFieldDefn(layer_src_def.GetGeomFieldIndex(gfd.GetNameRef()))
 517                gfd_def_dest = GeomFieldDefn(nom_gfd, gfd_def_src.GetType())
 518                if srs_lyr_dest:
 519                    gfd_def_dest.SetSpatialRef(srs_lyr_dest)
 520                layer_out.CreateGeomField(gfd_def_dest)
 521
 522    if not geom_field_out:
 523        null_geoms = True  # Si no hay geom siempre se añaden
 524
 525    add_layer_features_to_layer(layer_src, ds_gdal_dest, layer_out, nom_geom_src, nom_geom_out,
 526                                srs_lyr_dest=srs_lyr_dest,
 527                                null_geoms=null_geoms,
 528                                tolerance_simplify=tolerance_simplify,
 529                                epsg_code_src_default=epsg_code_src_default,
 530                                old_axis_mapping_srs=old_axis_mapping_srs)
 531
 532    if drvr_name == "GPKG":
 533        create_spatial_index_layer_gpkg(ds_gdal_dest, nom_layer)
 534
 535    if drvr_name == 'CSV':
 536        path_csv = ds_gdal_dest.GetName()
 537        if os.path.exists(path_csv):
 538            rename_wkt_geoms_csv(path_csv)
 539
 540    return ds_gdal_dest.GetLayerByName(nom_layer)
 541
 542
 543def rename_wkt_geoms_csv(path_csv):
 544    """
 545    Remove "_WKT" affix from WKT geometry fields in CSV file
 546
 547    Args:
 548        path_csv (str): Path to CSV file
 549
 550    Returns:
 551
 552    """
 553    with open(path_csv, 'r', encoding='utf-8') as r_file:
 554        content_csv = r_file.readlines()
 555        first_line = next(iter(content_csv), None)
 556    if first_line:
 557        new_first_line = first_line.replace(PREFFIX_GEOMS_LAYERS_GDAL_CSV, '')
 558        if new_first_line != first_line:
 559            content_csv[0] = new_first_line
 560            with open(path_csv, 'w', encoding='utf-8') as w_file:
 561                w_file.writelines(content_csv)
 562
 563
 564def add_layer_features_to_layer(layer_src, ds_gdal_dest, layer_dest, nom_geom_src=None, nom_geom_dest=None,
 565                                srs_lyr_dest=None, null_geoms=False, tolerance_simplify=None,
 566                                remove_prev_features=False, epsg_code_src_default=4326, old_axis_mapping_srs=None):
 567    """
 568    From a layer of a dataset, add the features to another layer of another dataset.
 569
 570    Args:
 571        layer_src (ogr.Layer): Layer origen
 572        ds_gdal_dest (gdal.Dataset): Dataset destino
 573        layer_dest (ogr.Layer): Layer destino
 574        nom_geom_src (str=None): Si el nombre no se corresponde con ninguna de las geometrias de la layer_src se utilizará
 575                            como ALIAS de la geometria 0 (la defecto) de la nueva layer resultante
 576        nom_geom_dest (str):
 577        srs_lyr_dest (ogr.SpatialReference=None): Spatial Reference System de la layer_dest
 578        null_geoms (bool=False): Por defecto no grabará las filas que la geometria principal (nom_geom) es nula
 579        tolerance_simplify (float=None): Tolerancia (distancia minima) en unidades del srs de la layer
 580             Mirar método Simplify() sobre osgeo.ogr.Geometry
 581        remove_prev_features (bool=False): Si es True, se eliminarán las features previas de la layer_dest
 582        epsg_code_src_default (int=4326): EPSG code del srs de la layer_src si no se encuentra
 583        old_axis_mapping_srs (bool=None): setea si quieres usar old mapping axes en (LONG, LAT) (GDAL >3.2)
 584
 585    Returns:
 586
 587    """
 588    geoms_out = geoms_layer_gdal(layer_dest)
 589    cols_out = cols_layer_gdal(layer_dest)
 590
 591    geom_transform = None
 592    srs_lyr_src = srs_for_layer(layer_src, nom_geom_src)
 593    if not srs_lyr_src:
 594        srs_lyr_src = srs_ref_from_epsg_code(epsg_code_src_default, old_axis_mapping_srs)
 595
 596    if srs_lyr_dest is None:
 597        srs_lyr_dest = srs_for_layer(layer_dest, nom_geom_dest)
 598
 599    if srs_lyr_dest and not srs_lyr_src.IsSame(srs_lyr_dest):
 600        geom_transform = osr.CoordinateTransformation(srs_lyr_src, srs_lyr_dest)
 601
 602    ds_trans = ds_gdal_dest.TestCapability(ODsCTransactions)
 603
 604    if remove_prev_features:
 605        if ds_trans:
 606            layer_dest.StartTransaction()
 607        for feat in layer_dest:
 608            layer_dest.DeleteFeature(feat.GetFID())
 609        if ds_trans:
 610            layer_dest.CommitTransaction()
 611
 612    if ds_trans:
 613        layer_dest.StartTransaction()
 614
 615    i = 0
 616    for feat_src, geom_src, nt_src in feats_layer_gdal(layer_src, nom_geom_src):
 617        cols_out_chk = [col.upper() for col in cols_out.union(geoms_out)]
 618        vals_camps = {nc: val for nc, val in nt_src._asdict().items()
 619                      if nc.upper() in cols_out_chk}
 620        if null_geoms or geom_src:
 621            if nom_geom_dest:
 622                vals_camps[nom_geom_dest.upper()] = geom_src
 623
 624            add_feature_to_layer_gdal(layer_dest,
 625                                      tolerance_simplify=tolerance_simplify,
 626                                      geom_trans=geom_transform,
 627                                      **vals_camps)
 628
 629            if i > 0 and (i % 1000) == 0:
 630                print_debug("{} registres tractats...".format(str(i)))
 631            i += 1
 632    if ds_trans:
 633        layer_dest.CommitTransaction()
 634
 635
 636def create_layer_on_ds_gdal(ds_gdal_dest, nom_layer, nom_geom=None, gtype=None, srs_lyr_out=None, **extra_opt_list):
 637    """
 638
 639    Args:
 640        ds_gdal_dest (ogr.DataSource):
 641        nom_layer (str):
 642        nom_geom (str=None):
 643        gtype (OGRwkbGeometryType=None): Indicar integer representativo del tipo de geometria de la layer (ogr.wkbPoint, ogr.wkbPolygon, ogr.LineString, ...)
 644        srs_lyr_out (osr.SpatialReference=None): codigo epsg del sistema de coordenadas de la geometria
 645        **extra_opt_list: Calves valores option list creacion layer gdal
 646
 647    Returns:
 648        layer_gdal (ogr.Layer), nom_layer (str)
 649    """
 650    drvr_name = ds_gdal_dest.GetDriver().GetName().upper()
 651
 652    opt_list = {k.upper(): v.upper() for k, v in extra_opt_list.items()}
 653
 654    opt_geom_name = "GEOMETRY_NAME"
 655    if nom_geom:
 656        opt_list[opt_geom_name] = "{}={}".format(opt_geom_name, nom_geom.upper())
 657    else:
 658        if opt_geom_name in opt_list:
 659            opt_list.pop(opt_geom_name)
 660        gtype = ogr.wkbNone
 661
 662    opt_list["IDENTIFIER"] = opt_list.get("IDENTIFIER", "IDENTIFIER={}".format(nom_layer))
 663
 664    opt_list = set_create_option_list_for_driver_gdal(drvr_name, **{k.upper(): v.upper() for k, v in opt_list.items()})
 665
 666    if ds_gdal_dest.TestCapability(ODsCDeleteLayer) and ds_gdal_dest.GetLayerByName(nom_layer):
 667        print_warning("!ATENCION! - Se sobreescribirá la layer '{}' sobre el datasource GDAL '{}'".format(
 668            nom_layer, ds_gdal_dest.GetDescription()))
 669        ds_gdal_dest.DeleteLayer(nom_layer)
 670
 671    if drvr_name.upper() == "KML":
 672        nom_layer = nom_layer.replace("-", "__")
 673
 674    layer_out = ds_gdal_dest.CreateLayer(nom_layer, srs_lyr_out, geom_type=gtype, options=list(opt_list.values()))
 675
 676    return layer_out, layer_out.GetName() if layer_out else None
 677
 678
 679def create_spatial_index_layer_gpkg(ds_gpkg, nom_layer):
 680    """
 681    Crea spatial index sobre una layer (layer_gpkg) de un datosource gpkg (ds_gpkg)
 682
 683    Args:
 684        ds_gpkg (osgeo.ogr.DataSource):
 685        layer_gpkg (ogr.Layer):
 686
 687    Returns:
 688        bool
 689    """
 690    layer_gpkg = ds_gpkg.GetLayerByName(nom_layer)
 691    if layer_gpkg and layer_gpkg.GetGeometryColumn():
 692        # Se crea spatial_index ya que GDAL NO lo hace
 693        ds_gpkg.StartTransaction()
 694        ds_gpkg.ExecuteSQL("SELECT CreateSpatialIndex('{tab_name}', '{geom_name}') ".format(
 695            tab_name=layer_gpkg.GetName(),
 696            geom_name=layer_gpkg.GetGeometryColumn()))
 697        ds_gpkg.CommitTransaction()
 698
 699        return True
 700    else:
 701        return False
 702
 703
 704def add_feature_to_layer_gdal(layer_gdal, tolerance_simplify=None, geom_trans=None, commit=False, **valors_camps):
 705    """
 706
 707    Args:
 708        layer_gdal (ogr.Layer):
 709        tolerance_simplify (float=None): Tolerancia (distancia minima) en unidades del srs de la layer.
 710                                        Mirar método Simplify() sobre osgeo.ogr.Geometry
 711        geom_trans (osr.CoordinateTransformation=None): transformacion para convertir las geometrias a otro SRS
 712        commit (bool=False): Per defecte no farà commit de la transaccio
 713        **valors_camps: pares nombre_campo=valor de la feature a crear
 714
 715    Returns:
 716        feat (ogr.Feature)
 717    """
 718    dd_feat = ogr.Feature(layer_gdal.GetLayerDefn())
 719
 720    for camp, val in valors_camps.items():
 721        idx_geom = dd_feat.GetGeomFieldIndex(camp)
 722        es_geom = idx_geom >= 0
 723        if es_geom:
 724            if val:
 725                if tolerance_simplify:
 726                    val = val.Simplify(tolerance_simplify)
 727                if val and geom_trans:
 728                    val.Transform(geom_trans)
 729
 730            dd_feat.SetGeomField(idx_geom, val)
 731
 732        idx_fld = dd_feat.GetFieldIndex(camp)
 733        if idx_fld >= 0:
 734            if val:
 735                if es_geom or hasattr(val, "ExportToIsoWkt"):
 736                    val = val.ExportToIsoWkt()
 737                dd_feat.SetField(camp, val)
 738            else:
 739                dd_feat.SetFieldNull(camp)
 740
 741        if idx_fld < 0 and idx_geom < 0:
 742            if isinstance(val, Geometry):
 743                if geom_trans:
 744                    val.Transform(geom_trans)
 745                dd_feat.SetGeometry(val)
 746            elif val:
 747                print_warning("!ATENCION! - La :layer_gdal no contiene el campo '{}'".format(camp))
 748
 749    commit_trans = commit and layer_gdal.TestCapability(OLCTransactions)
 750    if commit_trans:
 751        layer_gdal.StartTransaction()
 752
 753    new_feat = layer_gdal.CreateFeature(dd_feat)
 754
 755    if commit_trans:
 756        layer_gdal.CommitTransaction()
 757
 758    return new_feat
 759
 760
 761def drivers_ogr_gdal_disponibles():
 762    """
 763    Retorna lista de drivers disponibles a través de la librería osgeo-gdal-ogr
 764
 765    Returns:
 766        dict
 767    """
 768    cnt = ogr.GetDriverCount()
 769    driver_list = []
 770    drivers = OrderedDict()
 771
 772    for i in range(cnt):
 773        driver = ogr.GetDriver(i)
 774        driver_name = driver.GetName()
 775        driver_list.append(driver_name)
 776
 777    for driver_name in driver_list:
 778        # Is File GeoDatabase available?
 779        drv = ogr.GetDriverByName(driver_name)
 780        if drv is None:
 781            print_warning("{} !!ATENTION - driver NOT available!!".format(driver_name))
 782        else:
 783            drivers[driver_name] = drv
 784            print_debug(driver_name)
 785
 786    return drivers
 787
 788
 789def drivers_ogr_gdal_vector_file():
 790    """
 791    Devuelve diccionario con los driver gdal para fichero vectorial
 792
 793    Returns:
 794        dict
 795    """
 796    return {nd: d for nd, d in drivers_ogr_gdal_disponibles().items()
 797            if hasattr(d, "GetMetadata_Dict") and d.GetMetadata_Dict().get('DMD_EXTENSIONS')}
 798
 799
 800def format_nom_column(nom_col):
 801    """
 802
 803    Args:
 804        nom_col:
 805
 806    Returns:
 807        str
 808    """
 809    return nom_col.replace(" ", "_")
 810
 811
 812def namedtuple_layer_gdal(layer_gdal, extract_affix_geom_fld=None):
 813    """
 814    Devuelve namedTuple con los campos del layer pasado por parametro
 815
 816    Args:
 817        layer_gdal:
 818        extract_affix_geom_fld (str=None):
 819
 820    Returns:
 821        namedtuple: con nombre "gdalFeatDef_{NOM_LAYER}" y con los campos de la layer
 822    """
 823    camps_layer = set()
 824    for fld in fields_layer_gdal(layer_gdal):
 825        camps_layer.add(format_nom_column(fld.GetNameRef()))
 826
 827    for nom_geom in geoms_layer_gdal(layer_gdal, extract_affix=extract_affix_geom_fld):
 828        camps_layer.add(format_nom_column(nom_geom))
 829
 830    nom_layer = layer_gdal.GetName().upper().split(".")[0].replace("-", "_")
 831    return namedtuple(f"gdalFeatDef_{nom_layer}", camps_layer)
 832
 833
 834def feats_layer_ds_gdal(ds_gdal, nom_layer=None, filter_sql=None):
 835    """
 836    Itera las features (registros de una layer de gdal) y los devuelve como un namdetuple
 837
 838    Args:
 839        ds_gdal: datasource gdal
 840        nom_layer (str=None): Si no viene informado cogerá la primera layer que encuentre en el datasource
 841        filter_sql (str=None): Si viene informado se aplicará como filtro sql a la layer seleccionada.
 842                            Utiliza OGR SQL (vease https://www.gdal.org/ogr_sql.html)
 843
 844    Returns:
 845        ogr.Feature, ogr.Geometry, namedtuple_layer_gdal
 846    """
 847    if not nom_layer:
 848        layer_gdal = ds_gdal.GetLayer()
 849    else:
 850        layer_gdal = ds_gdal.GetLayerByName(nom_layer)
 851
 852    if layer_gdal:
 853        for feat, geom, vals in feats_layer_gdal(layer_gdal, filter_sql=filter_sql):
 854            yield feat, geom, vals
 855
 856
 857def feats_layer_gdal(layer_gdal, nom_geom=None, filter_sql=None, extract_affix_geom_fld=PREFFIX_GEOMS_LAYERS_GDAL):
 858    """
 859    Itera las features (registros de una layer de gdal) y los devuelve como un namdtuple
 860
 861    Args:
 862        layer_gdal (ogr.Layer):
 863        nom_geom (str=None): Por defecto la geometria activa o principal
 864        filter_sql (str=None): Si viene informado se aplicará como filtro sql a la layer seleccionada.
 865                            Utiliza OGR SQL (vease https://www.gdal.org/ogr_sql.html)
 866        extract_affix_geom_fld (str=SUFFIX_GEOMS_LAYERS_GDAL): Por defecto quita el sufijo 'geom_' a los campos geom
 867
 868    Returns:
 869        ogr.Feature, ogr.Geometry, namedtuple_layer_gdal
 870    """
 871    layer_gdal.ResetReading()
 872    ntup_layer = namedtuple_layer_gdal(layer_gdal, extract_affix_geom_fld)
 873    n_geoms_layer = {nom_geom_feat: fix_affix_geom_name_layer_gdal(nom_geom_feat, layer_gdal, extract_affix_geom_fld)
 874                     for nom_geom_feat in geoms_layer_gdal(layer_gdal)}
 875
 876    if filter_sql:
 877        layer_gdal.SetAttributeFilter(filter_sql)
 878
 879    def vals_feature_gdal(feat_gdal):
 880        vals = {}
 881        for camp, val in feat_gdal.items().items():
 882            vals[format_nom_column(camp)] = val
 883
 884        for camp_geom, camp_geom_val in n_geoms_layer.items():
 885            idx_geom = feat_gdal.GetGeomFieldIndex(camp_geom)
 886            if idx_geom >= 0:
 887                vals[camp_geom_val] = feat_gdal.GetGeomFieldRef(idx_geom)
 888
 889        return vals
 890
 891    if layer_gdal:
 892        for f_tab in layer_gdal:
 893            idx_geom = f_tab.GetGeomFieldIndex(nom_geom) if nom_geom else -1
 894            yield f_tab, \
 895                f_tab.GetGeomFieldRef(idx_geom) if idx_geom >= 0 else f_tab.geometry(), \
 896                ntup_layer(**vals_feature_gdal(f_tab))
 897
 898        layer_gdal.ResetReading()
 899
 900
 901def distinct_vals_camp_layer_gdal(layer_gdal, nom_camp, filter_sql=None):
 902    """
 903    Devuelve set con distintos valores para el campo indicado del layer GDAL indicada
 904
 905    Args:
 906        layer_gdal (ogr.Layer):
 907        nom_camp (str):
 908        filter_sql (str=None):
 909
 910    Returns:
 911        set
 912    """
 913    if nom_camp.upper() not in cols_layer_gdal(layer_gdal):
 914        raise Exception("Argumento :NOM_CAMP = '{}' erróneo ya que "
 915                        "LAYER_GDAL no contiene el campo indicado".format(nom_camp))
 916
 917    return {getattr(nt_feat, nom_camp.upper())
 918            for feat, geom, nt_feat in feats_layer_gdal(layer_gdal, filter_sql=filter_sql)}
 919
 920
 921def fields_layer_gdal(layer_gdal):
 922    """
 923    Itera sobre los FieldDefn de una layer gdal
 924
 925    Args:
 926        layer_gdal:
 927
 928    Yields:
 929        osgeo.ogr.FieldDefn
 930    """
 931    layer_def = layer_gdal.GetLayerDefn()
 932    for i in range(0, layer_def.GetFieldCount()):
 933        yield layer_def.GetFieldDefn(i)
 934
 935    layer_def = None
 936
 937
 938def geom_fields_layer_gdal(layer_gdal):
 939    """
 940    Itera sobre los GeomFieldDefn de una layer gdal
 941
 942    Args:
 943        layer_gdal:
 944
 945    Yields:
 946        osgeo.ogr.GeomFieldDefn
 947    """
 948    layer_def = layer_gdal.GetLayerDefn()
 949    for i in range(0, layer_def.GetGeomFieldCount()):
 950        yield layer_def.GetGeomFieldDefn(i)
 951
 952    layer_def = None
 953
 954
 955def nom_layers_datasource_gdal(ds_gdal):
 956    """
 957
 958    Args:
 959        ds_gdal (ogr.Datasource:
 960
 961    Returns:
 962        set
 963    """
 964    return {l.GetName() for l in ds_gdal}
 965
 966
 967def cols_layer_gdal(layer_gdal):
 968    """
 969    Retorna lista con las columnas de una layer gdal
 970
 971    Args:
 972        layer_gdal:
 973
 974    Returns:
 975        set
 976    """
 977    camps = set()
 978    for fd in fields_layer_gdal(layer_gdal):
 979        # camps.add(fd.GetName().upper())
 980        camps.add(fd.GetNameRef())
 981
 982    return camps
 983
 984
 985def geoms_layer_gdal(layer_gdal, extract_affix=None):
 986    """
 987    Retorna lista con las columnas geométricas de una layer gdal
 988
 989    Args:
 990        layer_gdal:
 991        extract_affix (str=None=): if informed extract the affix passed
 992
 993    Returns:
 994        set
 995    """
 996    camps_geom = set()
 997    for gdf in geom_fields_layer_gdal(layer_gdal):
 998        # camps_geom.add(gdf.GetName().upper())
 999        name_ref = gdf.GetNameRef()
1000        if extract_affix:
1001            name_ref = fix_affix_geom_name_layer_gdal(name_ref, layer_gdal, extract_affix)
1002        camps_geom.add(name_ref)
1003
1004    return camps_geom
1005
1006
1007def fix_affix_geom_name_layer_gdal(geom_name, layer_gdal, affix=PREFFIX_GEOMS_LAYERS_GDAL):
1008    """
1009    Extract affix (default=PREFFIX_GEOMS_LAYERS_GDAL) from name geoms if correspond with other column
1010    Fix GDAL > 3.4 where geoms on GeoCSV arrive with preffix 'geom_'
1011
1012    Args:
1013          geom_name (str):
1014          layer_gdal:
1015          affix (str=PREFFIX_GEOMS_LAYERS_GDAL):
1016
1017    Returns:
1018        str
1019    """
1020    cols_layer = cols_layer_gdal(layer_gdal)
1021    geom_name_layer = geom_name
1022    if geom_name.lower().startswith(affix):
1023        geom_name_aux = re.sub(affix, '', geom_name, flags=re.IGNORECASE)
1024        if geom_name_aux.lower() in (col.lower() for col in cols_layer):
1025            geom_name_layer = geom_name_aux
1026
1027    return geom_name_layer
1028
1029
1030def add_layer_gdal_to_ds_gdal(ds_gdal, layer_gdal, nom_layer=None, lite=False, srs_epsg_code=None, multi_geom=False,
1031                              nom_geom=None, null_geoms=False, **extra_opt_list):
1032    """
1033    Añade una layer_gdal a un datasource_gdal. Si es una layer con multigeometrias las separa en una layer por geometria
1034
1035    Args:
1036        ds_gdal (osgeo.ogr.Datasource):
1037        layer_gdal (osgeo.ogr.Layer):
1038        nom_layer (str=None):
1039        lite (bool=False):
1040        srs_epsg_code (int=None): codigo EPSG para el sistema de coordenadas con el que se quieren convertir
1041                                  las geometrias
1042        nom_geom (str=None): nombre de geometria de layer origen (layer_gdal) que se copiará, si no todas
1043        multi_geom (bool=False): Si el DS_GDAL destino permite multigeometria
1044        null_geoms (bool=False): Indica si se admitirán registros con NULL geoms. Por defecto NO
1045        **extra_opt_list (str): Lista claves-valores de opciones para createLayer del driver de ds_gdal indicado
1046
1047    Returns:
1048        new_layers_ds_gdal (list)
1049    """
1050    new_layers_ds_gdal = []
1051
1052    if not nom_layer:
1053        nom_layer = layer_gdal.GetName()
1054
1055    geoms_layer = geoms_layer_gdal(layer_gdal)
1056    if nom_geom:
1057        if nom_geom.upper() not in (g.upper() for g in geoms_layer):
1058            Exception("!ERROR! - Nombre de geometria '{}' no existe en la layer GDAL origen")
1059        else:
1060            geoms_layer = (nom_geom,)
1061
1062    if geoms_layer:
1063        tol = None
1064        if lite:
1065            tol = SIMPLIFY_TOLERANCE
1066
1067        nom_layer_base = nom_layer.split("-")[0]
1068        if not multi_geom:
1069            for geom_name in geoms_layer:
1070                # Fix GDAL > 3.4 where geoms on GeoCSV arrive with preffix 'geom_'
1071                geom_name_layer = fix_affix_geom_name_layer_gdal(geom_name, layer_gdal)
1072
1073                nom_layer = "{}-{}".format(nom_layer_base, geom_name_layer).lower()
1074                extra_opt_list["GEOMETRY_NAME"] = "GEOMETRY_NAME={}".format(geom_name_layer)
1075                lyr = create_layer_from_layer_gdal_on_ds_gdal(ds_gdal, layer_gdal, nom_layer, geom_name,
1076                                                              tolerance_simplify=tol, null_geoms=null_geoms,
1077                                                              epsg_code_dest=srs_epsg_code, **extra_opt_list)
1078                new_layers_ds_gdal.append(lyr)
1079        else:
1080            lyr = create_layer_from_layer_gdal_on_ds_gdal(ds_gdal, layer_gdal, nom_layer, exclude_cols_geoms=False,
1081                                                          tolerance_simplify=tol, null_geoms=True,
1082                                                          epsg_code_dest=srs_epsg_code, **extra_opt_list)
1083            new_layers_ds_gdal.append(lyr)
1084    else:
1085        if ds_gdal.GetDriver().GetName() == 'PostgreSQL':
1086            extra_opt_list.update(dict(
1087                precision=extra_opt_list.get('precision', 'PRECISION=NO')
1088            ))
1089
1090        lyr = copy_layer_gdal_to_ds_gdal(layer_gdal, ds_gdal, nom_layer.lower(), **extra_opt_list)
1091        new_layers_ds_gdal.append(lyr)
1092
1093    return new_layers_ds_gdal
1094
1095
1096def copy_layers_gpkg(ds_gpkg, driver, dir_base, lite=False, srs_epsg_code=None, zipped=True):
1097    """
1098
1099    Args:
1100        ds_gpkg (osgeo.ogr.Datasource):
1101        driver (str):
1102        dir_base (str=None):
1103        lite (bool=False):
1104        srs_epsg_code (int=None): codigo EPSG para el sistema de coordenadas con el que se quieren convertir
1105                                  las geometrias
1106        zipped (bool=False):
1107
1108    Returns:
1109        num_layers (int)
1110    """
1111    num_layers = 0
1112    subdir_drvr = os.path.normpath(os.path.join(dir_base, driver.upper()))
1113    utils.create_dir(subdir_drvr)
1114
1115    for layer_gpkg in (ds_gpkg.GetLayer(id_lyr) for id_lyr in range(ds_gpkg.GetLayerCount() - 1)):
1116        if driver == "GPKG":
1117            nom_ds, ext = utils.split_ext_file(os.path.basename(ds_gpkg.name))
1118        else:
1119            nom_ds = f"{layer_gpkg.GetName()}".lower()
1120
1121        ds_gdal, existia = datasource_gdal_vector_file(driver, nom_ds, subdir_drvr)
1122
1123        add_layer_gdal_to_ds_gdal(ds_gdal, layer_gpkg, lite=lite, srs_epsg_code=srs_epsg_code)
1124
1125        num_layers += 1
1126
1127    return num_layers
1128
1129
1130def set_csvt_for_layer_csv(path_csv, **tipus_camps):
1131    """
1132    Crea/Modifica el CSVT asociado con los tipos indicados para cada columna
1133
1134    Args:
1135        path_csv (str):
1136        **tipus_camps: clave=valor con el nombre del campo y el tipo de campo asociado (p.e. String(25), WKT, Integer,...)
1137
1138    Returns:
1139        path_csvt (str)
1140    """
1141    lyr_csv, nom_layer, ds_lyr = layer_gdal_from_file(path_csv, "CSV")
1142    if not lyr_csv:
1143        print_warning("!ATENCIO! - No s'ha pogut obrir la layer CSV '{}'".format(path_csv))
1144        return
1145
1146    path_lyr_csvt = os.path.join(os.path.dirname(path_csv), "{}.csvt".format(nom_layer))
1147    tips_lyr = {}
1148    for fld in fields_layer_gdal(lyr_csv):
1149        tip_fld = fld.GetFieldTypeName(fld.GetType())
1150        sufix = ""
1151        w = fld.GetWidth()
1152        if w:
1153            sufix = "{}".format(w)
1154        p = fld.GetPrecision()
1155        if p:
1156            sufix += ".{}".format(p)
1157        if sufix:
1158            tip_fld += "({})".format(sufix)
1159
1160        tips_lyr[fld.name.upper()] = tip_fld
1161
1162    for nom_camp, tip_camp in tipus_camps.items():
1163        nom_camp = nom_camp.upper()
1164        if nom_camp not in tips_lyr:
1165            print_warning("!ATENCIO! - Camp '{}' no existeix sobre la layer CSV '{}'".format(nom_camp, path_csv))
1166            continue
1167
1168        tips_lyr[nom_camp] = tip_camp
1169
1170    with open(path_lyr_csvt, mode="w", encoding="utf8") as f_csvt:
1171        f_csvt.write(",".join(tips_lyr.values()))
1172
1173
1174def zip_and_clean_ds_csv(path_csv):
1175    """
1176    # Zipea datasource osgeo CSV y como GDAL no crea los tipos WKT para las geometrias en el CSVT se fuerza
1177
1178    Args:
1179        path_csv (str): path Datasource osgeo CSV
1180
1181    Returns:
1182        zip_path (str)
1183    """
1184    ds_gdal_csv, dummy = datasource_gdal_vector_file("CSV", os.path.basename(path_csv).split(".")[0],
1185                                                     os.path.dirname(path_csv))
1186    layer_csv = ds_gdal_csv.GetLayer(0)
1187    tips_geoms = {gn: "WKT" for gn in
1188                  (*geoms_layer_gdal(layer_csv, extract_affix=PREFFIX_GEOMS_LAYERS_GDAL),
1189                   *geoms_layer_gdal(layer_csv, extract_affix=PREFFIX_GEOMS_LAYERS_GDAL_CSV))}
1190    path_csv = ds_gdal_csv.GetDescription()
1191    ds_gdal_csv = None  # Cerramos el datasource para que se guarden los cambios
1192    if path_csv and os.path.exists(path_csv):
1193        if tips_geoms:
1194            set_csvt_for_layer_csv(path_csv, **tips_geoms)
1195        rename_wkt_geoms_csv(path_csv)
1196    dir_base_csv = os.path.dirname(path_csv)
1197    nom_layer = os.path.basename(path_csv).split(".")[0]
1198    l_files_csv = [os.path.join(dir_base_csv, nf) for nf in os.listdir(dir_base_csv)
1199                   if nf.lower().startswith(nom_layer.lower()) and
1200                   any(nf.lower().endswith(ext) for ext in ('csv', 'csvt'))]
1201    zip_path = utils.zip_files(os.path.join(dir_base_csv, "{}.zip".format(nom_layer)), l_files_csv)
1202    if zip_path:
1203        for fl_csv in l_files_csv:
1204            os.remove(fl_csv)
1205
1206    return zip_path
1207
1208
1209def convert_angle(pt_xy, deg_ang, orig_srs, dest_srs):
1210    """
1211
1212    Args:
1213        pt_xy (tuple):
1214        deg_ang (float):
1215        orig_srs (osr.SpatialReference):
1216        dest_srs (osr.SpatialReference):
1217
1218    Returns:
1219
1220    """
1221    trans = osr.CoordinateTransformation(orig_srs, dest_srs)
1222    x, y = pt_xy
1223    dx = math.sin(math.radians(deg_ang)) * 0.00000001
1224    dy = math.cos(math.radians(deg_ang)) * 0.00000001
1225    pt1 = ogr.CreateGeometryFromWkt("POINT ({} {})".format(x, y))
1226    pt2 = ogr.CreateGeometryFromWkt("POINT ({} {})".format(x + dx, y + dy))
1227    pt1.Transform(trans)
1228    pt2.Transform(trans)
1229    x1, y1, z1 = pt1.GetPoint()
1230    x2, y2, z2 = pt2.GetPoint()
1231
1232    return math.degrees(math.atan2(y2 - y1, x2 - x1))
1233
1234
1235def transform_ogr_geom(a_ogr_geom, from_espg_code, to_epsg_code):
1236    """
1237    Transforma una geometria OGR según los EPSG indicados
1238
1239    Args:
1240        a_ogr_geom (ogr.geometry): una geometria del tipo OGR
1241        from_espg_code (int): codigo numérico del EPSG actual para la geometria
1242        to_epsg_code (int): codigo numérico del EPSG al que se quiere transformar
1243
1244    Returns:
1245        ogr.geometry
1246    """
1247    source = osr.SpatialReference()
1248    source.ImportFromEPSG(from_espg_code)
1249
1250    target = osr.SpatialReference()
1251    target.ImportFromEPSG(to_epsg_code)
1252
1253    a_transform = osr.CoordinateTransformation(source, target)
1254    a_ogr_geom.Transform(a_transform)
1255
1256    return a_ogr_geom
1257
1258
1259def ds_postgis(dbname='POSTGRES', host='localhost', port='5432', user='postgres', password='postgres', schema='public'):
1260    """
1261    Retorna datasource GDAL para ddbb postgis
1262
1263    Args:
1264        dbname:
1265        host:
1266        port:
1267        user:
1268        password:
1269        schema:
1270
1271    Returns:
1272        osgeo.ogr.DataSource
1273    """
1274    pg_conn = f"PG:dbname='{dbname}' host='{host}' port='{port}' user='{user}' password='{password}' active_schema='{schema}'"
1275    drvr, exts = driver_gdal('PostgreSQL')
1276    return drvr.Open(pg_conn, 1)
1277
1278
1279def reset_sequence_layer_postgis(ds_postgis, nom_layer):
1280    """
1281    Resetea la secuencia de la layer PostGIS indicada
1282
1283    Args:
1284        ds_postgis (osgeo.ogr.DataSource): datasource de la ddbb PostGIS:
1285        nom_layer (str): nombre de la layer PostGIS
1286
1287    Returns:
1288        bool: True si se ha reseteado correctamente
1289    """
1290    ok = False
1291    layer_dest = ds_postgis.GetLayerByName(nom_layer)
1292    fid_column = layer_dest.GetFIDColumn()
1293    get_seq_sql = f"SELECT pg_get_serial_sequence('{nom_layer}', '{fid_column}') FROM information_schema.columns " \
1294                  f"WHERE table_name = '{nom_layer}' AND column_default LIKE 'nextval%'"
1295
1296    if res_get_seq := ds_postgis.ExecuteSQL(get_seq_sql):
1297        seq_name = res_get_seq.GetNextFeature().GetField(0)
1298        ds_postgis.StartTransaction()
1299        reset_seq = f"SELECT setval('{seq_name}', 1, false);"
1300        ds_postgis.ExecuteSQL(reset_seq)
1301        ds_postgis.CommitTransaction()
1302        ok = True
1303
1304    return ok
1305
1306
1307if __name__ == '__main__':
1308    import fire
1309
1310    fire.Fire()
SIMPLIFY_TOLERANCE = 1e-06
DRIVERS_GDAL_NOT_DELETE_LAYERS = ('POSTGRESQL',)
DRIVERS_OLD_SRS_AXIS_MAPPING_4326 = ('GEOJSON', 'GPKG', 'POSTGRESQL')
PREFFIX_GEOMS_LAYERS_GDAL = 'geom_'
PREFFIX_GEOMS_LAYERS_GDAL_CSV = '_WKT'
def srs_ref_from_epsg_code(code_epsg: int, old_axis_mapping=False) -> osgeo.osr.SpatialReference:
34def srs_ref_from_epsg_code(code_epsg: int, old_axis_mapping=False) -> osr.SpatialReference:
35    """
36
37    Args:
38        code_epsg (int):
39        old_axis_mapping (bool=False): Por cambio entre GDAL2.0 y GDAL3.0 se (vease
40    https://github.com/OSGeo/gdal/blob/master/gdal/MIGRATION_GUIDE.TXT) por defecto se utiliza el mapeo de ejes
41    clasico LONG,LAT
42
43    Returns:
44        srs (osr.SpatialReference)
45    """
46    srs = osr.SpatialReference()
47    if old_axis_mapping:
48        try:
49            from osgeo.osr import OAMS_TRADITIONAL_GIS_ORDER
50            print_debug("OAMS_TRADITIONAL_GIS_ORDER")
51            srs.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
52        except ImportError:
53            print_warning("OAMS_TRADITIONAL_GIS_ORDER not available")
54
55    ret = srs.ImportFromEPSG(code_epsg)
56    if ret != 0:
57        raise Warning("No se puede retornar un osgeo.osr.SpatialReference EPSG para el codigo '{}'!!".format(code_epsg))
58
59    return srs
Arguments:
  • code_epsg (int):
  • old_axis_mapping (bool=False): Por cambio entre GDAL2.0 y GDAL3.0 se (vease

https://github.com/OSGeo/gdal/blob/master/gdal/MIGRATION_GUIDE.TXT) por defecto se utiliza el mapeo de ejes clasico LONG,LAT

Returns:

srs (osr.SpatialReference)

def layer_gdal_from_file( path_file: str, nom_driver: str = 'GeoJSON', nom_geom: str = None, default_srs_epsg_code: int = 4326, default_order_long_lat: bool = True):
 62def layer_gdal_from_file(path_file: str,
 63                         nom_driver: str = 'GeoJSON',
 64                         nom_geom: str = None,
 65                         default_srs_epsg_code: int = 4326,
 66                         default_order_long_lat: bool = True):
 67    """
 68
 69    Args:
 70        path_file (str):
 71        nom_driver (str='GeoJSON'):
 72        nom_geom (str=None): si se informa devolverá la layer solo con la geometria especificada
 73        default_srs_epsg_code (int=4326): codigo del sistema de coordenadas que asignará por defecto si la layer
 74                                          NO tiene sistema definido
 75        default_order_long_lat (bool=True): si no viene informado el SRS y se usa el default EPSG4326 entonces se supone
 76                                          por defecto que las geometria vienen en orden antiguo LONGITUD, LATITUD
 77
 78    Returns:
 79        layer_gdal (osgeo.ogr.Layer), nom_layer (str), datasource_gdal (osgeo.ogr.DataSource)
 80    """
 81    drvr = ogr.GetDriverByName(nom_driver)
 82
 83    nom_layer, ext = utils.split_ext_file(os.path.basename(path_file))
 84    if ext.lower().endswith("zip"):
 85        path_file = "/vsizip/{}".format(path_file)
 86
 87    ds_vector_file = drvr.Open(path_file, 0)
 88
 89    a_layer = None
 90    if ds_vector_file:
 91        a_layer = ds_vector_file.GetLayer(0)
 92
 93        if nom_geom:
 94            lay_def = a_layer.GetLayerDefn()
 95            nom_geom = nom_geom.strip().upper()
 96
 97            if lay_def.GetGeomFieldCount() > 1:
 98                idx_geom = lay_def.GetGeomFieldIndex(nom_geom)
 99                if idx_geom < 0:
100                    raise Exception("El fichero vectorial '{}' no contiene una geometria con el nombre '{}'".format(
101                        ds_vector_file.GetName(), nom_geom))
102                elif idx_geom > 0:
103                    # Convertimos a layer en MEMORY para poder cambiar estructura
104                    ds_mem = ds_gdal_memory()
105                    a_layer = create_layer_from_layer_gdal_on_ds_gdal(ds_mem, a_layer, nom_layer, nom_geom,
106                                                                      exclude_cols_geoms=False, null_geoms=True)
107                    if a_layer:
108                        ds_vector_file = ds_mem
109
110            elif not a_layer.GetGeometryColumn() and lay_def.GetGeomFieldDefn(0):
111                lay_def.GetGeomFieldDefn(0).SetName(nom_geom)
112
113        if a_layer:
114            # Por si no carga el SRS se asigna el :default_srs_epsg_code (defecto epsg:4326)
115            for gf in geom_fields_layer_gdal(a_layer):
116                if not gf.GetSpatialRef():
117                    gf.SetSpatialRef(srs_ref_from_epsg_code(default_srs_epsg_code, default_order_long_lat))
118
119    return a_layer, nom_layer, ds_vector_file
Arguments:
  • path_file (str):
  • nom_driver (str='GeoJSON'):
  • nom_geom (str=None): si se informa devolverá la layer solo con la geometria especificada
  • default_srs_epsg_code (int=4326): codigo del sistema de coordenadas que asignará por defecto si la layer NO tiene sistema definido
  • default_order_long_lat (bool=True): si no viene informado el SRS y se usa el default EPSG4326 entonces se supone por defecto que las geometria vienen en orden antiguo LONGITUD, LATITUD
Returns:

layer_gdal (osgeo.ogr.Layer), nom_layer (str), datasource_gdal (osgeo.ogr.DataSource)

def datasource_gdal_vector_file( nom_driver_gdal, nom_ds, a_dir, create=None, from_zip=False, **create_options):
122def datasource_gdal_vector_file(nom_driver_gdal, nom_ds, a_dir, create=None, from_zip=False, **create_options):
123    """
124    Crea datasource gdal para driver de tipo fichero
125    Args:
126        nom_driver_gdal (str):
127        nom_ds (str):
128        a_dir (str):
129        create (bool=None): Por defecto crea el datasource si este no existe y se abre sin problemas previamente.
130                        Si False entonces NO lo crea en ningún caso, y si True lo creará sin intentar abrirlo
131        from_zip (bool=False):
132        **create_options: lista claves valores con opciones de creacion para el datasource de GDAL
133                        p.e.: NameField="SEQENTITAT", DescriptionField="SW_URN"
134
135    Returns:
136        datasource_gdal (osgeo.ogr.DataSource), overwrited (bool)
137    """
138    driver, exts_driver = driver_gdal(nom_driver_gdal)
139    if not exts_driver:
140        raise Exception("ERROR! - El driver GDAL {} no es de tipo fichero vectorial".format(driver))
141
142    base_path_file = os.path.normpath(os.path.join(a_dir, nom_ds.strip().lower()))
143    _, ext_base = os.path.splitext(base_path_file)
144
145    vsi_prefix = ""
146    if from_zip:
147        ext_file = "zip"
148        vsi_prefix = "/vsizip/"
149    else:
150        if "topojson" in (ext.lower() for ext in exts_driver):
151            ext_file = "topojson"
152        else:
153            ext_file = exts_driver[0]
154
155    if (os.path.exists(base_path_file) and os.path.isfile(
156            base_path_file)) or ext_base.lower() == f'.{ext_file.lower()}':
157        path_file = base_path_file
158    else:
159        path_file = "{}.{}".format(base_path_file, ext_file)
160
161    overwrited = False
162    datasource_gdal = None
163    if not create and os.path.exists(path_file):
164        open_path_file = "{}{}".format(vsi_prefix, path_file)
165        datasource_gdal = driver.Open(open_path_file, 1)
166        if datasource_gdal is None:
167            datasource_gdal = driver.Open(open_path_file)
168
169    if datasource_gdal:
170        overwrited = True
171
172    if create or (create is not False and datasource_gdal is None and driver.TestCapability(ODrCCreateDataSource)):
173        datasource_gdal = driver.CreateDataSource(path_file,
174                                                  list("{}={}".format(opt, val) for opt, val in create_options.items()))
175
176    return datasource_gdal, overwrited

Crea datasource gdal para driver de tipo fichero

Arguments:
  • nom_driver_gdal (str):
  • nom_ds (str):
  • a_dir (str):
  • create (bool=None): Por defecto crea el datasource si este no existe y se abre sin problemas previamente. Si False entonces NO lo crea en ningún caso, y si True lo creará sin intentar abrirlo
  • from_zip (bool=False):
  • **create_options: lista claves valores con opciones de creacion para el datasource de GDAL p.e.: NameField="SEQENTITAT", DescriptionField="SW_URN"
Returns:

datasource_gdal (osgeo.ogr.DataSource), overwrited (bool)

def driver_gdal(nom_driver):
179def driver_gdal(nom_driver):
180    """
181    Devuelve el driver GDAL solicitado y si es de tipo fichero vectorial tambien devuelve la extension
182    Si no coincide exactamente devuelve el que tiene nombre más parecido.
183    Verificar con drivers_ogr_gdal_disponibles() los disponibles
184
185    Args:
186        nom_driver:
187    Returns:
188        driver_gdal (osgeo.ogr.Driver), exts_driver (list)
189    """
190    driver_gdal = ogr.GetDriverByName(nom_driver)
191    exts_drvr = driver_gdal.GetMetadata().get('DMD_EXTENSIONS', "").split(" ")
192
193    return driver_gdal, exts_drvr

Devuelve el driver GDAL solicitado y si es de tipo fichero vectorial tambien devuelve la extension Si no coincide exactamente devuelve el que tiene nombre más parecido. Verificar con drivers_ogr_gdal_disponibles() los disponibles

Arguments:
  • nom_driver:
Returns:

driver_gdal (osgeo.ogr.Driver), exts_driver (list)

def ds_gdal_memory():
196def ds_gdal_memory():
197    """
198    Devuelve un osgeo.ogr.DataSource de tipo "memory"
199
200    Returns:
201        datasource_gdal (osgeo.ogr.DataSource)
202    """
203    return ogr.GetDriverByName("memory").CreateDataSource("")

Devuelve un osgeo.ogr.DataSource de tipo "memory"

Returns:

datasource_gdal (osgeo.ogr.DataSource)

def set_create_option_list_for_layer_gdal(layer_gdal, drvr_name='GPKG', **extra_opt_list):
206def set_create_option_list_for_layer_gdal(layer_gdal, drvr_name="GPKG", **extra_opt_list):
207    """
208    Devuelve la lista de create options GDAL a partir de una layer, el driver para el que se quiere crear y options
209    pasadas por defecto
210
211    Args:
212        layer_gdal (ogr.Layer):
213        drvr_name (str="GPKG"): nombre de driver GDAL
214        extra_opt_list: lista pares claves-valores que se corresponden con option create list del driver GDAL
215
216    Returns:
217        opt_list (dict)
218    """
219    opt_list = set_create_option_list_for_driver_gdal(drvr_name, **extra_opt_list)
220
221    opt_geom_name = "GEOMETRY_NAME"
222    if opt_geom_name not in opt_list:
223        nom_geom_src = layer_gdal.GetGeometryColumn()
224        if nom_geom_src:
225            opt_list[opt_geom_name] = "{}={}".format(opt_geom_name, nom_geom_src)
226
227    return opt_list

Devuelve la lista de create options GDAL a partir de una layer, el driver para el que se quiere crear y options pasadas por defecto

Arguments:
  • layer_gdal (ogr.Layer):
  • drvr_name (str="GPKG"): nombre de driver GDAL
  • extra_opt_list: lista pares claves-valores que se corresponden con option create list del driver GDAL
Returns:

opt_list (dict)

def set_create_option_list_for_driver_gdal(drvr_name='GPKG', **extra_opt_list):
230def set_create_option_list_for_driver_gdal(drvr_name="GPKG", **extra_opt_list):
231    """
232    Devuelve la lista de create options GDAL a partir de una layer, el driver para el que se quiere crear y options
233    pasadas por defecto
234
235    Args:
236        drvr_name (str="GPKG"): nombre de driver GDAL
237        extra_opt_list: lista pares claves-valores que se corresponden con option create list del driver GDAL
238
239    Returns:
240        opt_list (dict)
241    """
242    # Se quitan las options que no son de creacion para el driver especificado
243    drvr, exts_drvr = driver_gdal(drvr_name)
244    if not drvr:
245        Exception("!ERROR! - El nombre de driver '{}' no es un driver GDAL disponible".format(drvr_name))
246
247    opt_list = {k.upper(): v.upper() for k, v in extra_opt_list.items()}
248
249    if not "FID" in opt_list and drvr.name == 'GPKG':
250        opt_list["FID"] = 'FID=FID_GPKG'
251
252    if drvr.name == "GeoJSON":
253        if "WRITE_BBOX" not in opt_list:
254            opt_list["WRITE_BBOX"] = 'WRITE_BBOX=YES'
255
256    if drvr.name == "CSV":
257        if "CREATE_CSVT" not in opt_list:
258            opt_list["CREATE_CSVT"] = 'CREATE_CSVT=YES'
259        if "GEOMETRY" not in opt_list:
260            opt_list["GEOMETRY"] = 'GEOMETRY=AS_WKT'
261
262    if drvr.GetMetadataItem('DS_LAYER_CREATIONOPTIONLIST'):
263        list_opts_drvr = drvr.GetMetadataItem('DS_LAYER_CREATIONOPTIONLIST')
264        keys_opt_list = list(opt_list.keys())
265        for n_opt in keys_opt_list:
266            if list_opts_drvr.find(n_opt) < 0:
267                opt_list.pop(n_opt)
268
269        if "SPATIAL_INDEX" not in opt_list and \
270                list_opts_drvr.find("SPATIAL_INDEX") >= 0 and drvr.name != 'PostgreSQL':
271            opt_list["SPATIAL_INDEX"] = 'SPATIAL_INDEX=YES'
272
273    return opt_list

Devuelve la lista de create options GDAL a partir de una layer, el driver para el que se quiere crear y options pasadas por defecto

Arguments:
  • drvr_name (str="GPKG"): nombre de driver GDAL
  • extra_opt_list: lista pares claves-valores que se corresponden con option create list del driver GDAL
Returns:

opt_list (dict)

def copy_layer_gdal_to_ds_gdal( layer_src, ds_gdal_dest, nom_layer=None, nom_geom=None, overwrite=True, **extra_opt_list):
276def copy_layer_gdal_to_ds_gdal(layer_src, ds_gdal_dest, nom_layer=None, nom_geom=None,
277                               overwrite=True, **extra_opt_list):
278    """
279    Copia una layer_gdal a otro datasource gdal
280
281    Args:
282        layer_src:
283        ds_gdal_dest:
284        nom_layer (str=None): OPC - Si no viene informado cogerá el nombre de la layer
285        nom_geom (str=None): OPC - Si se informa se cogerá como el nombre de la geometria de la nueva layer
286        overwrite (bool=True):
287        **extra_opt_list (str): Lista claves-valores de opciones para copylayer o createLayer
288                                del driver de ds_gdal indicado
289
290    Returns:
291        layer_dest (ogr.layer)
292    """
293    if not nom_layer:
294        nom_layer = layer_src.GetName()
295    nom_layer = nom_layer.strip().lower()
296
297    layer_dest = ds_gdal_dest.GetLayerByName(nom_layer)
298    if layer_dest:
299        if not overwrite:
300            return layer_dest
301        else:
302            ds_gdal_dest.DeleteLayer(nom_layer)
303
304    drvr_name = ds_gdal_dest.GetDriver().GetName()
305
306    extra_opt_list["IDENTIFIER"] = "IDENTIFIER={}".format(nom_layer)
307
308    opt_list = set_create_option_list_for_layer_gdal(layer_src, drvr_name=drvr_name,
309                                                     **{k.upper(): v.upper() for k, v in extra_opt_list.items()})
310
311    layer_dest = ds_gdal_dest.CopyLayer(layer_src, nom_layer, list(opt_list.values()))
312
313    if layer_dest:
314        if not layer_dest.GetGeometryColumn() and layer_dest.GetLayerDefn().GetGeomFieldDefn(0) and \
315                (layer_src.GetGeometryColumn() or nom_geom):
316            if not nom_geom:
317                nom_geom = layer_src.GetGeometryColumn()
318            else:
319                nom_geom = nom_geom.upper()
320
321            layer_dest.GetLayerDefn().GetGeomFieldDefn(0).SetName(nom_geom)
322
323        if drvr_name == "GPKG":
324            create_spatial_index_layer_gpkg(ds_gdal_dest, nom_layer)
325
326    return layer_dest

Copia una layer_gdal a otro datasource gdal

Arguments:
  • layer_src:
  • ds_gdal_dest:
  • nom_layer (str=None): OPC - Si no viene informado cogerá el nombre de la layer
  • nom_geom (str=None): OPC - Si se informa se cogerá como el nombre de la geometria de la nueva layer
  • overwrite (bool=True):
  • **extra_opt_list (str): Lista claves-valores de opciones para copylayer o createLayer del driver de ds_gdal indicado
Returns:

layer_dest (ogr.layer)

def layer_gtype_from_geoms(layer_gdal, nom_geom=None):
329def layer_gtype_from_geoms(layer_gdal, nom_geom=None):
330    """
331    A partir de la 1º geometria informada de una layer_gdal devuelve el tipo de geometria que es. Si no encuentra
332    devuelve el geom_type de la layer (layer_gdal.GetGeomType())
333
334    Args:
335        layer_gdal (osgeo.ogr.Layer):
336        nom_geom (str=None): Nombre geometria
337
338    Returns:
339        geom_type (int=layer_gdal.GetGeomType()): Si no encuentra devuelve 0 por defecto (GEOMETRY)
340    """
341    idx_geom = 0
342    if nom_geom:
343        idx_geom = layer_gdal.GetLayerDefn().GetGeomFieldIndex(nom_geom)
344
345    if idx_geom >= 0:
346        layer_gdal.ResetReading()
347        return next((f.GetGeomFieldRef(idx_geom).GetGeometryType()
348                     for f in layer_gdal if f.GetGeomFieldRef(idx_geom)),
349                    layer_gdal.GetGeomType())

A partir de la 1º geometria informada de una layer_gdal devuelve el tipo de geometria que es. Si no encuentra devuelve el geom_type de la layer (layer_gdal.GetGeomType())

Arguments:
  • layer_gdal (osgeo.ogr.Layer):
  • nom_geom (str=None): Nombre geometria
Returns:

geom_type (int=layer_gdal.GetGeomType()): Si no encuentra devuelve 0 por defecto (GEOMETRY)

def srs_for_layer(layer_src, nom_geom_src=None):
352def srs_for_layer(layer_src, nom_geom_src=None):
353    """
354    Devuelve el SpatialReference de la layer_gdal
355    Args:
356        layer_src (ogr.Layer)
357        nom_geom_src (str=None): Nombre geometria
358
359    Returns:
360        srs_lyr_src (osgeo.osr.SpatialReference)
361    """
362    srs_lyr_src = layer_src.GetSpatialRef()
363    layer_src_def = layer_src.GetLayerDefn()
364
365    if act_geom_field := layer_src_def.GetGeomFieldDefn(0):
366        if nom_geom_src:
367            idx_act_geom_field = layer_src_def.GetGeomFieldIndex(nom_geom_src)
368            if idx_act_geom_field >= 0:
369                act_geom_field = layer_src_def.GetGeomFieldDefn(idx_act_geom_field)
370
371        if act_geom_field.GetSpatialRef():
372            srs_lyr_src = act_geom_field.GetSpatialRef()
373
374    return srs_lyr_src

Devuelve el SpatialReference de la layer_gdal

Arguments:
  • layer_src (ogr.Layer)
  • nom_geom_src (str=None): Nombre geometria
Returns:

srs_lyr_src (osgeo.osr.SpatialReference)

def create_layer_from_layer_gdal_on_ds_gdal( ds_gdal_dest, layer_src, nom_layer=None, nom_geom_src=None, sel_camps=None, exclude_cols_geoms=True, tolerance_simplify=None, null_geoms=False, gtype_layer_from_geoms=True, epsg_code_dest=None, epsg_code_src_default=4326, old_axis_mapping_srs=None, **extra_opt_list):
377def create_layer_from_layer_gdal_on_ds_gdal(ds_gdal_dest, layer_src, nom_layer=None, nom_geom_src=None, sel_camps=None,
378                                            exclude_cols_geoms=True, tolerance_simplify=None, null_geoms=False,
379                                            gtype_layer_from_geoms=True, epsg_code_dest=None,
380                                            epsg_code_src_default=4326, old_axis_mapping_srs=None, **extra_opt_list):
381    """
382    Crea nuevo layer a partir de layer_gdal.
383
384    Args:
385        ds_gdal_dest (ogr.DataSource):
386        layer_src (ogr.Layer):
387        nom_layer (str=None):
388        nom_geom_src (str=None): Si el nombre no se corresponde con ninguna de las geometrias de la layer_src se utilizará
389                            como ALIAS de la geometria 0 (la defecto) de la nueva layer resultante
390        sel_camps (list=None): OPC - Lista de campos a escoger de la layer original
391        exclude_cols_geoms (bool=True): Por defecto de la lista de columnas alfanuméricas (no geometrias) excluirá las
392                                       columnas que hagan referencia a alguna de las geometrias
393        tolerance_simplify (float=None): Tolerancia (distancia minima) en unidades del srs de la layer
394             Mirar método Simplify() sobre osgeo.ogr.Geometry
395        null_geoms (bool=False): Por defecto no grabará las filas que la geometria principal (nom_geom) es nula
396        gtype_layer_from_geoms (bool=True): Por defecto, si gtype de layer origen == 0 (GEOMETRY) deducirá el tipo de
397                                geometria (POINT, LINE o POLYGON) a partir de la primera informada
398                                encontrada en la layer_origen
399        epsg_code_dest (int=None): Codigo EPSG para le que se transformarán las geometrias desde el SRS original
400        epsg_code_src_default (int=4326): Codigo EPSG que se usará para las layer_src que NO tengan SRS asignado
401        old_axis_mapping_srs (bool=None): setea si quieres usar old mapping axes en (LONG, LAT) (GDAL >3.2)  
402        **extra_opt_list (str): Lista claves-valores de opciones para createLayer del driver de ds_gdal indicado
403
404    Returns:
405        layer_out (ogr.Layer)
406    """
407    desc_ds_gdal = ds_gdal_dest.GetDescription()
408    drvr_name = ds_gdal_dest.GetDriver().GetName().upper()
409    print_debug("Inici crear layer '{}' en ds_gdal '{}'".format(
410        (nom_layer if nom_layer else layer_src.GetName()).upper(),
411        desc_ds_gdal if desc_ds_gdal else drvr_name))
412
413    geoms_src = geoms_layer_gdal(layer_src)
414    camps_src = cols_layer_gdal(layer_src)
415
416    if nom_geom_src:
417        nom_geom_src = nom_geom_src.upper()
418        if len(geoms_src) > 1:
419            if nom_geom_src not in map(lambda ng: ng.upper(), geoms_src):
420                raise Exception("Argumento :NOM_GEOM = '{}' erróneo ya que "
421                                "LAYER_GDAL original no contiene dicha geometria".format(nom_geom_src))
422        elif len(geoms_src) == 0:
423            raise Exception("Argumento :NOM_GEOM = '{}' erróneo ya que "
424                            "LAYER_GDAL original no contiene geometrias".format(nom_geom_src))
425
426    if sel_camps:
427        sel_camps = {ng.upper() for ng in sel_camps}
428        if not sel_camps.issubset(camps_src):
429            raise Exception("Argumento :SEL_CAMPS = '[{}]' erróneo ya que "
430                            "LAYER_GDAL no contiene alguno de los campos indicados".format(",".join(sel_camps)))
431    else:
432        sel_camps = set()
433
434    if not nom_layer:
435        nom_layer = layer_src.GetName()
436    else:
437        nom_layer = nom_layer.strip()
438
439    nom_layer = nom_layer.lower()
440
441    gtype = layer_src.GetGeomType()
442    srs_lyr_src = layer_src.GetSpatialRef()
443
444    layer_src_def = layer_src.GetLayerDefn()
445    act_geom_field = layer_src_def.GetGeomFieldDefn(0)
446    nom_geom_out = None
447
448    if act_geom_field:
449        if nom_geom_src:
450            idx_act_geom_field = layer_src_def.GetGeomFieldIndex(nom_geom_src)
451            if idx_act_geom_field >= 0:
452                act_geom_field = layer_src_def.GetGeomFieldDefn(idx_act_geom_field)
453                nom_geom_out = fix_affix_geom_name_layer_gdal(nom_geom_src, layer_src)
454
455        gtype = act_geom_field.GetType()
456        if gtype_layer_from_geoms and not gtype:
457            gtype = layer_gtype_from_geoms(layer_src, nom_geom_src)
458
459        if act_geom_field.GetSpatialRef():
460            srs_lyr_src = act_geom_field.GetSpatialRef()
461
462    if sel_camps:
463        sel_camps = {c.strip().upper() for c in sel_camps}
464
465    geoms_src_minus_affix = geoms_layer_gdal(layer_src, PREFFIX_GEOMS_LAYERS_GDAL)
466    if exclude_cols_geoms:
467        if sel_camps:
468            sel_camps.difference_update(geoms_src_minus_affix)
469        else:
470            sel_camps = cols_layer_gdal(layer_src).difference(geoms_src_minus_affix)
471
472    srs_lyr_dest = None
473    if not srs_lyr_src and epsg_code_src_default:
474        srs_lyr_src = srs_ref_from_epsg_code(epsg_code_src_default, old_axis_mapping=old_axis_mapping_srs)
475    if srs_lyr_src:
476        if old_axis_mapping_srs is None and epsg_code_dest == 4326:
477            old_axis_mapping_srs = drvr_name in DRIVERS_OLD_SRS_AXIS_MAPPING_4326
478
479        if epsg_code_dest:
480            srs_lyr_dest = srs_ref_from_epsg_code(epsg_code_dest, old_axis_mapping=old_axis_mapping_srs)
481        else:
482            srs_lyr_dest = srs_lyr_src
483
484    if ds_gdal_dest.TestCapability(ODsCCreateLayer):
485        # To avoid message "Warning"
486        if ds_gdal_dest.TestCapability(ODsCDeleteLayer) and ds_gdal_dest.GetLayerByName(nom_layer):
487            ds_gdal_dest.DeleteLayer(nom_layer)
488
489        layer_out, nom_layer = create_layer_on_ds_gdal(ds_gdal_dest, nom_layer, nom_geom_out, gtype, srs_lyr_dest,
490                                                       **extra_opt_list)
491    else:
492        layer_out = ds_gdal_dest.GetLayer(0)
493
494    geom_field_out = None
495    if layer_out.TestCapability(OLCAlterFieldDefn):
496        layer_out_def = layer_out.GetLayerDefn()
497        geom_field_out = layer_out_def.GetGeomFieldDefn(0)
498        if geom_field_out:
499            if not nom_geom_out:
500                nom_geom_out = act_geom_field.GetNameRef()
501                if not nom_geom_out:
502                    nom_geom_out = "GEOMETRY"
503            geom_field_out.SetName(nom_geom_out)
504
505    if layer_out.TestCapability(OLCCreateField):
506        for fd in fields_layer_gdal(layer_src):
507            nom_fd = fd.GetNameRef().upper()
508            if layer_out.FindFieldIndex(nom_fd, True) < 0 and (not sel_camps or nom_fd in sel_camps) and \
509                    nom_fd not in (gn.upper() for gn in geoms_src_minus_affix):
510                layer_out.CreateField(fd)
511
512        for gfd in geom_fields_layer_gdal(layer_src):
513            nom_gfd = fix_affix_geom_name_layer_gdal(gfd.GetNameRef(), layer_src).upper()
514            if (not sel_camps or nom_gfd in sel_camps) and \
515                    (nom_gfd not in geoms_src or not exclude_cols_geoms) and \
516                    nom_gfd != nom_geom_out:
517                gfd_def_src = layer_src_def.GetGeomFieldDefn(layer_src_def.GetGeomFieldIndex(gfd.GetNameRef()))
518                gfd_def_dest = GeomFieldDefn(nom_gfd, gfd_def_src.GetType())
519                if srs_lyr_dest:
520                    gfd_def_dest.SetSpatialRef(srs_lyr_dest)
521                layer_out.CreateGeomField(gfd_def_dest)
522
523    if not geom_field_out:
524        null_geoms = True  # Si no hay geom siempre se añaden
525
526    add_layer_features_to_layer(layer_src, ds_gdal_dest, layer_out, nom_geom_src, nom_geom_out,
527                                srs_lyr_dest=srs_lyr_dest,
528                                null_geoms=null_geoms,
529                                tolerance_simplify=tolerance_simplify,
530                                epsg_code_src_default=epsg_code_src_default,
531                                old_axis_mapping_srs=old_axis_mapping_srs)
532
533    if drvr_name == "GPKG":
534        create_spatial_index_layer_gpkg(ds_gdal_dest, nom_layer)
535
536    if drvr_name == 'CSV':
537        path_csv = ds_gdal_dest.GetName()
538        if os.path.exists(path_csv):
539            rename_wkt_geoms_csv(path_csv)
540
541    return ds_gdal_dest.GetLayerByName(nom_layer)

Crea nuevo layer a partir de layer_gdal.

Arguments:
  • ds_gdal_dest (ogr.DataSource):
  • layer_src (ogr.Layer):
  • nom_layer (str=None):
  • nom_geom_src (str=None): Si el nombre no se corresponde con ninguna de las geometrias de la layer_src se utilizará como ALIAS de la geometria 0 (la defecto) de la nueva layer resultante
  • sel_camps (list=None): OPC - Lista de campos a escoger de la layer original
  • exclude_cols_geoms (bool=True): Por defecto de la lista de columnas alfanuméricas (no geometrias) excluirá las columnas que hagan referencia a alguna de las geometrias
  • tolerance_simplify (float=None): Tolerancia (distancia minima) en unidades del srs de la layer Mirar método Simplify() sobre osgeo.ogr.Geometry
  • null_geoms (bool=False): Por defecto no grabará las filas que la geometria principal (nom_geom) es nula
  • gtype_layer_from_geoms (bool=True): Por defecto, si gtype de layer origen == 0 (GEOMETRY) deducirá el tipo de geometria (POINT, LINE o POLYGON) a partir de la primera informada encontrada en la layer_origen
  • epsg_code_dest (int=None): Codigo EPSG para le que se transformarán las geometrias desde el SRS original
  • epsg_code_src_default (int=4326): Codigo EPSG que se usará para las layer_src que NO tengan SRS asignado
  • old_axis_mapping_srs (bool=None): setea si quieres usar old mapping axes en (LONG, LAT) (GDAL >3.2)
  • **extra_opt_list (str): Lista claves-valores de opciones para createLayer del driver de ds_gdal indicado
Returns:

layer_out (ogr.Layer)

def rename_wkt_geoms_csv(path_csv):
544def rename_wkt_geoms_csv(path_csv):
545    """
546    Remove "_WKT" affix from WKT geometry fields in CSV file
547
548    Args:
549        path_csv (str): Path to CSV file
550
551    Returns:
552
553    """
554    with open(path_csv, 'r', encoding='utf-8') as r_file:
555        content_csv = r_file.readlines()
556        first_line = next(iter(content_csv), None)
557    if first_line:
558        new_first_line = first_line.replace(PREFFIX_GEOMS_LAYERS_GDAL_CSV, '')
559        if new_first_line != first_line:
560            content_csv[0] = new_first_line
561            with open(path_csv, 'w', encoding='utf-8') as w_file:
562                w_file.writelines(content_csv)

Remove "_WKT" affix from WKT geometry fields in CSV file

Arguments:
  • path_csv (str): Path to CSV file

Returns:

def add_layer_features_to_layer( layer_src, ds_gdal_dest, layer_dest, nom_geom_src=None, nom_geom_dest=None, srs_lyr_dest=None, null_geoms=False, tolerance_simplify=None, remove_prev_features=False, epsg_code_src_default=4326, old_axis_mapping_srs=None):
565def add_layer_features_to_layer(layer_src, ds_gdal_dest, layer_dest, nom_geom_src=None, nom_geom_dest=None,
566                                srs_lyr_dest=None, null_geoms=False, tolerance_simplify=None,
567                                remove_prev_features=False, epsg_code_src_default=4326, old_axis_mapping_srs=None):
568    """
569    From a layer of a dataset, add the features to another layer of another dataset.
570
571    Args:
572        layer_src (ogr.Layer): Layer origen
573        ds_gdal_dest (gdal.Dataset): Dataset destino
574        layer_dest (ogr.Layer): Layer destino
575        nom_geom_src (str=None): Si el nombre no se corresponde con ninguna de las geometrias de la layer_src se utilizará
576                            como ALIAS de la geometria 0 (la defecto) de la nueva layer resultante
577        nom_geom_dest (str):
578        srs_lyr_dest (ogr.SpatialReference=None): Spatial Reference System de la layer_dest
579        null_geoms (bool=False): Por defecto no grabará las filas que la geometria principal (nom_geom) es nula
580        tolerance_simplify (float=None): Tolerancia (distancia minima) en unidades del srs de la layer
581             Mirar método Simplify() sobre osgeo.ogr.Geometry
582        remove_prev_features (bool=False): Si es True, se eliminarán las features previas de la layer_dest
583        epsg_code_src_default (int=4326): EPSG code del srs de la layer_src si no se encuentra
584        old_axis_mapping_srs (bool=None): setea si quieres usar old mapping axes en (LONG, LAT) (GDAL >3.2)
585
586    Returns:
587
588    """
589    geoms_out = geoms_layer_gdal(layer_dest)
590    cols_out = cols_layer_gdal(layer_dest)
591
592    geom_transform = None
593    srs_lyr_src = srs_for_layer(layer_src, nom_geom_src)
594    if not srs_lyr_src:
595        srs_lyr_src = srs_ref_from_epsg_code(epsg_code_src_default, old_axis_mapping_srs)
596
597    if srs_lyr_dest is None:
598        srs_lyr_dest = srs_for_layer(layer_dest, nom_geom_dest)
599
600    if srs_lyr_dest and not srs_lyr_src.IsSame(srs_lyr_dest):
601        geom_transform = osr.CoordinateTransformation(srs_lyr_src, srs_lyr_dest)
602
603    ds_trans = ds_gdal_dest.TestCapability(ODsCTransactions)
604
605    if remove_prev_features:
606        if ds_trans:
607            layer_dest.StartTransaction()
608        for feat in layer_dest:
609            layer_dest.DeleteFeature(feat.GetFID())
610        if ds_trans:
611            layer_dest.CommitTransaction()
612
613    if ds_trans:
614        layer_dest.StartTransaction()
615
616    i = 0
617    for feat_src, geom_src, nt_src in feats_layer_gdal(layer_src, nom_geom_src):
618        cols_out_chk = [col.upper() for col in cols_out.union(geoms_out)]
619        vals_camps = {nc: val for nc, val in nt_src._asdict().items()
620                      if nc.upper() in cols_out_chk}
621        if null_geoms or geom_src:
622            if nom_geom_dest:
623                vals_camps[nom_geom_dest.upper()] = geom_src
624
625            add_feature_to_layer_gdal(layer_dest,
626                                      tolerance_simplify=tolerance_simplify,
627                                      geom_trans=geom_transform,
628                                      **vals_camps)
629
630            if i > 0 and (i % 1000) == 0:
631                print_debug("{} registres tractats...".format(str(i)))
632            i += 1
633    if ds_trans:
634        layer_dest.CommitTransaction()

From a layer of a dataset, add the features to another layer of another dataset.

Arguments:
  • layer_src (ogr.Layer): Layer origen
  • ds_gdal_dest (gdal.Dataset): Dataset destino
  • layer_dest (ogr.Layer): Layer destino
  • nom_geom_src (str=None): Si el nombre no se corresponde con ninguna de las geometrias de la layer_src se utilizará como ALIAS de la geometria 0 (la defecto) de la nueva layer resultante
  • nom_geom_dest (str):
  • srs_lyr_dest (ogr.SpatialReference=None): Spatial Reference System de la layer_dest
  • null_geoms (bool=False): Por defecto no grabará las filas que la geometria principal (nom_geom) es nula
  • tolerance_simplify (float=None): Tolerancia (distancia minima) en unidades del srs de la layer Mirar método Simplify() sobre osgeo.ogr.Geometry
  • remove_prev_features (bool=False): Si es True, se eliminarán las features previas de la layer_dest
  • epsg_code_src_default (int=4326): EPSG code del srs de la layer_src si no se encuentra
  • old_axis_mapping_srs (bool=None): setea si quieres usar old mapping axes en (LONG, LAT) (GDAL >3.2)

Returns:

def create_layer_on_ds_gdal( ds_gdal_dest, nom_layer, nom_geom=None, gtype=None, srs_lyr_out=None, **extra_opt_list):
637def create_layer_on_ds_gdal(ds_gdal_dest, nom_layer, nom_geom=None, gtype=None, srs_lyr_out=None, **extra_opt_list):
638    """
639
640    Args:
641        ds_gdal_dest (ogr.DataSource):
642        nom_layer (str):
643        nom_geom (str=None):
644        gtype (OGRwkbGeometryType=None): Indicar integer representativo del tipo de geometria de la layer (ogr.wkbPoint, ogr.wkbPolygon, ogr.LineString, ...)
645        srs_lyr_out (osr.SpatialReference=None): codigo epsg del sistema de coordenadas de la geometria
646        **extra_opt_list: Calves valores option list creacion layer gdal
647
648    Returns:
649        layer_gdal (ogr.Layer), nom_layer (str)
650    """
651    drvr_name = ds_gdal_dest.GetDriver().GetName().upper()
652
653    opt_list = {k.upper(): v.upper() for k, v in extra_opt_list.items()}
654
655    opt_geom_name = "GEOMETRY_NAME"
656    if nom_geom:
657        opt_list[opt_geom_name] = "{}={}".format(opt_geom_name, nom_geom.upper())
658    else:
659        if opt_geom_name in opt_list:
660            opt_list.pop(opt_geom_name)
661        gtype = ogr.wkbNone
662
663    opt_list["IDENTIFIER"] = opt_list.get("IDENTIFIER", "IDENTIFIER={}".format(nom_layer))
664
665    opt_list = set_create_option_list_for_driver_gdal(drvr_name, **{k.upper(): v.upper() for k, v in opt_list.items()})
666
667    if ds_gdal_dest.TestCapability(ODsCDeleteLayer) and ds_gdal_dest.GetLayerByName(nom_layer):
668        print_warning("!ATENCION! - Se sobreescribirá la layer '{}' sobre el datasource GDAL '{}'".format(
669            nom_layer, ds_gdal_dest.GetDescription()))
670        ds_gdal_dest.DeleteLayer(nom_layer)
671
672    if drvr_name.upper() == "KML":
673        nom_layer = nom_layer.replace("-", "__")
674
675    layer_out = ds_gdal_dest.CreateLayer(nom_layer, srs_lyr_out, geom_type=gtype, options=list(opt_list.values()))
676
677    return layer_out, layer_out.GetName() if layer_out else None
Arguments:
  • ds_gdal_dest (ogr.DataSource):
  • nom_layer (str):
  • nom_geom (str=None):
  • gtype (OGRwkbGeometryType=None): Indicar integer representativo del tipo de geometria de la layer (ogr.wkbPoint, ogr.wkbPolygon, ogr.LineString, ...)
  • srs_lyr_out (osr.SpatialReference=None): codigo epsg del sistema de coordenadas de la geometria
  • **extra_opt_list: Calves valores option list creacion layer gdal
Returns:

layer_gdal (ogr.Layer), nom_layer (str)

def create_spatial_index_layer_gpkg(ds_gpkg, nom_layer):
680def create_spatial_index_layer_gpkg(ds_gpkg, nom_layer):
681    """
682    Crea spatial index sobre una layer (layer_gpkg) de un datosource gpkg (ds_gpkg)
683
684    Args:
685        ds_gpkg (osgeo.ogr.DataSource):
686        layer_gpkg (ogr.Layer):
687
688    Returns:
689        bool
690    """
691    layer_gpkg = ds_gpkg.GetLayerByName(nom_layer)
692    if layer_gpkg and layer_gpkg.GetGeometryColumn():
693        # Se crea spatial_index ya que GDAL NO lo hace
694        ds_gpkg.StartTransaction()
695        ds_gpkg.ExecuteSQL("SELECT CreateSpatialIndex('{tab_name}', '{geom_name}') ".format(
696            tab_name=layer_gpkg.GetName(),
697            geom_name=layer_gpkg.GetGeometryColumn()))
698        ds_gpkg.CommitTransaction()
699
700        return True
701    else:
702        return False

Crea spatial index sobre una layer (layer_gpkg) de un datosource gpkg (ds_gpkg)

Arguments:
  • ds_gpkg (osgeo.ogr.DataSource):
  • layer_gpkg (ogr.Layer):
Returns:

bool

def add_feature_to_layer_gdal( layer_gdal, tolerance_simplify=None, geom_trans=None, commit=False, **valors_camps):
705def add_feature_to_layer_gdal(layer_gdal, tolerance_simplify=None, geom_trans=None, commit=False, **valors_camps):
706    """
707
708    Args:
709        layer_gdal (ogr.Layer):
710        tolerance_simplify (float=None): Tolerancia (distancia minima) en unidades del srs de la layer.
711                                        Mirar método Simplify() sobre osgeo.ogr.Geometry
712        geom_trans (osr.CoordinateTransformation=None): transformacion para convertir las geometrias a otro SRS
713        commit (bool=False): Per defecte no farà commit de la transaccio
714        **valors_camps: pares nombre_campo=valor de la feature a crear
715
716    Returns:
717        feat (ogr.Feature)
718    """
719    dd_feat = ogr.Feature(layer_gdal.GetLayerDefn())
720
721    for camp, val in valors_camps.items():
722        idx_geom = dd_feat.GetGeomFieldIndex(camp)
723        es_geom = idx_geom >= 0
724        if es_geom:
725            if val:
726                if tolerance_simplify:
727                    val = val.Simplify(tolerance_simplify)
728                if val and geom_trans:
729                    val.Transform(geom_trans)
730
731            dd_feat.SetGeomField(idx_geom, val)
732
733        idx_fld = dd_feat.GetFieldIndex(camp)
734        if idx_fld >= 0:
735            if val:
736                if es_geom or hasattr(val, "ExportToIsoWkt"):
737                    val = val.ExportToIsoWkt()
738                dd_feat.SetField(camp, val)
739            else:
740                dd_feat.SetFieldNull(camp)
741
742        if idx_fld < 0 and idx_geom < 0:
743            if isinstance(val, Geometry):
744                if geom_trans:
745                    val.Transform(geom_trans)
746                dd_feat.SetGeometry(val)
747            elif val:
748                print_warning("!ATENCION! - La :layer_gdal no contiene el campo '{}'".format(camp))
749
750    commit_trans = commit and layer_gdal.TestCapability(OLCTransactions)
751    if commit_trans:
752        layer_gdal.StartTransaction()
753
754    new_feat = layer_gdal.CreateFeature(dd_feat)
755
756    if commit_trans:
757        layer_gdal.CommitTransaction()
758
759    return new_feat
Arguments:
  • layer_gdal (ogr.Layer):
  • tolerance_simplify (float=None): Tolerancia (distancia minima) en unidades del srs de la layer. Mirar método Simplify() sobre osgeo.ogr.Geometry
  • geom_trans (osr.CoordinateTransformation=None): transformacion para convertir las geometrias a otro SRS
  • commit (bool=False): Per defecte no farà commit de la transaccio
  • **valors_camps: pares nombre_campo=valor de la feature a crear
Returns:

feat (ogr.Feature)

def drivers_ogr_gdal_disponibles():
762def drivers_ogr_gdal_disponibles():
763    """
764    Retorna lista de drivers disponibles a través de la librería osgeo-gdal-ogr
765
766    Returns:
767        dict
768    """
769    cnt = ogr.GetDriverCount()
770    driver_list = []
771    drivers = OrderedDict()
772
773    for i in range(cnt):
774        driver = ogr.GetDriver(i)
775        driver_name = driver.GetName()
776        driver_list.append(driver_name)
777
778    for driver_name in driver_list:
779        # Is File GeoDatabase available?
780        drv = ogr.GetDriverByName(driver_name)
781        if drv is None:
782            print_warning("{} !!ATENTION - driver NOT available!!".format(driver_name))
783        else:
784            drivers[driver_name] = drv
785            print_debug(driver_name)
786
787    return drivers

Retorna lista de drivers disponibles a través de la librería osgeo-gdal-ogr

Returns:

dict

def drivers_ogr_gdal_vector_file():
790def drivers_ogr_gdal_vector_file():
791    """
792    Devuelve diccionario con los driver gdal para fichero vectorial
793
794    Returns:
795        dict
796    """
797    return {nd: d for nd, d in drivers_ogr_gdal_disponibles().items()
798            if hasattr(d, "GetMetadata_Dict") and d.GetMetadata_Dict().get('DMD_EXTENSIONS')}

Devuelve diccionario con los driver gdal para fichero vectorial

Returns:

dict

def format_nom_column(nom_col):
801def format_nom_column(nom_col):
802    """
803
804    Args:
805        nom_col:
806
807    Returns:
808        str
809    """
810    return nom_col.replace(" ", "_")
Arguments:
  • nom_col:
Returns:

str

def namedtuple_layer_gdal(layer_gdal, extract_affix_geom_fld=None):
813def namedtuple_layer_gdal(layer_gdal, extract_affix_geom_fld=None):
814    """
815    Devuelve namedTuple con los campos del layer pasado por parametro
816
817    Args:
818        layer_gdal:
819        extract_affix_geom_fld (str=None):
820
821    Returns:
822        namedtuple: con nombre "gdalFeatDef_{NOM_LAYER}" y con los campos de la layer
823    """
824    camps_layer = set()
825    for fld in fields_layer_gdal(layer_gdal):
826        camps_layer.add(format_nom_column(fld.GetNameRef()))
827
828    for nom_geom in geoms_layer_gdal(layer_gdal, extract_affix=extract_affix_geom_fld):
829        camps_layer.add(format_nom_column(nom_geom))
830
831    nom_layer = layer_gdal.GetName().upper().split(".")[0].replace("-", "_")
832    return namedtuple(f"gdalFeatDef_{nom_layer}", camps_layer)

Devuelve namedTuple con los campos del layer pasado por parametro

Arguments:
  • layer_gdal:
  • extract_affix_geom_fld (str=None):
Returns:

namedtuple: con nombre "gdalFeatDef_{NOM_LAYER}" y con los campos de la layer

def feats_layer_ds_gdal(ds_gdal, nom_layer=None, filter_sql=None):
835def feats_layer_ds_gdal(ds_gdal, nom_layer=None, filter_sql=None):
836    """
837    Itera las features (registros de una layer de gdal) y los devuelve como un namdetuple
838
839    Args:
840        ds_gdal: datasource gdal
841        nom_layer (str=None): Si no viene informado cogerá la primera layer que encuentre en el datasource
842        filter_sql (str=None): Si viene informado se aplicará como filtro sql a la layer seleccionada.
843                            Utiliza OGR SQL (vease https://www.gdal.org/ogr_sql.html)
844
845    Returns:
846        ogr.Feature, ogr.Geometry, namedtuple_layer_gdal
847    """
848    if not nom_layer:
849        layer_gdal = ds_gdal.GetLayer()
850    else:
851        layer_gdal = ds_gdal.GetLayerByName(nom_layer)
852
853    if layer_gdal:
854        for feat, geom, vals in feats_layer_gdal(layer_gdal, filter_sql=filter_sql):
855            yield feat, geom, vals

Itera las features (registros de una layer de gdal) y los devuelve como un namdetuple

Arguments:
  • ds_gdal: datasource gdal
  • nom_layer (str=None): Si no viene informado cogerá la primera layer que encuentre en el datasource
  • filter_sql (str=None): Si viene informado se aplicará como filtro sql a la layer seleccionada. Utiliza OGR SQL (vease https://www.gdal.org/ogr_sql.html)
Returns:

ogr.Feature, ogr.Geometry, namedtuple_layer_gdal

def feats_layer_gdal( layer_gdal, nom_geom=None, filter_sql=None, extract_affix_geom_fld='geom_'):
858def feats_layer_gdal(layer_gdal, nom_geom=None, filter_sql=None, extract_affix_geom_fld=PREFFIX_GEOMS_LAYERS_GDAL):
859    """
860    Itera las features (registros de una layer de gdal) y los devuelve como un namdtuple
861
862    Args:
863        layer_gdal (ogr.Layer):
864        nom_geom (str=None): Por defecto la geometria activa o principal
865        filter_sql (str=None): Si viene informado se aplicará como filtro sql a la layer seleccionada.
866                            Utiliza OGR SQL (vease https://www.gdal.org/ogr_sql.html)
867        extract_affix_geom_fld (str=SUFFIX_GEOMS_LAYERS_GDAL): Por defecto quita el sufijo 'geom_' a los campos geom
868
869    Returns:
870        ogr.Feature, ogr.Geometry, namedtuple_layer_gdal
871    """
872    layer_gdal.ResetReading()
873    ntup_layer = namedtuple_layer_gdal(layer_gdal, extract_affix_geom_fld)
874    n_geoms_layer = {nom_geom_feat: fix_affix_geom_name_layer_gdal(nom_geom_feat, layer_gdal, extract_affix_geom_fld)
875                     for nom_geom_feat in geoms_layer_gdal(layer_gdal)}
876
877    if filter_sql:
878        layer_gdal.SetAttributeFilter(filter_sql)
879
880    def vals_feature_gdal(feat_gdal):
881        vals = {}
882        for camp, val in feat_gdal.items().items():
883            vals[format_nom_column(camp)] = val
884
885        for camp_geom, camp_geom_val in n_geoms_layer.items():
886            idx_geom = feat_gdal.GetGeomFieldIndex(camp_geom)
887            if idx_geom >= 0:
888                vals[camp_geom_val] = feat_gdal.GetGeomFieldRef(idx_geom)
889
890        return vals
891
892    if layer_gdal:
893        for f_tab in layer_gdal:
894            idx_geom = f_tab.GetGeomFieldIndex(nom_geom) if nom_geom else -1
895            yield f_tab, \
896                f_tab.GetGeomFieldRef(idx_geom) if idx_geom >= 0 else f_tab.geometry(), \
897                ntup_layer(**vals_feature_gdal(f_tab))
898
899        layer_gdal.ResetReading()

Itera las features (registros de una layer de gdal) y los devuelve como un namdtuple

Arguments:
  • layer_gdal (ogr.Layer):
  • nom_geom (str=None): Por defecto la geometria activa o principal
  • filter_sql (str=None): Si viene informado se aplicará como filtro sql a la layer seleccionada. Utiliza OGR SQL (vease https://www.gdal.org/ogr_sql.html)
  • extract_affix_geom_fld (str=SUFFIX_GEOMS_LAYERS_GDAL): Por defecto quita el sufijo 'geom_' a los campos geom
Returns:

ogr.Feature, ogr.Geometry, namedtuple_layer_gdal

def distinct_vals_camp_layer_gdal(layer_gdal, nom_camp, filter_sql=None):
902def distinct_vals_camp_layer_gdal(layer_gdal, nom_camp, filter_sql=None):
903    """
904    Devuelve set con distintos valores para el campo indicado del layer GDAL indicada
905
906    Args:
907        layer_gdal (ogr.Layer):
908        nom_camp (str):
909        filter_sql (str=None):
910
911    Returns:
912        set
913    """
914    if nom_camp.upper() not in cols_layer_gdal(layer_gdal):
915        raise Exception("Argumento :NOM_CAMP = '{}' erróneo ya que "
916                        "LAYER_GDAL no contiene el campo indicado".format(nom_camp))
917
918    return {getattr(nt_feat, nom_camp.upper())
919            for feat, geom, nt_feat in feats_layer_gdal(layer_gdal, filter_sql=filter_sql)}

Devuelve set con distintos valores para el campo indicado del layer GDAL indicada

Arguments:
  • layer_gdal (ogr.Layer):
  • nom_camp (str):
  • filter_sql (str=None):
Returns:

set

def fields_layer_gdal(layer_gdal):
922def fields_layer_gdal(layer_gdal):
923    """
924    Itera sobre los FieldDefn de una layer gdal
925
926    Args:
927        layer_gdal:
928
929    Yields:
930        osgeo.ogr.FieldDefn
931    """
932    layer_def = layer_gdal.GetLayerDefn()
933    for i in range(0, layer_def.GetFieldCount()):
934        yield layer_def.GetFieldDefn(i)
935
936    layer_def = None

Itera sobre los FieldDefn de una layer gdal

Arguments:
  • layer_gdal:
Yields:

osgeo.ogr.FieldDefn

def geom_fields_layer_gdal(layer_gdal):
939def geom_fields_layer_gdal(layer_gdal):
940    """
941    Itera sobre los GeomFieldDefn de una layer gdal
942
943    Args:
944        layer_gdal:
945
946    Yields:
947        osgeo.ogr.GeomFieldDefn
948    """
949    layer_def = layer_gdal.GetLayerDefn()
950    for i in range(0, layer_def.GetGeomFieldCount()):
951        yield layer_def.GetGeomFieldDefn(i)
952
953    layer_def = None

Itera sobre los GeomFieldDefn de una layer gdal

Arguments:
  • layer_gdal:
Yields:

osgeo.ogr.GeomFieldDefn

def nom_layers_datasource_gdal(ds_gdal):
956def nom_layers_datasource_gdal(ds_gdal):
957    """
958
959    Args:
960        ds_gdal (ogr.Datasource:
961
962    Returns:
963        set
964    """
965    return {l.GetName() for l in ds_gdal}
Arguments:
  • ds_gdal (ogr.Datasource:
Returns:

set

def cols_layer_gdal(layer_gdal):
968def cols_layer_gdal(layer_gdal):
969    """
970    Retorna lista con las columnas de una layer gdal
971
972    Args:
973        layer_gdal:
974
975    Returns:
976        set
977    """
978    camps = set()
979    for fd in fields_layer_gdal(layer_gdal):
980        # camps.add(fd.GetName().upper())
981        camps.add(fd.GetNameRef())
982
983    return camps

Retorna lista con las columnas de una layer gdal

Arguments:
  • layer_gdal:
Returns:

set

def geoms_layer_gdal(layer_gdal, extract_affix=None):
 986def geoms_layer_gdal(layer_gdal, extract_affix=None):
 987    """
 988    Retorna lista con las columnas geométricas de una layer gdal
 989
 990    Args:
 991        layer_gdal:
 992        extract_affix (str=None=): if informed extract the affix passed
 993
 994    Returns:
 995        set
 996    """
 997    camps_geom = set()
 998    for gdf in geom_fields_layer_gdal(layer_gdal):
 999        # camps_geom.add(gdf.GetName().upper())
1000        name_ref = gdf.GetNameRef()
1001        if extract_affix:
1002            name_ref = fix_affix_geom_name_layer_gdal(name_ref, layer_gdal, extract_affix)
1003        camps_geom.add(name_ref)
1004
1005    return camps_geom

Retorna lista con las columnas geométricas de una layer gdal

Arguments:
  • layer_gdal:
  • extract_affix (str=None=): if informed extract the affix passed
Returns:

set

def fix_affix_geom_name_layer_gdal(geom_name, layer_gdal, affix='geom_'):
1008def fix_affix_geom_name_layer_gdal(geom_name, layer_gdal, affix=PREFFIX_GEOMS_LAYERS_GDAL):
1009    """
1010    Extract affix (default=PREFFIX_GEOMS_LAYERS_GDAL) from name geoms if correspond with other column
1011    Fix GDAL > 3.4 where geoms on GeoCSV arrive with preffix 'geom_'
1012
1013    Args:
1014          geom_name (str):
1015          layer_gdal:
1016          affix (str=PREFFIX_GEOMS_LAYERS_GDAL):
1017
1018    Returns:
1019        str
1020    """
1021    cols_layer = cols_layer_gdal(layer_gdal)
1022    geom_name_layer = geom_name
1023    if geom_name.lower().startswith(affix):
1024        geom_name_aux = re.sub(affix, '', geom_name, flags=re.IGNORECASE)
1025        if geom_name_aux.lower() in (col.lower() for col in cols_layer):
1026            geom_name_layer = geom_name_aux
1027
1028    return geom_name_layer

Extract affix (default=PREFFIX_GEOMS_LAYERS_GDAL) from name geoms if correspond with other column Fix GDAL > 3.4 where geoms on GeoCSV arrive with preffix 'geom_'

Arguments:
  • geom_name (str):
  • layer_gdal:
  • affix (str=PREFFIX_GEOMS_LAYERS_GDAL):
Returns:

str

def add_layer_gdal_to_ds_gdal( ds_gdal, layer_gdal, nom_layer=None, lite=False, srs_epsg_code=None, multi_geom=False, nom_geom=None, null_geoms=False, **extra_opt_list):
1031def add_layer_gdal_to_ds_gdal(ds_gdal, layer_gdal, nom_layer=None, lite=False, srs_epsg_code=None, multi_geom=False,
1032                              nom_geom=None, null_geoms=False, **extra_opt_list):
1033    """
1034    Añade una layer_gdal a un datasource_gdal. Si es una layer con multigeometrias las separa en una layer por geometria
1035
1036    Args:
1037        ds_gdal (osgeo.ogr.Datasource):
1038        layer_gdal (osgeo.ogr.Layer):
1039        nom_layer (str=None):
1040        lite (bool=False):
1041        srs_epsg_code (int=None): codigo EPSG para el sistema de coordenadas con el que se quieren convertir
1042                                  las geometrias
1043        nom_geom (str=None): nombre de geometria de layer origen (layer_gdal) que se copiará, si no todas
1044        multi_geom (bool=False): Si el DS_GDAL destino permite multigeometria
1045        null_geoms (bool=False): Indica si se admitirán registros con NULL geoms. Por defecto NO
1046        **extra_opt_list (str): Lista claves-valores de opciones para createLayer del driver de ds_gdal indicado
1047
1048    Returns:
1049        new_layers_ds_gdal (list)
1050    """
1051    new_layers_ds_gdal = []
1052
1053    if not nom_layer:
1054        nom_layer = layer_gdal.GetName()
1055
1056    geoms_layer = geoms_layer_gdal(layer_gdal)
1057    if nom_geom:
1058        if nom_geom.upper() not in (g.upper() for g in geoms_layer):
1059            Exception("!ERROR! - Nombre de geometria '{}' no existe en la layer GDAL origen")
1060        else:
1061            geoms_layer = (nom_geom,)
1062
1063    if geoms_layer:
1064        tol = None
1065        if lite:
1066            tol = SIMPLIFY_TOLERANCE
1067
1068        nom_layer_base = nom_layer.split("-")[0]
1069        if not multi_geom:
1070            for geom_name in geoms_layer:
1071                # Fix GDAL > 3.4 where geoms on GeoCSV arrive with preffix 'geom_'
1072                geom_name_layer = fix_affix_geom_name_layer_gdal(geom_name, layer_gdal)
1073
1074                nom_layer = "{}-{}".format(nom_layer_base, geom_name_layer).lower()
1075                extra_opt_list["GEOMETRY_NAME"] = "GEOMETRY_NAME={}".format(geom_name_layer)
1076                lyr = create_layer_from_layer_gdal_on_ds_gdal(ds_gdal, layer_gdal, nom_layer, geom_name,
1077                                                              tolerance_simplify=tol, null_geoms=null_geoms,
1078                                                              epsg_code_dest=srs_epsg_code, **extra_opt_list)
1079                new_layers_ds_gdal.append(lyr)
1080        else:
1081            lyr = create_layer_from_layer_gdal_on_ds_gdal(ds_gdal, layer_gdal, nom_layer, exclude_cols_geoms=False,
1082                                                          tolerance_simplify=tol, null_geoms=True,
1083                                                          epsg_code_dest=srs_epsg_code, **extra_opt_list)
1084            new_layers_ds_gdal.append(lyr)
1085    else:
1086        if ds_gdal.GetDriver().GetName() == 'PostgreSQL':
1087            extra_opt_list.update(dict(
1088                precision=extra_opt_list.get('precision', 'PRECISION=NO')
1089            ))
1090
1091        lyr = copy_layer_gdal_to_ds_gdal(layer_gdal, ds_gdal, nom_layer.lower(), **extra_opt_list)
1092        new_layers_ds_gdal.append(lyr)
1093
1094    return new_layers_ds_gdal

Añade una layer_gdal a un datasource_gdal. Si es una layer con multigeometrias las separa en una layer por geometria

Arguments:
  • ds_gdal (osgeo.ogr.Datasource):
  • layer_gdal (osgeo.ogr.Layer):
  • nom_layer (str=None):
  • lite (bool=False):
  • srs_epsg_code (int=None): codigo EPSG para el sistema de coordenadas con el que se quieren convertir las geometrias
  • nom_geom (str=None): nombre de geometria de layer origen (layer_gdal) que se copiará, si no todas
  • multi_geom (bool=False): Si el DS_GDAL destino permite multigeometria
  • null_geoms (bool=False): Indica si se admitirán registros con NULL geoms. Por defecto NO
  • **extra_opt_list (str): Lista claves-valores de opciones para createLayer del driver de ds_gdal indicado
Returns:

new_layers_ds_gdal (list)

def copy_layers_gpkg( ds_gpkg, driver, dir_base, lite=False, srs_epsg_code=None, zipped=True):
1097def copy_layers_gpkg(ds_gpkg, driver, dir_base, lite=False, srs_epsg_code=None, zipped=True):
1098    """
1099
1100    Args:
1101        ds_gpkg (osgeo.ogr.Datasource):
1102        driver (str):
1103        dir_base (str=None):
1104        lite (bool=False):
1105        srs_epsg_code (int=None): codigo EPSG para el sistema de coordenadas con el que se quieren convertir
1106                                  las geometrias
1107        zipped (bool=False):
1108
1109    Returns:
1110        num_layers (int)
1111    """
1112    num_layers = 0
1113    subdir_drvr = os.path.normpath(os.path.join(dir_base, driver.upper()))
1114    utils.create_dir(subdir_drvr)
1115
1116    for layer_gpkg in (ds_gpkg.GetLayer(id_lyr) for id_lyr in range(ds_gpkg.GetLayerCount() - 1)):
1117        if driver == "GPKG":
1118            nom_ds, ext = utils.split_ext_file(os.path.basename(ds_gpkg.name))
1119        else:
1120            nom_ds = f"{layer_gpkg.GetName()}".lower()
1121
1122        ds_gdal, existia = datasource_gdal_vector_file(driver, nom_ds, subdir_drvr)
1123
1124        add_layer_gdal_to_ds_gdal(ds_gdal, layer_gpkg, lite=lite, srs_epsg_code=srs_epsg_code)
1125
1126        num_layers += 1
1127
1128    return num_layers
Arguments:
  • ds_gpkg (osgeo.ogr.Datasource):
  • driver (str):
  • dir_base (str=None):
  • lite (bool=False):
  • srs_epsg_code (int=None): codigo EPSG para el sistema de coordenadas con el que se quieren convertir las geometrias
  • zipped (bool=False):
Returns:

num_layers (int)

def set_csvt_for_layer_csv(path_csv, **tipus_camps):
1131def set_csvt_for_layer_csv(path_csv, **tipus_camps):
1132    """
1133    Crea/Modifica el CSVT asociado con los tipos indicados para cada columna
1134
1135    Args:
1136        path_csv (str):
1137        **tipus_camps: clave=valor con el nombre del campo y el tipo de campo asociado (p.e. String(25), WKT, Integer,...)
1138
1139    Returns:
1140        path_csvt (str)
1141    """
1142    lyr_csv, nom_layer, ds_lyr = layer_gdal_from_file(path_csv, "CSV")
1143    if not lyr_csv:
1144        print_warning("!ATENCIO! - No s'ha pogut obrir la layer CSV '{}'".format(path_csv))
1145        return
1146
1147    path_lyr_csvt = os.path.join(os.path.dirname(path_csv), "{}.csvt".format(nom_layer))
1148    tips_lyr = {}
1149    for fld in fields_layer_gdal(lyr_csv):
1150        tip_fld = fld.GetFieldTypeName(fld.GetType())
1151        sufix = ""
1152        w = fld.GetWidth()
1153        if w:
1154            sufix = "{}".format(w)
1155        p = fld.GetPrecision()
1156        if p:
1157            sufix += ".{}".format(p)
1158        if sufix:
1159            tip_fld += "({})".format(sufix)
1160
1161        tips_lyr[fld.name.upper()] = tip_fld
1162
1163    for nom_camp, tip_camp in tipus_camps.items():
1164        nom_camp = nom_camp.upper()
1165        if nom_camp not in tips_lyr:
1166            print_warning("!ATENCIO! - Camp '{}' no existeix sobre la layer CSV '{}'".format(nom_camp, path_csv))
1167            continue
1168
1169        tips_lyr[nom_camp] = tip_camp
1170
1171    with open(path_lyr_csvt, mode="w", encoding="utf8") as f_csvt:
1172        f_csvt.write(",".join(tips_lyr.values()))

Crea/Modifica el CSVT asociado con los tipos indicados para cada columna

Arguments:
  • path_csv (str):
  • **tipus_camps: clave=valor con el nombre del campo y el tipo de campo asociado (p.e. String(25), WKT, Integer,...)
Returns:

path_csvt (str)

def zip_and_clean_ds_csv(path_csv):
1175def zip_and_clean_ds_csv(path_csv):
1176    """
1177    # Zipea datasource osgeo CSV y como GDAL no crea los tipos WKT para las geometrias en el CSVT se fuerza
1178
1179    Args:
1180        path_csv (str): path Datasource osgeo CSV
1181
1182    Returns:
1183        zip_path (str)
1184    """
1185    ds_gdal_csv, dummy = datasource_gdal_vector_file("CSV", os.path.basename(path_csv).split(".")[0],
1186                                                     os.path.dirname(path_csv))
1187    layer_csv = ds_gdal_csv.GetLayer(0)
1188    tips_geoms = {gn: "WKT" for gn in
1189                  (*geoms_layer_gdal(layer_csv, extract_affix=PREFFIX_GEOMS_LAYERS_GDAL),
1190                   *geoms_layer_gdal(layer_csv, extract_affix=PREFFIX_GEOMS_LAYERS_GDAL_CSV))}
1191    path_csv = ds_gdal_csv.GetDescription()
1192    ds_gdal_csv = None  # Cerramos el datasource para que se guarden los cambios
1193    if path_csv and os.path.exists(path_csv):
1194        if tips_geoms:
1195            set_csvt_for_layer_csv(path_csv, **tips_geoms)
1196        rename_wkt_geoms_csv(path_csv)
1197    dir_base_csv = os.path.dirname(path_csv)
1198    nom_layer = os.path.basename(path_csv).split(".")[0]
1199    l_files_csv = [os.path.join(dir_base_csv, nf) for nf in os.listdir(dir_base_csv)
1200                   if nf.lower().startswith(nom_layer.lower()) and
1201                   any(nf.lower().endswith(ext) for ext in ('csv', 'csvt'))]
1202    zip_path = utils.zip_files(os.path.join(dir_base_csv, "{}.zip".format(nom_layer)), l_files_csv)
1203    if zip_path:
1204        for fl_csv in l_files_csv:
1205            os.remove(fl_csv)
1206
1207    return zip_path

Zipea datasource osgeo CSV y como GDAL no crea los tipos WKT para las geometrias en el CSVT se fuerza

Arguments:
  • path_csv (str): path Datasource osgeo CSV
Returns:

zip_path (str)

def convert_angle(pt_xy, deg_ang, orig_srs, dest_srs):
1210def convert_angle(pt_xy, deg_ang, orig_srs, dest_srs):
1211    """
1212
1213    Args:
1214        pt_xy (tuple):
1215        deg_ang (float):
1216        orig_srs (osr.SpatialReference):
1217        dest_srs (osr.SpatialReference):
1218
1219    Returns:
1220
1221    """
1222    trans = osr.CoordinateTransformation(orig_srs, dest_srs)
1223    x, y = pt_xy
1224    dx = math.sin(math.radians(deg_ang)) * 0.00000001
1225    dy = math.cos(math.radians(deg_ang)) * 0.00000001
1226    pt1 = ogr.CreateGeometryFromWkt("POINT ({} {})".format(x, y))
1227    pt2 = ogr.CreateGeometryFromWkt("POINT ({} {})".format(x + dx, y + dy))
1228    pt1.Transform(trans)
1229    pt2.Transform(trans)
1230    x1, y1, z1 = pt1.GetPoint()
1231    x2, y2, z2 = pt2.GetPoint()
1232
1233    return math.degrees(math.atan2(y2 - y1, x2 - x1))
Arguments:
  • pt_xy (tuple):
  • deg_ang (float):
  • orig_srs (osr.SpatialReference):
  • dest_srs (osr.SpatialReference):

Returns:

def transform_ogr_geom(a_ogr_geom, from_espg_code, to_epsg_code):
1236def transform_ogr_geom(a_ogr_geom, from_espg_code, to_epsg_code):
1237    """
1238    Transforma una geometria OGR según los EPSG indicados
1239
1240    Args:
1241        a_ogr_geom (ogr.geometry): una geometria del tipo OGR
1242        from_espg_code (int): codigo numérico del EPSG actual para la geometria
1243        to_epsg_code (int): codigo numérico del EPSG al que se quiere transformar
1244
1245    Returns:
1246        ogr.geometry
1247    """
1248    source = osr.SpatialReference()
1249    source.ImportFromEPSG(from_espg_code)
1250
1251    target = osr.SpatialReference()
1252    target.ImportFromEPSG(to_epsg_code)
1253
1254    a_transform = osr.CoordinateTransformation(source, target)
1255    a_ogr_geom.Transform(a_transform)
1256
1257    return a_ogr_geom

Transforma una geometria OGR según los EPSG indicados

Arguments:
  • a_ogr_geom (ogr.geometry): una geometria del tipo OGR
  • from_espg_code (int): codigo numérico del EPSG actual para la geometria
  • to_epsg_code (int): codigo numérico del EPSG al que se quiere transformar
Returns:

ogr.geometry

def ds_postgis( dbname='POSTGRES', host='localhost', port='5432', user='postgres', password='postgres', schema='public'):
1260def ds_postgis(dbname='POSTGRES', host='localhost', port='5432', user='postgres', password='postgres', schema='public'):
1261    """
1262    Retorna datasource GDAL para ddbb postgis
1263
1264    Args:
1265        dbname:
1266        host:
1267        port:
1268        user:
1269        password:
1270        schema:
1271
1272    Returns:
1273        osgeo.ogr.DataSource
1274    """
1275    pg_conn = f"PG:dbname='{dbname}' host='{host}' port='{port}' user='{user}' password='{password}' active_schema='{schema}'"
1276    drvr, exts = driver_gdal('PostgreSQL')
1277    return drvr.Open(pg_conn, 1)

Retorna datasource GDAL para ddbb postgis

Arguments:
  • dbname:
  • host:
  • port:
  • user:
  • password:
  • schema:
Returns:

osgeo.ogr.DataSource

def reset_sequence_layer_postgis(ds_postgis, nom_layer):
1280def reset_sequence_layer_postgis(ds_postgis, nom_layer):
1281    """
1282    Resetea la secuencia de la layer PostGIS indicada
1283
1284    Args:
1285        ds_postgis (osgeo.ogr.DataSource): datasource de la ddbb PostGIS:
1286        nom_layer (str): nombre de la layer PostGIS
1287
1288    Returns:
1289        bool: True si se ha reseteado correctamente
1290    """
1291    ok = False
1292    layer_dest = ds_postgis.GetLayerByName(nom_layer)
1293    fid_column = layer_dest.GetFIDColumn()
1294    get_seq_sql = f"SELECT pg_get_serial_sequence('{nom_layer}', '{fid_column}') FROM information_schema.columns " \
1295                  f"WHERE table_name = '{nom_layer}' AND column_default LIKE 'nextval%'"
1296
1297    if res_get_seq := ds_postgis.ExecuteSQL(get_seq_sql):
1298        seq_name = res_get_seq.GetNextFeature().GetField(0)
1299        ds_postgis.StartTransaction()
1300        reset_seq = f"SELECT setval('{seq_name}', 1, false);"
1301        ds_postgis.ExecuteSQL(reset_seq)
1302        ds_postgis.CommitTransaction()
1303        ok = True
1304
1305    return ok

Resetea la secuencia de la layer PostGIS indicada

Arguments:
  • ds_postgis (osgeo.ogr.DataSource): datasource de la ddbb PostGIS:
  • nom_layer (str): nombre de la layer PostGIS
Returns:

bool: True si se ha reseteado correctamente