mirror of
https://github.com/Alex38Lyon/Synthese-PSM_LARRA.git
synced 2026-06-01 13:59:13 +00:00
pyThtoQgis
This commit is contained in:
@@ -1,6 +1,14 @@
|
||||
Scripts for Therion
|
||||
====================
|
||||
|
||||
pyThtoQGis
|
||||
---------
|
||||
|
||||
Script to convert (.shp) Therion file in (.qkpg) files for Qgis
|
||||
|
||||
Usage : python pyThtoGgis.py
|
||||
|
||||
|
||||
pyThtoDat
|
||||
---------
|
||||
|
||||
|
||||
@@ -3,6 +3,15 @@ Scripts pour Therion
|
||||
|
||||
🇬🇧 [Read in English](./README.en.md)
|
||||
|
||||
pyThtoQGis
|
||||
---------
|
||||
|
||||
Script pour convertir les fichiers (.shp) produit par Therion en fichiers (.qkpg) pour Qgis
|
||||
|
||||
Usage : python pyThtoGgis.py
|
||||
|
||||
|
||||
|
||||
pyThtoDat
|
||||
---------
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
2026-01-08 10:01:05,741 - INFO - ################################################################################################################################################################
|
||||
2026-01-08 10:01:05,741 - INFO - # Conversion Th, Dat, Mak, Tro, files to Therion files and folders #
|
||||
2026-01-08 10:01:05,741 - INFO - # Script pyCreateTh by : alexandre.pont@yahoo.fr #
|
||||
2026-01-08 10:01:05,742 - INFO - # Version : 2026.01.08 #
|
||||
2026-01-08 10:01:05,744 - INFO - # Input file : . #
|
||||
2026-01-08 10:01:05,744 - INFO - # Output folder : . #
|
||||
2026-01-08 10:01:05,744 - INFO - # Log file : pyCreateTh.log #
|
||||
2026-01-08 10:01:05,744 - INFO - # Config file: config.ini #
|
||||
2026-01-08 10:01:05,744 - INFO - # #
|
||||
2026-01-08 10:01:05,745 - INFO - ################################################################################################################################################################
|
||||
2026-01-08 10:01:05,745 - CRITICAL - !!! No valid file selected
|
||||
2026-01-08 10:02:43,193 - INFO - ######################################################################################################################################################
|
||||
2026-01-08 10:02:43,193 - INFO - # Conversion Th, Dat, Mak, Tro, files to Therion files and folders #
|
||||
2026-01-08 10:02:43,194 - INFO - # Script pyCreateTh by : alexandre.pont@yahoo.fr #
|
||||
2026-01-08 10:02:43,194 - INFO - # Version : 2026.01.08 #
|
||||
2026-01-08 10:02:43,194 - INFO - # Input file : . #
|
||||
2026-01-08 10:02:43,194 - INFO - # Output folder : . #
|
||||
2026-01-08 10:02:43,194 - INFO - # Log file : pyCreateTh.log #
|
||||
2026-01-08 10:02:43,195 - INFO - # Config file: config.ini #
|
||||
2026-01-08 10:02:43,195 - INFO - # #
|
||||
2026-01-08 10:02:43,195 - INFO - ######################################################################################################################################################
|
||||
2026-01-08 10:02:43,195 - CRITICAL - !!! No valid file selected, try again
|
||||
2026-01-08 10:03:35,413 - INFO - ######################################################################################################################################################
|
||||
2026-01-08 10:03:35,413 - INFO - # Conversion th, dat, mak, tro and trox, files to therion files #
|
||||
2026-01-08 10:03:35,414 - INFO - # Script pyCreateTh by : alexandre.pont@yahoo.fr #
|
||||
2026-01-08 10:03:35,414 - INFO - # Version : 2026.01.08 #
|
||||
2026-01-08 10:03:35,414 - INFO - # Input file : . #
|
||||
2026-01-08 10:03:35,414 - INFO - # Output folder : . #
|
||||
2026-01-08 10:03:35,415 - INFO - # Log file : pyCreateTh.log #
|
||||
2026-01-08 10:03:35,415 - INFO - # Config file: config.ini #
|
||||
2026-01-08 10:03:35,415 - INFO - # #
|
||||
2026-01-08 10:03:35,416 - INFO - ######################################################################################################################################################
|
||||
2026-01-08 10:03:35,416 - CRITICAL - !!! No valid file selected, try again
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"cutareas",
|
||||
"ENDC",
|
||||
"esri",
|
||||
"fiona",
|
||||
"ftype",
|
||||
"geodf",
|
||||
"geopandas",
|
||||
"Ggis",
|
||||
"gpkg",
|
||||
"infile",
|
||||
"Masekd",
|
||||
"outputspath",
|
||||
"pathshp",
|
||||
"polygonize",
|
||||
"pygeos",
|
||||
"Qgis",
|
||||
"shapefiles",
|
||||
"thconfig",
|
||||
"therion",
|
||||
"writerecords"
|
||||
]
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
PROJCS["ED_1950_UTM_Zone_30N",
|
||||
GEOGCS["GCS_European_1950",
|
||||
DATUM["D_European_1950",
|
||||
SPHEROID["International_1924",6378388.0,297.0]],
|
||||
PRIMEM["Greenwich",0.0],
|
||||
UNIT["Degree",0.0174532925199433]],
|
||||
PROJECTION["Transverse_Mercator"],
|
||||
PARAMETER["False_Easting",500000.0],
|
||||
PARAMETER["False_Northing",0.0],
|
||||
PARAMETER["Central_Meridian",-3.0],
|
||||
PARAMETER["Scale_Factor",0.9996],
|
||||
PARAMETER["Latitude_Of_Origin",0.0],
|
||||
UNIT["Meter",1.0]]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
PROJCS["ED_1950_UTM_Zone_30N",
|
||||
GEOGCS["GCS_European_1950",
|
||||
DATUM["D_European_1950",
|
||||
SPHEROID["International_1924",6378388.0,297.0]],
|
||||
PRIMEM["Greenwich",0.0],
|
||||
UNIT["Degree",0.0174532925199433]],
|
||||
PROJECTION["Transverse_Mercator"],
|
||||
PARAMETER["False_Easting",500000.0],
|
||||
PARAMETER["False_Northing",0.0],
|
||||
PARAMETER["Central_Meridian",-3.0],
|
||||
PARAMETER["Scale_Factor",0.9996],
|
||||
PARAMETER["Latitude_Of_Origin",0.0],
|
||||
UNIT["Meter",1.0]]
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
ISO-8859-1
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
PROJCS["ED_1950_UTM_Zone_30N",GEOGCS["GCS_European_1950",DATUM["D_European_1950",SPHEROID["International_1924",6378388.0,297.0]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-3.0],PARAMETER["Scale_Factor",0.9996],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
PROJCS["ED_1950_UTM_Zone_30N",
|
||||
GEOGCS["GCS_European_1950",
|
||||
DATUM["D_European_1950",
|
||||
SPHEROID["International_1924",6378388.0,297.0]],
|
||||
PRIMEM["Greenwich",0.0],
|
||||
UNIT["Degree",0.0174532925199433]],
|
||||
PROJECTION["Transverse_Mercator"],
|
||||
PARAMETER["False_Easting",500000.0],
|
||||
PARAMETER["False_Northing",0.0],
|
||||
PARAMETER["Central_Meridian",-3.0],
|
||||
PARAMETER["Scale_Factor",0.9996],
|
||||
PARAMETER["Latitude_Of_Origin",0.0],
|
||||
UNIT["Meter",1.0]]
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
ISO-8859-1
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
PROJCS["ED_1950_UTM_Zone_30N",GEOGCS["GCS_European_1950",DATUM["D_European_1950",SPHEROID["International_1924",6378388.0,297.0]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-3.0],PARAMETER["Scale_Factor",0.9996],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
PROJCS["ED_1950_UTM_Zone_30N",
|
||||
GEOGCS["GCS_European_1950",
|
||||
DATUM["D_European_1950",
|
||||
SPHEROID["International_1924",6378388.0,297.0]],
|
||||
PRIMEM["Greenwich",0.0],
|
||||
UNIT["Degree",0.0174532925199433]],
|
||||
PROJECTION["Transverse_Mercator"],
|
||||
PARAMETER["False_Easting",500000.0],
|
||||
PARAMETER["False_Northing",0.0],
|
||||
PARAMETER["Central_Meridian",-3.0],
|
||||
PARAMETER["Scale_Factor",0.9996],
|
||||
PARAMETER["Latitude_Of_Origin",0.0],
|
||||
UNIT["Meter",1.0]]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
PROJCS["ED_1950_UTM_Zone_30N",
|
||||
GEOGCS["GCS_European_1950",
|
||||
DATUM["D_European_1950",
|
||||
SPHEROID["International_1924",6378388.0,297.0]],
|
||||
PRIMEM["Greenwich",0.0],
|
||||
UNIT["Degree",0.0174532925199433]],
|
||||
PROJECTION["Transverse_Mercator"],
|
||||
PARAMETER["False_Easting",500000.0],
|
||||
PARAMETER["False_Northing",0.0],
|
||||
PARAMETER["Central_Meridian",-3.0],
|
||||
PARAMETER["Scale_Factor",0.9996],
|
||||
PARAMETER["Latitude_Of_Origin",0.0],
|
||||
UNIT["Meter",1.0]]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
PROJCS["ED_1950_UTM_Zone_30N",
|
||||
GEOGCS["GCS_European_1950",
|
||||
DATUM["D_European_1950",
|
||||
SPHEROID["International_1924",6378388.0,297.0]],
|
||||
PRIMEM["Greenwich",0.0],
|
||||
UNIT["Degree",0.0174532925199433]],
|
||||
PROJECTION["Transverse_Mercator"],
|
||||
PARAMETER["False_Easting",500000.0],
|
||||
PARAMETER["False_Northing",0.0],
|
||||
PARAMETER["Central_Meridian",-3.0],
|
||||
PARAMETER["Scale_Factor",0.9996],
|
||||
PARAMETER["Latitude_Of_Origin",0.0],
|
||||
UNIT["Meter",1.0]]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
PROJCS["ED_1950_UTM_Zone_30N",
|
||||
GEOGCS["GCS_European_1950",
|
||||
DATUM["D_European_1950",
|
||||
SPHEROID["International_1924",6378388.0,297.0]],
|
||||
PRIMEM["Greenwich",0.0],
|
||||
UNIT["Degree",0.0174532925199433]],
|
||||
PROJECTION["Transverse_Mercator"],
|
||||
PARAMETER["False_Easting",500000.0],
|
||||
PARAMETER["False_Northing",0.0],
|
||||
PARAMETER["Central_Meridian",-3.0],
|
||||
PARAMETER["Scale_Factor",0.9996],
|
||||
PARAMETER["Latitude_Of_Origin",0.0],
|
||||
UNIT["Meter",1.0]]
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,453 @@
|
||||
######!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020 Xavier Robert <xavier.robert@ird.fr>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
|
||||
"""
|
||||
#############################################################
|
||||
# #
|
||||
# Script to automatize data extraction of Therion databases #
|
||||
# #
|
||||
# By Xavier Robert #
|
||||
# Grenoble, october 2022 #
|
||||
# #
|
||||
#############################################################
|
||||
|
||||
Written by Xavier Robert, octobert 2022
|
||||
|
||||
xavier.robert@ird.fr
|
||||
|
||||
"""
|
||||
|
||||
# Do divisions with Reals, not with integers
|
||||
# Must be at the beginning of the file
|
||||
from __future__ import division
|
||||
|
||||
# Import Python modules
|
||||
#import numpy as np
|
||||
import fiona
|
||||
import shapely
|
||||
from shapely.geometry import Polygon, LineString
|
||||
import geopandas as gpd
|
||||
import pandas as pd
|
||||
import sys, os, copy, shutil
|
||||
#from functools import wraps
|
||||
from alive_progress import alive_bar # https://github.com/rsalmei/alive-progress
|
||||
|
||||
###### TO DO #####
|
||||
|
||||
# -
|
||||
|
||||
##### End TO DO #####
|
||||
|
||||
#################################################################################################
|
||||
#################################################################################################
|
||||
|
||||
#def validate(func):
|
||||
# """
|
||||
# Function to validate areas topology.
|
||||
# From https://shapely.readthedocs.io/en/latest/manual.html
|
||||
|
||||
# Args:
|
||||
# func (_type_): _description_
|
||||
|
||||
# Raises:
|
||||
# TopologicalError: Error of topology
|
||||
# - area does not close
|
||||
# - inner ring
|
||||
# - boundaries intersects
|
||||
|
||||
|
||||
# Returns:
|
||||
# _type_: _description_
|
||||
# """
|
||||
# @wraps(func)
|
||||
# def wrapper(*args, **kwargs):
|
||||
# ob = func(*args, **kwargs)
|
||||
# if not ob.is_valid:
|
||||
# raise TopologicalError(
|
||||
# "Given arguments do not determine a valid geometric object")
|
||||
# return ob
|
||||
# return wrapper
|
||||
|
||||
|
||||
def validate(inputfile, rec):
|
||||
|
||||
rec2 = rec
|
||||
#print(rec['geometry']['coordinates'][0]) # il y a visiblement un soucis avec le nombre de []
|
||||
|
||||
if not Polygon(rec['geometry']['coordinates'][0]).is_valid:
|
||||
print('Problem in %s geometry' %(inputfile))
|
||||
print('%s is not a valid geometric object' %(rec['properties']['_ID']))
|
||||
raise TopologicalError('\033[91mERROR:\033[00m Correction does not work...\n%s is not a valid geometric object\n\t The error is: %s' %(str(rec['properties']['_ID']), shapely.validation.explain_validity(rec)))
|
||||
#print('We try to correct it')
|
||||
#rec2b = shapely.validation.make_valid(Polygon(rec['geometry']['coordinates'][0]))
|
||||
# Check à améliorer, il faut que ce soit un Polygon, et non un MultiPolygon...
|
||||
#if not rec2b.is_valid:
|
||||
# raise TopologicalError('ERROR: Correction failed...\n%s is not a valid geometric object\n\t The error is: %s' %(str(rec['properties']['_ID']), shapely.validation.explain_validity(rec)))
|
||||
#else:
|
||||
# rec2['geometry']['coordinates'][0] = list(rec2b.exterior.coords)
|
||||
|
||||
# Find where there is the error if possible
|
||||
#Diagnostics
|
||||
#validation.explain_validity(ob):
|
||||
#Returns a string explaining the validity or invalidity of the object.
|
||||
#The messages may or may not have a representation of a problem point that can be parsed out.
|
||||
#coords = [(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)]
|
||||
#p = Polygon(coords)
|
||||
#from shapely.validation import explain_validity
|
||||
#shapely.validation.explain_validity(p)
|
||||
#'Ring Self-intersection[1 1]'
|
||||
#shapely.validation.make_valid(ob)
|
||||
#Returns a valid representation of the geometry, if it is invalid. If it is valid, the input geometry will be returned.
|
||||
|
||||
#In many cases, in order to create a valid geometry, the input geometry must be split into multiple parts or multiple geometries. If the geometry must be split into multiple parts of the same geometry type, then a multi-part geometry (e.g. a MultiPolygon) will be returned. if the geometry must be split into multiple parts of different types, then a GeometryCollection will be returned.
|
||||
#For example, this operation on a geometry with a bow-tie structure:
|
||||
#from shapely.validation import make_valid
|
||||
#coords = [(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)]
|
||||
#p = Polygon(coords)
|
||||
#make_valid(p)
|
||||
#<MULTIPOLYGON (((1 1, 0 0, 0 2, 1 1)), ((2 0, 1 1, 2 2, 2 0)))>
|
||||
#Yields a MultiPolygon with two parts, and sometimes area + line:
|
||||
|
||||
return rec2
|
||||
|
||||
#################################################################################################
|
||||
def cutareas(pathshp, outlines, outputspath):
|
||||
"""
|
||||
Function to cut shapefiles areas with the outline to only keep the lines inside the outline
|
||||
|
||||
Args:
|
||||
pathshp (str) : path where are stored output shp from Therion
|
||||
outlines (geopandas obj): the outline shapefile
|
||||
outputspath (str) : path where to copy the gpkg files
|
||||
"""
|
||||
|
||||
print('Working with areas...')
|
||||
# 2- Validate the outline and Areas shapefile
|
||||
#for rec in outlines:
|
||||
# rec2 = validate('outline2d.shp', rec)
|
||||
# # update correction --> To do ?
|
||||
# #if rec2 != rec:
|
||||
#for rec in areas:
|
||||
# rec2 = validate('areas2d.shp', rec)
|
||||
# # update correction
|
||||
# #if rec2 != rec:
|
||||
|
||||
# Read the Line Shapefile
|
||||
areas = gpd.read_file(pathshp + 'areas2d.shp', driver = 'ESRI shapefile')
|
||||
|
||||
# Extract the intersections between outlines and lines
|
||||
# be careful, for this operation, geopandas needs to work with rtree and not pygeos
|
||||
# --> uninstall pygeos and install rtree
|
||||
try:
|
||||
areasIN = areas.overlay(outlines, how = 'intersection')
|
||||
except:
|
||||
print('ERROR: 1) uninstall pygeos and install rtree\n\t2) check your polygons validity')
|
||||
import rtree
|
||||
print ('\tYou may check the validity of your polygons with the verify function in QGIS')
|
||||
areasIN = areas.overlay(outlines, how = 'intersection')
|
||||
|
||||
# Removes inner lines that have different id and scrap_id
|
||||
areasIN = areasIN[areasIN['_SCRAP_ID'] == areasIN ['_ID']]
|
||||
|
||||
# Save output
|
||||
#areasIN.to_file("areas2dMasekd.gpkg", driver = "GPKG", encoding = 'utf8')
|
||||
areasIN.to_file(outputspath + "areas2dMasekd.gpkg", driver = "GPKG")
|
||||
|
||||
return
|
||||
|
||||
#################################################################################################
|
||||
def cutLines(pathshp, outlines, outputspath):
|
||||
"""
|
||||
Function to cut shapefiles lines with the outline to only keep the lines inside the outline
|
||||
|
||||
Args:
|
||||
pathshp (str) : path where are stored output shp from Therion
|
||||
outlines (geopandas obj): the outline shapefile
|
||||
outputspath (str) : path where to copy the gpkg files
|
||||
"""
|
||||
|
||||
print('Working with lines...')
|
||||
# Read the Line Shapefile
|
||||
lines = gpd.read_file(pathshp + 'lines2d.shp', driver = 'ESRI shapefile')
|
||||
# Extract lines that are not masked by the outline
|
||||
linesOUT = pd.concat((lines[lines['_TYPE'] == 'centerline'],
|
||||
lines[lines['_TYPE'] == 'water_flow'],
|
||||
lines[lines['_TYPE'] == 'label'],
|
||||
lines[lines['_CLIP'] == 'off']),
|
||||
ignore_index=True)
|
||||
|
||||
# Extract lines will be masked by the outline
|
||||
linesIN = lines[lines['_CLIP'] != 'off']
|
||||
linesIN = linesIN[linesIN['_TYPE'] != 'centerline']
|
||||
linesIN = linesIN[linesIN['_TYPE'] != 'water_flow']
|
||||
linesIN = linesIN[linesIN['_TYPE'] != 'label']
|
||||
|
||||
# Extract the intersections between outlines and lines
|
||||
# be careful, for this operation, geopandas needs to work with rtree and not pygeos
|
||||
# --> uninstall pygeos and install rtree
|
||||
try:
|
||||
linesIN = linesIN.overlay(outlines, how = 'intersection', keep_geom_type=True)
|
||||
except:
|
||||
print('\033[91mERROR: 1\033[00m) uninstall pygeos and install rtree\n\t2) check your polygons validity')
|
||||
import rtree
|
||||
print ('\tYou may check the validity of your polygons with the verify function in QGIS')
|
||||
linesIN = linesIN.overlay(outlines, how = 'intersection', keep_geom_type=True)
|
||||
print('TEST')
|
||||
|
||||
# Removes inner lines that have different id and scrap_id
|
||||
linesIN = linesIN[linesIN['_SCRAP_ID'] == linesIN ['_ID']]
|
||||
|
||||
# Merge the IN and OUT database
|
||||
linesTOT = pd.concat((linesOUT, linesIN),
|
||||
ignore_index=True)
|
||||
|
||||
# Save output
|
||||
#linesTOT.to_file("lines2dMasekd.gpkg", driver="GPKG", encoding = 'utf8')
|
||||
linesTOT.to_file(outputspath + "lines2dMasekd.gpkg", driver="GPKG")
|
||||
|
||||
return
|
||||
|
||||
#################################################################################################
|
||||
def AddAltPoint(pathshp, outputspath):
|
||||
"""
|
||||
Function to add the altitude of the stations and entrances in the attribut table
|
||||
|
||||
Args:
|
||||
pathshp (str) : path where are stored output shp from Therion
|
||||
outputspath (str): path where to copy the gpkg files
|
||||
"""
|
||||
|
||||
|
||||
print('Working with points...')
|
||||
|
||||
# Definition des altitudes des entrées supérieures des réseaux à plusieurs entrées
|
||||
EntreeSupp = {'JB' : 2333, # Entrée C37
|
||||
'CP' : 2136, # Entrée CP16
|
||||
'LP9' : 2299, # Entrée LP9
|
||||
'CP6' : 2182, # Entrée CP53
|
||||
'CP62' : 1960, # Entrée CP62
|
||||
'A21' : 1797, # Entrée A21
|
||||
'Mirolda': 2330 # Entrée Jockers
|
||||
}
|
||||
# Définition des noms de réseau
|
||||
RNames = {'JB' : 'Gouffre Jean Bernard',
|
||||
'CP' : 'Réseau de la Combe aux Puaires',
|
||||
'LP9' : 'LP9 - CP39',
|
||||
'CP6' : 'CP6 - CP53',
|
||||
'CP62' : 'CP62 - CP63',
|
||||
'A21' : 'A21 -A24',
|
||||
'Mirolda': 'Réseau Lucien-Bouclier - Mirolda'
|
||||
}
|
||||
# Définition des noms de systèmes
|
||||
SNames = {'SynclinalJB' : 'Système du Jean-Bernard',
|
||||
'SystemeCP' : 'Système de la Combe aux Puaires',
|
||||
'SystemeAV' : 'Système des Avoudrues',
|
||||
'SystemeA21' : 'Système du A21',
|
||||
'SystemMirolda' : 'Système du Criou - Mirolda',
|
||||
'SystemeBossetan': 'Système de Bossetan',
|
||||
'sources' : 'Résurgences',
|
||||
'tuet' : 'Système du Tuet',
|
||||
'eauxfroides' : 'Système des Eaux Froides'
|
||||
}
|
||||
# Open the text file with the coordinates of the caves
|
||||
# This text file (Caves.txt) should be build with Therion compilation
|
||||
# and stored in the output's shapefiles folder
|
||||
# export cave-list -location on -o Outputs/SHP/Caves.txt
|
||||
f = open(pathshp + 'Caves.txt', 'r').readlines()
|
||||
|
||||
# Make a new shapefile instance
|
||||
with fiona.open(pathshp + 'points2d.shp', 'r') as inputshp:
|
||||
# Créer le nouveau schéma des shapefiles
|
||||
newschema = inputshp.schema
|
||||
newschema['properties']['_CAVE'] = 'str'
|
||||
newschema['properties']['_SYSTEM'] = 'str'
|
||||
newschema['properties']['_ALT'] = 'str:4'
|
||||
newschema['properties']['_DEPTH'] = 'float'
|
||||
newschema['properties']['_EASTING'] = 'float'
|
||||
newschema['properties']['_NORTHING'] = 'float'
|
||||
# Open the output shapefile
|
||||
#with fiona.open(inputfile[:-4] + 'Alt.shp', 'w', crs=inputshp.crs, driver='ESRI Shapefile', schema=newschema) as ouput:
|
||||
#with fiona.open('points2dAlt.gpkg', 'w', crs=inputshp.crs, driver='GPKG', schema=newschema, encoding = 'utf8') as ouput:
|
||||
with fiona.open(outputspath + 'points2dAlt.gpkg', 'w', crs=inputshp.crs, driver='GPKG', schema=newschema) as ouput:
|
||||
with alive_bar(len(inputshp), title = "\x1b[32;1m- Processing stations...\x1b[0m", length = 20) as bar:
|
||||
# do a loop on the stations
|
||||
for rec in inputshp:
|
||||
# Copy the schema from the input data
|
||||
g = rec
|
||||
g['properties']['_CAVE'] = ''
|
||||
g['properties']['_SYSTEM'] = ''
|
||||
g['properties']['_DEPTH'] = ''
|
||||
|
||||
# Add Alt, Easting, Northing
|
||||
g['properties']['_ALT'] = str(round(float(rec['geometry']['coordinates'][2])))
|
||||
g['properties']['_EASTING'] = float(rec['geometry']['coordinates'][0])
|
||||
g['properties']['_NORTHING'] = float(rec['geometry']['coordinates'][1])
|
||||
|
||||
if rec['properties']['_TYPE'] == 'station' and rec['properties']['_STSURVEY'] != None:
|
||||
# Find system
|
||||
system = rec['properties']['_STSURVEY'].split('.')[-2]
|
||||
g['properties']['_SYSTEM'] = SNames[system]
|
||||
|
||||
# Find Cave
|
||||
xxx = rec['properties']['_STSURVEY'].split('.')
|
||||
while len(xxx) < 4:
|
||||
xxx.append('junk')
|
||||
if 'trous' in xxx[0] or SNames[system] == 'Résurgences' or 'sources' in xxx[0]:
|
||||
g['properties']['_CAVE'] = rec['properties']['_STNAME']
|
||||
g['properties']['_DEPTH'] = 0
|
||||
|
||||
elif 'eauxfroides' in xxx[-3]:
|
||||
g['properties']['_CAVE'] = 'Résurgence des Eaux Froides'
|
||||
g['properties']['_DEPTH'] = 0
|
||||
|
||||
elif 'tuet' in xxx[-4]:
|
||||
g['properties']['_CAVE'] = 'Tuet'
|
||||
g['properties']['_DEPTH'] = 0
|
||||
|
||||
elif 'ReseauCP' in xxx[-4]:
|
||||
g['properties']['_CAVE'] = RNames['CP']
|
||||
g['properties']['_DEPTH'] = EntreeSupp['CP'] - float(rec['geometry']['coordinates'][2])
|
||||
|
||||
elif 'LP9' in xxx[-4]:
|
||||
g['properties']['_CAVE'] = RNames['LP9']
|
||||
g['properties']['_DEPTH'] = EntreeSupp['LP9'] - float(rec['geometry']['coordinates'][2])
|
||||
|
||||
elif 'CP6' in xxx[-4]:
|
||||
g['properties']['_CAVE'] = RNames['CP6']
|
||||
g['properties']['_DEPTH'] = EntreeSupp['CP6'] - float(rec['geometry']['coordinates'][2])
|
||||
|
||||
elif 'CP62' in xxx[-4]:
|
||||
g['properties']['_CAVE'] = RNames['CP62']
|
||||
g['properties']['_DEPTH'] = EntreeSupp['CP62'] - float(rec['geometry']['coordinates'][2])
|
||||
|
||||
elif xxx[-3] == 'Jean-Bernard':
|
||||
#g['properties']['_CAVE'] = rec['properties']['_STSURVEY'].split('.')[-3]
|
||||
g['properties']['_CAVE'] = RNames['JB']
|
||||
g['properties']['_DEPTH'] = EntreeSupp['JB'] - float(rec['geometry']['coordinates'][2])
|
||||
|
||||
elif 'A21' in xxx[-4]:
|
||||
g['properties']['_CAVE'] = RNames['A21']
|
||||
g['properties']['_DEPTH'] = EntreeSupp['A21'] - float(rec['geometry']['coordinates'][2])
|
||||
|
||||
elif 'Mirolda' in xxx[-3]:
|
||||
g['properties']['_CAVE'] = RNames['Mirolda']
|
||||
g['properties']['_DEPTH'] = EntreeSupp['Mirolda'] - float(rec['geometry']['coordinates'][2])
|
||||
|
||||
else:
|
||||
g['properties']['_CAVE'] = xxx[-4]
|
||||
if g['properties']['_CAVE'] == 'A22':
|
||||
g['properties']['_CAVE'] = 'A(V)22'
|
||||
#g['properties']['_DEPTH'] = 0
|
||||
# Trouver l'altitude de l'entrée !!!!
|
||||
for line in f:
|
||||
if g['properties']['_CAVE'] in line and line.split('\t')[6] != '\n':
|
||||
altmax = float(line.split('\t')[6])
|
||||
g['properties']['_DEPTH'] = altmax - float(rec['geometry']['coordinates'][2])
|
||||
|
||||
# Write record
|
||||
ouput.write (g)
|
||||
# Update progress bar
|
||||
bar()
|
||||
return
|
||||
|
||||
#################################################################################################
|
||||
def shp2gpkg(pathshp, outputspath):
|
||||
"""
|
||||
function to convert shp files into gpkg files
|
||||
|
||||
Args:
|
||||
pathshp (str) : path where are stored output shp from Therion
|
||||
outputspath (str): path where to copy the gpkg files
|
||||
"""
|
||||
|
||||
# files to be converted
|
||||
files = ['outline2d', 'points2d']
|
||||
|
||||
print('shp2gpkg : ', files)
|
||||
with alive_bar(len(files), title = "\x1b[32;1m- Processing shp2pkg...\x1b[0m", length = 20) as bar:
|
||||
for fname in files :
|
||||
if fname == 'walls3d':
|
||||
print('shp2gpkg does not support walls3d files...\n\t I am only copying the shp file into the right folder')
|
||||
for ftype in ['.shp', '.dbf', '.prj', '.shx']:
|
||||
shutil.copy2(pathshp + fname + ftype, outputspath + fname + ftype)
|
||||
#pass
|
||||
#input = gpd.read_file(fname + '.shp', layer = 'walls3d', driver = 'ESRI shapefile')
|
||||
#input.to_file(fname + ".gpkg", driver="GPKG", encoding = 'utf8')
|
||||
#with fiona.open(fname + '.shp', 'r') as inputshp:
|
||||
# with fiona.open(fname + '.gpkg', 'w', crs=inputshp.crs, driver='GPKG', schema=inputshp.schema, encoding = 'utf8') as ouput:
|
||||
# for rec in inputshp:
|
||||
# # Write record
|
||||
# ouput.write (g)
|
||||
else:
|
||||
input = gpd.read_file(pathshp + fname + '.shp', driver = 'ESRI shapefile')
|
||||
#input.to_file(fname + ".gpkg", driver="GPKG", encoding = 'utf8')
|
||||
input.to_file(outputspath + fname + ".gpkg", driver="GPKG")
|
||||
#input.to_file(fname + ".gpkg", driver="GPKG")
|
||||
#update bar
|
||||
bar()
|
||||
|
||||
return
|
||||
|
||||
#################################################################################################
|
||||
def ThCutAreas(pathshp, outputspath):
|
||||
|
||||
print(' ')
|
||||
print('****************************************************************')
|
||||
print('Program to cut areas and lines that are intersecting the outline')
|
||||
print(' Written by X. Robert, ISTerre')
|
||||
print(' October 2022 ')
|
||||
print('****************************************************************')
|
||||
print(' ')
|
||||
|
||||
# Check if areas, lines and outline shapefiles exists...
|
||||
areaOK = True
|
||||
for fname in ['outline2d', 'lines2d', 'areas2d', 'points2d']:
|
||||
if not os.path.isfile(pathshp + fname + '.shp'):
|
||||
if fname == 'areas2d':
|
||||
areaOK = False
|
||||
else:
|
||||
print(f'\033[91mERROR:\033[00m File {(str(pathshp + fname + '.shp'))} does not exist')
|
||||
return
|
||||
# Check if Outputs path exists
|
||||
if not os.path.exists(outputspath):
|
||||
print ('\033[91mWARNING:\033[00m ' + outputspath + ' does not exist, I am creating it...')
|
||||
os.mkdir(outputspath)
|
||||
|
||||
#1- Read the outline shapefile
|
||||
outlines = gpd.read_file(pathshp + 'outline2d.shp', driver = 'ESRI shapefile')
|
||||
print('Check')
|
||||
## Change SHP to gpkg
|
||||
shp2gpkg(pathshp, outputspath)
|
||||
## Work with points
|
||||
#AddAltPoint(pathshp, outputspath)
|
||||
## Work with lines
|
||||
cutLines(pathshp, outlines, outputspath)
|
||||
## Work with Areas
|
||||
if areaOK:
|
||||
print ('Cuting areas...')
|
||||
cutareas(pathshp, outlines, outputspath)
|
||||
else:
|
||||
print ("No areas to process...")
|
||||
|
||||
#5- End ?
|
||||
|
||||
print('')
|
||||
print('Update point, areas and lines done.')
|
||||
print('')
|
||||
|
||||
######################################################################################################
|
||||
if __name__ == u'__main__':
|
||||
###################################################
|
||||
# initiate variables
|
||||
#inputfile = 'stations3d.shp'
|
||||
pathshp = './Inputs/'
|
||||
outputspath = './Outputs/'
|
||||
###################################################
|
||||
# Run the transformation
|
||||
ThCutAreas(pathshp, outputspath)
|
||||
# End...
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,239 @@
|
||||
"""
|
||||
!#############################################################################################
|
||||
# #
|
||||
# general_fonctions.py for pythStat.py #
|
||||
# #
|
||||
!#############################################################################################
|
||||
|
||||
Alex 2026 01 09
|
||||
|
||||
"""
|
||||
import os, logging, sys, re, unicodedata
|
||||
from pathlib import Path
|
||||
|
||||
log = logging.getLogger("Logger")
|
||||
|
||||
#################################################################################################
|
||||
# Couleurs ANSI par niveau de log
|
||||
#################################################################################################
|
||||
COLOR_CODES = {
|
||||
logging.DEBUG: "\033[94m", # Bleu
|
||||
logging.INFO: "\033[92m", # Vert
|
||||
logging.WARNING: "\033[95m", # MAGENTA
|
||||
logging.ERROR: "\033[91m", # Rouge
|
||||
logging.CRITICAL: "\033[1;91m", # Rouge vif
|
||||
}
|
||||
RESET = "\033[0m"
|
||||
|
||||
#################################################################################################
|
||||
# Codes de couleur ANSI
|
||||
class Colors:
|
||||
BLACK = '\033[90m'
|
||||
RED = '\033[91m'
|
||||
GREEN = '\033[92m'
|
||||
YELLOW = '\033[93m'
|
||||
BLUE = '\033[94m'
|
||||
MAGENTA = '\033[95m'
|
||||
CYAN = '\033[96m'
|
||||
WHITE = '\033[97m'
|
||||
|
||||
ERROR = '\033[91m'
|
||||
WARNING = '\033[95m'
|
||||
HEADER = '\033[1;94m'
|
||||
DEBUG = '\033[94m' # Bleu
|
||||
INFO = '\033[92m' # Vert
|
||||
CRITICAL = '\033[1;91m', # Rouge vif
|
||||
|
||||
ENDC = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
UNDERLINE = '\033[4m'
|
||||
|
||||
#################################################################################################
|
||||
# Coloration des messages d'aide d'arg #
|
||||
#################################################################################################
|
||||
def colored_help(parser):
|
||||
"""
|
||||
Affiche l'aide colorée pour les arguments de la ligne de commande.
|
||||
|
||||
Args:
|
||||
parser (argparse.ArgumentParser): Le parseur d'arguments.
|
||||
Returns:
|
||||
None
|
||||
|
||||
"""
|
||||
# Captures the help output
|
||||
help_text = parser.format_help()
|
||||
|
||||
# Coloration des différentes parties
|
||||
colored_help_text = help_text.replace(
|
||||
'usage:', f'{Colors.ERROR}usage:{Colors.ENDC}'
|
||||
).replace(
|
||||
'options:', f'{Colors.GREEN}options:{Colors.ENDC}'
|
||||
).replace('positional arguments:', f'{Colors.BLUE}positional arguments:{Colors.ENDC}'
|
||||
).replace(', --help', f'{Colors.BLUE}, --help:{Colors.ENDC}'
|
||||
).replace('elp:', f'{Colors.BLUE}elp{Colors.ENDC}')
|
||||
|
||||
# Surligner les arguments
|
||||
# for action in parser._actions:
|
||||
# if action.option_strings:
|
||||
# # Colorer les options (--xyz)
|
||||
# for opt in action.option_strings:
|
||||
# colored_help_text = colored_help_text.replace(opt, f'{Colors.BLUE}{opt}{Colors.ENDC}').replace('--help', f'{Colors.BLUE}--help:{Colors.ENDC}')
|
||||
|
||||
# Imprimer le texte coloré
|
||||
print(colored_help_text)
|
||||
sys.exit(1)
|
||||
|
||||
#################################################################################################
|
||||
# Mise au format des noms #
|
||||
#################################################################################################
|
||||
def sanitize_filename(thName):
|
||||
"""
|
||||
Cleans a string to make it compatible with filenames on Windows, Linux, and macOS.
|
||||
Replaces special and accented characters with compatible characters.
|
||||
Replaces parentheses with underscores and enforces proper casing.
|
||||
|
||||
Args:
|
||||
thName (str): The filename to clean.
|
||||
|
||||
Returns:
|
||||
str: The cleaned and compatible string.
|
||||
|
||||
"""
|
||||
# Unicode normalization to replace accented characters with their non-accented equivalents
|
||||
thName = unicodedata.normalize('NFKD', thName).encode('ASCII', 'ignore').decode('ASCII')
|
||||
|
||||
# Replace parentheses with underscores
|
||||
thName = thName.replace('(', '_').replace(')', '_')
|
||||
|
||||
# Replace illegal characters with an underscore
|
||||
thName = re.sub(r'[<>:"/\\|?*\']', '_', thName) # Illegal on Windows
|
||||
thName = re.sub(r'\s+', '_', thName) # Spaces to underscores
|
||||
thName = re.sub(r'[^a-zA-Z0-9._-]', '_', thName) # Keep only allowed chars
|
||||
|
||||
# Convert to lowercase, then capitalize the first letter
|
||||
# thName = thName.lower().capitalize()
|
||||
# thName = thName.capitalize()
|
||||
|
||||
# Suppression des underscores en début et fin
|
||||
thName = thName.strip('_')
|
||||
|
||||
return thName or "default_filename" # Avoid empty result
|
||||
|
||||
#################################################################################################
|
||||
# Supprime les codes ANSI (pour l'écriture dans les fichiers)
|
||||
#################################################################################################
|
||||
def strip_ansi_codes(text):
|
||||
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
||||
return ansi_escape.sub('', text)
|
||||
|
||||
#################################################################################################
|
||||
# Formatter pour la console avec couleurs
|
||||
#################################################################################################
|
||||
class ConsoleFormatter(logging.Formatter):
|
||||
def format(self, record):
|
||||
color = COLOR_CODES.get(record.levelno, "")
|
||||
message = super().format(record)
|
||||
return f"{color}{message}{RESET}"
|
||||
|
||||
#################################################################################################
|
||||
# Formatter pour le fichier avec "!!!" sur les erreurs
|
||||
#################################################################################################
|
||||
class FileFormatter(logging.Formatter):
|
||||
def format(self, record):
|
||||
clean_msg = strip_ansi_codes(record.getMessage())
|
||||
prefix = "!!! " if record.levelno >= logging.ERROR else ""
|
||||
record_copy = logging.LogRecord(
|
||||
name=record.name,
|
||||
level=record.levelno,
|
||||
pathname=record.pathname,
|
||||
lineno=record.lineno,
|
||||
msg=f"{prefix}{clean_msg}",
|
||||
args=(),
|
||||
exc_info=record.exc_info,
|
||||
func=record.funcName,
|
||||
sinfo=record.stack_info
|
||||
)
|
||||
return super().format(record_copy)
|
||||
|
||||
#################################################################################################
|
||||
# Fonction de configuration du logger
|
||||
#################################################################################################
|
||||
def setup_logger(logfile="app.log", debug_log=False):
|
||||
logger = logging.getLogger("Logger")
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.handlers.clear()
|
||||
|
||||
min_level = logging.DEBUG if debug_log else logging.INFO
|
||||
|
||||
# Console stderr handler — affichage à l'écran avec couleurs
|
||||
stderr_handler = logging.StreamHandler(sys.stderr)
|
||||
stderr_handler.setLevel(min_level)
|
||||
stderr_formatter = ConsoleFormatter("%(levelname)s: %(message)s") # <-- Ta classe personnalisée
|
||||
stderr_handler.setFormatter(stderr_formatter)
|
||||
logger.addHandler(stderr_handler)
|
||||
|
||||
# File handler — fichier de log
|
||||
file_handler = logging.FileHandler(logfile, encoding="utf-8")
|
||||
file_handler.setLevel(min_level)
|
||||
file_formatter = FileFormatter("%(asctime)s - %(levelname)s - %(message)s") # <-- Ta classe personnalisée
|
||||
file_handler.setFormatter(file_formatter)
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
return logger
|
||||
|
||||
#################################################################################################
|
||||
# fonction pour réduire l'affichage des chemins long #
|
||||
#################################################################################################
|
||||
def safe_relpath(path, base_dir=None, max_depth=3, max_name_len=50, prefix="~"):
|
||||
"""
|
||||
Retourne un chemin lisible et sûr pour affichage (logs / UI).
|
||||
|
||||
- Compatible Windows / Linux / macOS
|
||||
- Tronque la profondeur du chemin
|
||||
- Tronque le nom de fichier si trop long
|
||||
- Ne lève jamais d'exception
|
||||
"""
|
||||
|
||||
try:
|
||||
path = Path(path).expanduser().resolve()
|
||||
except Exception:
|
||||
return str(path)
|
||||
|
||||
try:
|
||||
base = Path(base_dir).expanduser().resolve() if base_dir else Path.cwd().resolve()
|
||||
except Exception:
|
||||
base = None
|
||||
|
||||
name = path.name or str(path)
|
||||
if len(name) > max_name_len:
|
||||
stem = path.stem[: max(1, max_name_len - 6)]
|
||||
name = f"{stem}...{path.suffix}"
|
||||
|
||||
try:
|
||||
if base:
|
||||
rel = path.relative_to(base)
|
||||
parts = list(rel.parts)
|
||||
else:
|
||||
raise ValueError
|
||||
except Exception:
|
||||
parts = list(path.parts)
|
||||
|
||||
if not parts:
|
||||
parts = ["."]
|
||||
|
||||
if isinstance(max_depth, int) and max_depth > 0 and len(parts) > max_depth:
|
||||
parts = parts[-max_depth:]
|
||||
parts.insert(0, prefix)
|
||||
|
||||
if parts and parts[-1] not in (".", os.sep):
|
||||
parts[-1] = name
|
||||
|
||||
try:
|
||||
return os.path.join(*parts)
|
||||
except Exception:
|
||||
return name
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
"""
|
||||
!#############################################################################################!
|
||||
|
||||
global_data.py for pyThtoQgis.py
|
||||
|
||||
!#############################################################################################!
|
||||
"""
|
||||
|
||||
Version = "2026.01.12"
|
||||
|
||||
#################################################################################################
|
||||
|
||||
pathshp = ".\\Inputs\\"
|
||||
outputspath = ".\\Outputs\\"
|
||||
|
||||
# error_count = 0 # Compteur d'erreurs
|
||||
debug_log = False
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"cSpell.words": [
|
||||
"ENDC",
|
||||
"geopandas",
|
||||
"Ggis",
|
||||
"Qgis"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,665 @@
|
||||
######!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020 Xavier Robert <xavier.robert@ird.fr>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
|
||||
"""
|
||||
#############################################################
|
||||
# #
|
||||
# Script to automatize data extraction of Therion databases #
|
||||
# #
|
||||
# By Xavier Robert #
|
||||
# Grenoble, October 2022 #
|
||||
# #
|
||||
#############################################################
|
||||
|
||||
Written by Xavier Robert, October 2022x
|
||||
Xavier.robert@ird.fr
|
||||
|
||||
Modifié Alex 2025 01 31
|
||||
|
||||
Inputs files (16): (.dbf, .prj, .shp, .shx)
|
||||
- points2d
|
||||
- lines2d
|
||||
- areas2d
|
||||
- outlines
|
||||
|
||||
En cas d'erreur corriger manuellement (QGis) les topologies des fichiers
|
||||
|
||||
"""
|
||||
|
||||
# Do divisions with Reals, not with integers
|
||||
# Must be at the beginning of the file
|
||||
from __future__ import division
|
||||
|
||||
# Import Python modules
|
||||
#import numpy as np
|
||||
import sys, os, argparse, shutil
|
||||
import matplotlib.pyplot as plt
|
||||
import tkinter as tk
|
||||
from tkinter import filedialog
|
||||
import fiona
|
||||
from fiona import Env
|
||||
import shapely
|
||||
import geopandas as gpd
|
||||
import pandas as pd
|
||||
from shapely.geometry import Point, MultiPoint, Polygon, LineString, MultiLineString, MultiPolygon, Polygon
|
||||
from shapely.geometry import shape, mapping, GeometryCollection
|
||||
from shapely.ops import transform, unary_union, polygonize
|
||||
from shapely.errors import TopologicalError
|
||||
from shapely.validation import make_valid, explain_validity
|
||||
from collections import Counter
|
||||
|
||||
#from functools import wraps
|
||||
from alive_progress import alive_bar # https://github.com/rsalmei/alive-progress
|
||||
|
||||
import Lib.global_data as globalDat
|
||||
from Lib.general_fonctions import setup_logger, Colors, safe_relpath, colored_help
|
||||
|
||||
|
||||
|
||||
#################################################################################################
|
||||
def cutareas(pathshp, outlines, outputspath):
|
||||
"""
|
||||
Function to cut shapefiles areas with the outline to only keep the lines inside the outline
|
||||
|
||||
Args:
|
||||
pathshp (str) : path where are stored output shp from Therion
|
||||
outlines (geopandas obj): the outline shapefile
|
||||
outputspath (str) : path where to copy the gpkg files
|
||||
"""
|
||||
|
||||
print(f'{Colors.GREEN}Working with :{Colors.ENDC} areas2d.gpkg')
|
||||
# 2- Validate the outline and Areas shapefile
|
||||
#for rec in outlines:
|
||||
# rec2 = validate('outline2d.shp', rec)
|
||||
# # update correction --> To do ?
|
||||
# #if rec2 != rec:
|
||||
#for rec in areas:
|
||||
# rec2 = validate('areas2d.shp', rec)
|
||||
# # update correction
|
||||
# #if rec2 != rec:
|
||||
|
||||
# Read the Line Shapefile
|
||||
# areas = gpd.read_file(pathshp + 'areas2d.shp', driver = 'ESRI shapefile')
|
||||
areas = gpd.read_file(pathshp + 'areas2d.gpkg')
|
||||
|
||||
# Corriger les erreurs de topologie dans les lignes avant traitement
|
||||
# areas = fix_topology(areas)
|
||||
|
||||
# Extract the intersections between outlines and lines
|
||||
# be careful, for this operation, geopandas needs to work with rtree and not pygeos
|
||||
# --> uninstall pygeos and install rtree
|
||||
try:
|
||||
areasIN = areas.overlay(outlines, how = 'intersection')
|
||||
except:
|
||||
print('ERROR: 1) uninstall pygeos and install rtree\n\t2) check your polygons validity')
|
||||
import rtree
|
||||
print ('\tYou may check the validity of your polygons with the verify function in QGIS')
|
||||
areasIN = areas.overlay(outlines, how = 'intersection')
|
||||
|
||||
# Removes inner lines that have different id and scrap_id
|
||||
areasIN = areasIN[areasIN['_SCRAP_ID'] == areasIN ['_ID']]
|
||||
|
||||
# Save output
|
||||
#areasIN.to_file("areas2dMasekd.gpkg", driver = "GPKG", encoding = 'utf8')
|
||||
areasIN.to_file(outputspath + "areas2dMasekd.gpkg", driver = "GPKG")
|
||||
|
||||
return
|
||||
|
||||
#################################################################################################
|
||||
def cutLines(pathshp, outlines, outputspath):
|
||||
"""
|
||||
Function to cut shapefiles lines with the outline to only keep the lines inside the outline
|
||||
|
||||
Args:
|
||||
pathshp (str) : path where are stored output shp from Therion
|
||||
outlines (geopandas obj): the outline shapefile
|
||||
outputspath (str) : path where to copy the gpkg files
|
||||
"""
|
||||
|
||||
print(f'{Colors.GREEN}Working with :{Colors.ENDC} lines2d.gpkg')
|
||||
# Read the Line Shapefile
|
||||
lines = gpd.read_file(pathshp + 'lines2d.gpkg')
|
||||
# lines = fix_topology(lines)
|
||||
|
||||
# Vérifier si outlines est un GeoDataFrame
|
||||
if not isinstance(outlines, gpd.GeoDataFrame):
|
||||
print(f"{Colors.GREEN}Outlines is not a GeoDataFrame. Attempting conversion...")
|
||||
outlines = gpd.read_file(outlines) # Lire un fichier shapefile si outlines est une chaine de caractères
|
||||
|
||||
# Extract lines that are not masked by the outline
|
||||
linesOUT = pd.concat((lines[lines['_TYPE'] == 'centerline'],
|
||||
lines[lines['_TYPE'] == 'water_flow'],
|
||||
lines[lines['_TYPE'] == 'label'],
|
||||
lines[lines['_CLIP'] == 'off']),
|
||||
ignore_index=True)
|
||||
|
||||
# Extract lines will be masked by the outline
|
||||
linesIN = lines[lines['_CLIP'] != 'off']
|
||||
linesIN = linesIN[linesIN['_TYPE'] != 'centerline']
|
||||
linesIN = linesIN[linesIN['_TYPE'] != 'water_flow']
|
||||
linesIN = linesIN[linesIN['_TYPE'] != 'label']
|
||||
|
||||
# Extract the intersections between outlines and lines
|
||||
# be careful, for this operation, geopandas needs to work with rtree and not pygeos
|
||||
# --> uninstall pygeos and install rtree
|
||||
try:
|
||||
# outlines = outlines.buffer(0) # [Note Alex] Réparer les géométries invalides
|
||||
linesIN = linesIN.overlay(outlines, how = 'intersection', keep_geom_type=True)
|
||||
except:
|
||||
print(f"{Colors.ERROR}ERROR: uninstall pygeos and install rtree\n\t2) check your polygons validity")
|
||||
print (f"{Colors.ERROR}You may check the validity of your polygons with the verify function in QGIS")
|
||||
linesIN = linesIN.overlay(outlines, how = 'intersection', keep_geom_type=True)
|
||||
|
||||
|
||||
# Removes inner lines that have different id and scrap_id
|
||||
linesIN = linesIN[linesIN['_SCRAP_ID'] == linesIN ['_ID']]
|
||||
|
||||
# Merge the IN and OUT database
|
||||
linesTOT = pd.concat((linesOUT, linesIN),ignore_index=True)
|
||||
|
||||
# Save output
|
||||
linesTOT.to_file(outputspath + "lines2dMasekd.gpkg", driver="GPKG")
|
||||
|
||||
return
|
||||
|
||||
#################################################################################################
|
||||
def shp2gpkg(pathshp, infile, outputspath, outfile ):
|
||||
"""
|
||||
Function to convert shp files into gpkg files using Fiona.
|
||||
|
||||
Args:
|
||||
pathshp (str): Path where the input shp files are stored.
|
||||
infile (str): Name of the file to be converted (without extension).
|
||||
outputspath (str): Path where the output gpkg files will be saved.
|
||||
outfile (str): Name of the output file (without extension).
|
||||
"""
|
||||
try:
|
||||
# Configuration de l'environnement Fiona pour accepter les géométries non fermées
|
||||
with Env(OGR_GEOMETRY_ACCEPT_UNCLOSED_RING="YES"):
|
||||
|
||||
input_shp = os.path.join(pathshp, infile + '.shp')
|
||||
output_gpkg = os.path.join(outputspath, outfile + '.gpkg')
|
||||
|
||||
# Vérification que le fichier source existe
|
||||
if not os.path.exists(input_shp):
|
||||
raise FileNotFoundError(f"\t{Colors.ERROR}Error (shp2gpkg): the file {Colors.ENDC}{input_shp}{Colors.ERROR} did not exist.")
|
||||
|
||||
|
||||
# Lecture du fichier Shapefile
|
||||
with fiona.open(input_shp, 'r', encoding='utf-8') as source:
|
||||
|
||||
geom_types = Counter() # pour compter le nombre de chaque type
|
||||
has_z = set()
|
||||
|
||||
for feat in source:
|
||||
geom_type = feat["geometry"]["type"]
|
||||
geom_types[geom_type] += 1
|
||||
|
||||
coords = feat["geometry"]["coordinates"]
|
||||
|
||||
# Vérifie si la géométrie a un Z
|
||||
def check_z(coords):
|
||||
"""
|
||||
Détecte la présence d'une coordonnée Z
|
||||
quelle que soit la profondeur de la géométrie
|
||||
"""
|
||||
if isinstance(coords, (list, tuple)):
|
||||
# Cas Point : (x,y) ou (x,y,z)
|
||||
if len(coords) in (2, 3) and all(isinstance(c, (int, float)) for c in coords):
|
||||
return len(coords) == 3
|
||||
|
||||
# Cas Line / Polygon / Multi*
|
||||
for c in coords:
|
||||
if check_z(c):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
if check_z(coords):
|
||||
has_z.add(True)
|
||||
else:
|
||||
has_z.add(False)
|
||||
|
||||
print(
|
||||
f"{Colors.GREEN}File conversion to GPKG: {Colors.ENDC}{input_shp}"
|
||||
f"{Colors.GREEN}, geometries found: {Colors.ENDC}{dict(geom_types)}"
|
||||
f"{Colors.GREEN}, with altitude: {Colors.ENDC}{has_z}"
|
||||
)
|
||||
|
||||
|
||||
# Affichage du nombre d'objets et de leur type
|
||||
num_features = len(source)
|
||||
geometry_type = source.schema['geometry']
|
||||
|
||||
# Vérification que le driver GPKG est disponible
|
||||
if 'GPKG' not in fiona.supported_drivers:
|
||||
raise RuntimeError(f"{Colors.ERROR}Error: The GPKG driver is not supported by Fiona{Colors.ENDC}")
|
||||
|
||||
# Création du fichier GeoPackage
|
||||
with fiona.open( output_gpkg, 'w', driver='GPKG', schema=source.schema, crs=source.crs, encoding='utf-8') as destination:
|
||||
for feature in source:
|
||||
destination.write(feature)
|
||||
|
||||
print(f'{Colors.GREEN}Conversion to GPKG OK, {Colors.GREEN} file : {Colors.ENDC}{pathshp}{infile}.shp{Colors.GREEN} to : {Colors.ENDC}{outputspath}{outfile}.gpkg, type {geometry_type} : {num_features}')
|
||||
|
||||
|
||||
except FileNotFoundError as e:
|
||||
print(f"{Colors.ERROR}Error (shp2gpkg): {Colors.ENDC}{e}", file=sys.stderr)
|
||||
|
||||
except RuntimeError as e:
|
||||
print(f"{Colors.ERROR}Error (shp2gpkg): {Colors.ENDC}{e}", file=sys.stderr)
|
||||
|
||||
except fiona.errors.FionaError as e:
|
||||
print(f"{Colors.ERROR}Error read/write file (shp2gpkg): {Colors.ENDC}{e}", file=sys.stderr)
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Colors.ERROR}Error unknown (shp2gpkg): {Colors.ENDC}{e}", file=sys.stderr)
|
||||
|
||||
#################################################################################################
|
||||
def poly_rock(infile, outfile):
|
||||
"""
|
||||
Converts line features from the input GeoPackage file into closed polygons,
|
||||
and saves the valid ones to an existing layer in the output GeoPackage.
|
||||
Only features with the attribute _Type equal to 'rock-edge' or 'rock-border' are considered.
|
||||
"""
|
||||
|
||||
# Load the input layer (assumed to be a line layer)
|
||||
gdf = gpd.read_file(infile, encoding='utf-8') # Adjust layer name if needed
|
||||
|
||||
# Filter features based on _Type attribute
|
||||
gdf = gdf[gdf['_TYPE'].isin(['rock-edge', 'rock-border'])]
|
||||
|
||||
# Attempt to convert LineStrings to Polygons
|
||||
polygons = []
|
||||
for _, row in gdf.iterrows():
|
||||
geom = row.geometry
|
||||
if isinstance(geom, LineString) and geom.is_ring:
|
||||
new_row = row.copy()
|
||||
new_row['geometry'] = Polygon(geom)
|
||||
polygons.append(new_row)
|
||||
|
||||
# Create a GeoDataFrame from the valid polygons
|
||||
if polygons:
|
||||
poly_gdf = gpd.GeoDataFrame(polygons, geometry='geometry', crs=gdf.crs)
|
||||
|
||||
# Load existing output data if it exists
|
||||
try:
|
||||
existing_gdf = gpd.read_file(outfile, encoding='utf-8')
|
||||
poly_gdf = gpd.pd.concat([existing_gdf, poly_gdf], ignore_index=True)
|
||||
except Exception:
|
||||
pass # If file doesn't exist, create a new one
|
||||
|
||||
# Save updated data to the output GeoPackage
|
||||
poly_gdf.to_file(outfile, driver='GPKG', encoding='utf-8') # Adjust layer name if needed
|
||||
|
||||
print(f"{Colors.GREEN}Added {Colors.ENDC}{len(polygons)} {Colors.GREEN}polygons to {Colors.ENDC}{outfile}.")
|
||||
else:
|
||||
print(f"{Colors.ERROR}No valid closed polylines found.")
|
||||
|
||||
#################################################################################################
|
||||
def count_topology_errors(file_path):
|
||||
"""
|
||||
Analyse un shapefile pour détecter les erreurs topologiques et compte les occurrences par type.
|
||||
|
||||
Args:
|
||||
file_path (str): Chemin vers le shapefile à analyser.
|
||||
|
||||
Returns:
|
||||
tuple:
|
||||
- dict: clé = type d'erreur, valeur = liste des indices de records concernés
|
||||
- int: nombre total d'erreurs détectées
|
||||
"""
|
||||
error_details = {}
|
||||
record_types = {}
|
||||
total_records = 0
|
||||
total_errors = 0
|
||||
try:
|
||||
if not os.path.exists(file_path):
|
||||
print(f"{Colors.ERROR}File not found: {Colors.ENDC}{file_path}")
|
||||
return {}, -1
|
||||
|
||||
with fiona.open(file_path, "r") as src:
|
||||
for i, record in enumerate(src):
|
||||
total_records += 1
|
||||
|
||||
# Vérifier si la géométrie est présente
|
||||
if record is None or record.get('geometry') is None:
|
||||
props = record.get('properties', {})
|
||||
print(f"{Colors.ERROR}Error in file {Colors.ENDC}{safe_relpath(file_path)}{Colors.ERROR}, record {Colors.ENDC}{i+1}{Colors.ERROR} has no geometry, correct it : "
|
||||
f"_ID: {Colors.ENDC}{props.get('_ID')}{Colors.ERROR}, _NAME: {Colors.ENDC}{props.get('_NAME')}{Colors.ERROR}, _SURVEY: {Colors.ENDC}{props.get('_SURVEY')}{Colors.ENDC}")
|
||||
continue
|
||||
|
||||
# Créer l'objet Shapely
|
||||
try:
|
||||
geometry = shape(record['geometry'])
|
||||
|
||||
except Exception as e:
|
||||
props = record.get('properties', {})
|
||||
print(f"{Colors.ERROR}Error in file {Colors.ENDC}{safe_relpath(file_path)}{Colors.ERROR},Cannot create shape for record {Colors.ENDC}{i+1}{Colors.ERROR}: {Colors.ENDC}{e}{Colors.ERROR}"
|
||||
f"_ID: {Colors.ENDC}{props.get('_ID')}{Colors.ERROR}, _NAME: {Colors.ENDC}{props.get('_NAME')}{Colors.ERROR}, _SURVEY: {Colors.ENDC}{props.get('_SURVEY')}{Colors.ENDC}")
|
||||
continue
|
||||
|
||||
# Ignorer les géométries vides
|
||||
if geometry.is_empty:
|
||||
print(f"{Colors.WARNING}Warning, file {Colors.ENDC}{safe_relpath(file_path)}{Colors.WARNING},Record {i+1} has empty geometry. Skipping.{Colors.ENDC}")
|
||||
continue
|
||||
|
||||
# Comptage des types de géométrie
|
||||
geom_type = geometry.geom_type
|
||||
record_types[geom_type] = record_types.get(geom_type, 0) + 1
|
||||
|
||||
# Vérifier la validité topologique
|
||||
try:
|
||||
validity_explanation = explain_validity(geometry)
|
||||
|
||||
if validity_explanation != "Valid Geometry":
|
||||
total_errors += 1
|
||||
# Conserver l'explication complète comme type d'erreur
|
||||
error_details.setdefault(validity_explanation, []).append(i)
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Colors.ERROR}Error in file {Colors.ENDC}{safe_relpath(file_path)}{Colors.ERROR}, validating geometry for record {Colors.ENDC}{i+1}{Colors.ERROR}: {Colors.ENDC}{e}{Colors.ENDC}")
|
||||
|
||||
|
||||
print(f"{Colors.GREEN}Geometry num: {Colors.ENDC}{i+1}{Colors.YELLOW}, types found: {Colors.ENDC}{record_types}")
|
||||
|
||||
# Affichage du résumé
|
||||
if total_errors == 0:
|
||||
print(f"{Colors.GREEN}File error check OK: {Colors.ENDC}{safe_relpath(file_path)}{Colors.GREEN}, "
|
||||
f"records: {Colors.ENDC}{total_records}{Colors.GREEN}, no errors found")
|
||||
|
||||
else:
|
||||
print(f"{Colors.ERROR}File error check NOK: {Colors.ENDC}{safe_relpath(file_path)}{Colors.ERROR}, "
|
||||
f"records: {Colors.ENDC}{total_records}{Colors.ERROR}, total errors: {Colors.ENDC}{total_errors}")
|
||||
|
||||
# for err_type, indices in error_details.items():
|
||||
# print(f"{Colors.ERROR}Detail error in file {Colors.ENDC}{safe_relpath(file_path)}{Colors.ERROR}, {Colors.ENDC}{err_type} "
|
||||
# f"{Colors.ERROR}occurrences: {Colors.ENDC}{len(indices)}{Colors.ENDC}")
|
||||
|
||||
# Optionnel : afficher le détail des types de géométrie
|
||||
print(f"{Colors.GREEN}Geometry in file: {Colors.ENDC}{safe_relpath(file_path)}{Colors.GREEN}, types found: {Colors.ENDC}{record_types}")
|
||||
|
||||
return error_details, total_errors
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Colors.ERROR}Topology error when analyzing the shapefile: {Colors.ENDC}"
|
||||
f"{safe_relpath(file_path)}{Colors.ERROR}, code: {Colors.ENDC}{e}")
|
||||
return {}, -1
|
||||
|
||||
#################################################################################################
|
||||
def fix_geometries(input_shp, output_shp):
|
||||
"""
|
||||
Fixes geometry errors in a Shapefile and saves only objects of the same type as the source file.
|
||||
Displays a summary of the modifications and a report of geometries by type before and after processing.
|
||||
|
||||
:param input_shp: Path to the input Shapefile
|
||||
:param output_shp: Path to the output Shapefile
|
||||
"""
|
||||
|
||||
try :
|
||||
with fiona.open(input_shp, 'r') as src:
|
||||
meta = src.meta # File metadata
|
||||
original_geom_type = meta['schema']['geometry'].upper() # Expected geometry type (in uppercase to avoid format discrepancies)
|
||||
original_geom_type_simple = original_geom_type.replace('3D ', '') # Remove '3D ' prefix
|
||||
fixed_features = []
|
||||
geom_counts_before = Counter()
|
||||
geom_counts_after = Counter()
|
||||
modifications = 0
|
||||
error_details = {}
|
||||
corrected = 0
|
||||
i = 0
|
||||
|
||||
for feature in src:
|
||||
fixed_feature = dict(feature) # Copy the feature
|
||||
geom = shape(feature['geometry'])
|
||||
geom_type = geom.geom_type.upper()
|
||||
geom_counts_before[geom_type] += 1
|
||||
|
||||
# Fix the geometry
|
||||
valid_geom = make_valid(geom)
|
||||
|
||||
geom_type_fixed = valid_geom.geom_type.upper()
|
||||
|
||||
geom_counts_after[geom_type_fixed] += 1
|
||||
|
||||
# Check if the fixed geometry is of the same type as the original
|
||||
if geom_type_fixed == original_geom_type or geom_type_fixed == original_geom_type_simple:
|
||||
fixed_feature['geometry'] = mapping(valid_geom)
|
||||
fixed_features.append(fixed_feature)
|
||||
|
||||
else:
|
||||
modifications += 1
|
||||
|
||||
try:
|
||||
# Validate the geometry and explain any issues
|
||||
validity_explanation = explain_validity(geom)
|
||||
|
||||
if validity_explanation != "Valid Geometry":
|
||||
# Extract the type of error
|
||||
error_type = validity_explanation.split(" ")[0] # First word of the explanation
|
||||
|
||||
# Add the record index to the error details
|
||||
if error_type in error_details:
|
||||
error_details[error_type].append(i)
|
||||
|
||||
else:
|
||||
error_details[error_type] = [i]
|
||||
|
||||
if error_type=="Too" : corrected += 1
|
||||
|
||||
i += 1
|
||||
|
||||
else :
|
||||
props = feature.get('properties', {})
|
||||
print(f"{Colors.ERROR}Error in file {Colors.ENDC}{safe_relpath(input_shp)}{Colors.ERROR},correct it manually, "
|
||||
f"_ID: {Colors.ENDC}{props.get('_ID')}{Colors.ERROR}, _NAME: {Colors.ENDC}{props.get('_NAME')}{Colors.ERROR}, _SURVEY: {Colors.ENDC}{props.get('_SURVEY')}{Colors.ENDC}")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Colors.ERROR}Error processing record {Colors.ENDC}: {e}")
|
||||
|
||||
|
||||
# Write the output file with only geometries of the original type
|
||||
if fixed_features:
|
||||
with fiona.open(output_shp, 'w', **meta) as dst:
|
||||
dst.writerecords(fixed_features)
|
||||
|
||||
# print(f"{Colors.GREEN}Correction completed{Colors.GREEN}, file {Colors.ENDC}{input_shp}{Colors.GREEN} saved as: {Colors.ENDC}{output_shp}")
|
||||
|
||||
if error_details:
|
||||
for error_type, indices in error_details.items():
|
||||
if error_type=="Too" :
|
||||
print(f"{Colors.GREEN}Correction completed{Colors.GREEN}, file {Colors.ENDC}{input_shp}{Colors.GREEN} saved as: {Colors.ENDC}{output_shp}{Colors.GREEN} Erreur type : {Colors.ENDC}{error_type} : {len(indices)}{Colors.GREEN} occurrences corrected{Colors.ENDC}")
|
||||
|
||||
else :
|
||||
print(f"{Colors.WARNING}Correction issue{Colors.ERROR}, file {Colors.ENDC}{input_shp}{Colors.ERROR} saved as: {Colors.ENDC}{output_shp}{Colors.ERROR} Erreur type : {Colors.ENDC}{error_type} : {len(indices)}{Colors.ERROR} occurrences{Colors.ENDC}")
|
||||
|
||||
else:
|
||||
print(f"{Colors.ERROR}Error: No valid features found in {Colors.ENDC}{input_shp}.{Colors.ERROR}No file generated.{Colors.ENDC}")
|
||||
|
||||
# Display the summary
|
||||
if modifications !=0 : print(f"{Colors.WARNING}Total number of geometries ignored: {Colors.ENDC}{modifications}")
|
||||
print(f"{Colors.INFO}Total number of geometries corrected : {Colors.ENDC}{corrected}")
|
||||
print(f"{Colors.INFO}Before correction: {Colors.ENDC}{dict(geom_counts_before)}")
|
||||
print(f"{Colors.INFO}After correction: {Colors.ENDC}{dict(geom_counts_after)}")
|
||||
|
||||
|
||||
# Return the number of modifications
|
||||
return modifications - corrected
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Colors.ERROR}Fix geometry error in the shapefile: {Colors.ENDC}"
|
||||
f"{safe_relpath(input_shp)}{Colors.ERROR}, code: {Colors.ENDC}{e}")
|
||||
return -1
|
||||
|
||||
#################################################################################################
|
||||
def ThtoQGis(pathshp, outputspath):
|
||||
|
||||
# Check if areas, lines, points2d and outline shapefiles exists...
|
||||
|
||||
# Check if Outputs path exists
|
||||
if not os.path.exists(outputspath):
|
||||
print (f"{Colors.WARNING}WARNING: {Colors.ENDC}{safe_relpath(outputspath)}{Colors.WARNING} does not exist, I am creating it...")
|
||||
os.mkdir(outputspath)
|
||||
|
||||
modifications = 0
|
||||
if os.path.isfile(pathshp + 'areas2d.shp') :
|
||||
file_list = ['outline2d', 'lines2d', 'areas2d', 'points2d']
|
||||
areaOK = True
|
||||
|
||||
else :
|
||||
file_list = ['outline2d', 'lines2d', 'points2d']
|
||||
areaOK = False
|
||||
|
||||
print(f"{Colors.HEADER}{Colors.UNDERLINE}Step 1: Test files and convert to GPKG format in the folder:{Colors.ENDC} {safe_relpath(outputspath)}")
|
||||
|
||||
for fname in file_list:
|
||||
print(f"{Colors.HEADER}Working with file: {Colors.ENDC}{fname}.shp")
|
||||
|
||||
if not os.path.isfile(pathshp + fname + '.shp'):
|
||||
if fname == 'areas2d':
|
||||
areaOK = False
|
||||
|
||||
else:
|
||||
print(f"{Colors.ERROR}ERROR the file {Colors.ENDC}{(str(pathshp + fname + '.shp'))}{Colors.ERROR} does not exist'{Colors.ENDC}")
|
||||
return False
|
||||
|
||||
err = count_topology_errors(pathshp + fname + '.shp')
|
||||
|
||||
if err[1] == -1 : return False
|
||||
|
||||
if err[1] != 0 :
|
||||
modifications += fix_geometries(pathshp + fname + '.shp', pathshp + fname + '_fixed.shp')
|
||||
|
||||
err2 = count_topology_errors(pathshp + fname + '_fixed.shp')
|
||||
|
||||
if err2[1] == -1 : return False
|
||||
|
||||
if err2[1] == 0 : shp2gpkg(pathshp, fname + "_fixed", outputspath, fname)
|
||||
|
||||
else :
|
||||
print(f'{Colors.ERROR}ERROR: in file {Colors.ENDC}{(str(pathshp + fname + '.shp'))} {Colors.ERROR} please fix it manually with QGis... {Colors.ENDC}')
|
||||
return False
|
||||
|
||||
else :
|
||||
shp2gpkg(pathshp, fname, outputspath, fname)
|
||||
|
||||
|
||||
print(f"{Colors.HEADER}{Colors.UNDERLINE}Step 2: Adapte files for Qgis in the folder:{Colors.ENDC} {safe_relpath(outputspath)}")
|
||||
#1- Read the outline shapefile
|
||||
|
||||
outlines = gpd.read_file(outputspath + 'outline2d.gpkg')
|
||||
|
||||
## Work with lines
|
||||
cutLines(outputspath, outlines, outputspath)
|
||||
|
||||
## Work with Areas
|
||||
|
||||
if areaOK == True :
|
||||
cutareas(outputspath, outlines, outputspath)
|
||||
poly_rock(outputspath + "lines2d.gpkg", outputspath + "areas2dMasekd.gpkg")
|
||||
os.remove(outputspath + "areas2d.gpkg")
|
||||
os.remove(outputspath + "lines2d.gpkg")
|
||||
|
||||
if modifications == 0 :
|
||||
print(f'{Colors.GREEN}Update point, areas and lines done without error {Colors.ENDC}')
|
||||
|
||||
else :
|
||||
print(f'{Colors.GREEN}Update point, areas and lines done with warning {Colors.ENDC}{modifications}{Colors.GREEN} to be checked{Colors.ENDC}')
|
||||
|
||||
else :
|
||||
os.remove(outputspath + "lines2d.gpkg")
|
||||
|
||||
if modifications == 0 :
|
||||
print(f'{Colors.HEADER}Update point and lines done without error {Colors.ENDC}')
|
||||
|
||||
else :
|
||||
print(f'{Colors.GREEN}Update point and lines done with warning {Colors.ENDC}{modifications}{Colors.GREEN} to be checked{Colors.ENDC}')
|
||||
|
||||
|
||||
|
||||
|
||||
#####################################################################################################################################
|
||||
# #
|
||||
# Main #
|
||||
# #
|
||||
#####################################################################################################################################
|
||||
if __name__ == u'__main__':
|
||||
###################################################
|
||||
|
||||
|
||||
|
||||
#################################################################################################
|
||||
# Parse arguments #
|
||||
#################################################################################################
|
||||
parser = argparse.ArgumentParser(
|
||||
description=f"{Colors.HEADER}Script to generate QGis (.gpkg) files from Therion (.shp) files with auto-correction if possible",
|
||||
formatter_class=argparse.RawTextHelpFormatter)
|
||||
parser.print_help = colored_help.__get__(parser)
|
||||
parser.add_argument(
|
||||
'--option',
|
||||
default="auto",
|
||||
choices=["auto", "manual", "test"],
|
||||
help=(
|
||||
f"Execution options for pyThtoQgis.py\n"
|
||||
f"auto\t-> Execution from the folder {globalDat.pathshp} (défaut)\n"
|
||||
f"manual\t-> Manual selection for the input folder\n"
|
||||
f"test\t-> Tests fonction (debug)\n"
|
||||
)
|
||||
)
|
||||
|
||||
parser.epilog = (
|
||||
f"{Colors.HEADER}to generate shp files with therion, add in .thconfig : "
|
||||
f"-> {Colors.ENDC}export model -fmt esri -o Outputs/SHP/ -enc UTF-8"
|
||||
)
|
||||
|
||||
# Analyser les arguments de ligne de commande
|
||||
args = parser.parse_args()
|
||||
|
||||
if os.name == 'posix': os.system('clear') # Linux, MacOS
|
||||
elif os.name == 'nt': os.system('cls')# Windows
|
||||
else: print("\n" * 100)
|
||||
|
||||
print(f'{Colors.HEADER}*********************************************************************************************************')
|
||||
print(f'{Colors.HEADER}Script to generate QGis (.gpkg) files from Therion (.shp) files with auto-correction if possible')
|
||||
print(f'{Colors.HEADER} Original written by X. Robert, ISTerre : {Colors.ENDC}October 2022')
|
||||
print(f'{Colors.HEADER} Updated by : {Colors.ENDC}alexandre.pont@yahoo.fr')
|
||||
print(f'{Colors.HEADER} Version : {Colors.ENDC}{globalDat.Version}')
|
||||
|
||||
|
||||
if args.option == "auto" :
|
||||
|
||||
print(f'{Colors.HEADER} input folder : {Colors.ENDC}{globalDat.pathshp}')
|
||||
print(f'{Colors.HEADER} output folder : {Colors.ENDC}{globalDat.outputspath}')
|
||||
print(f'{Colors.HEADER}*********************************************************************************************************')
|
||||
|
||||
ThtoQGis(globalDat.pathshp, globalDat.outputspath)
|
||||
|
||||
|
||||
elif args.option == "manual" :
|
||||
root = tk.Tk()
|
||||
root.withdraw() # Cacher la fenêtre principale de Tkinter
|
||||
input_folder_name = filedialog.askdirectory( title="Choose the shp folder")
|
||||
|
||||
if not input_folder_name:
|
||||
print(f"{Colors.ERROR}No folder selected. The program will terminate")
|
||||
sys.exit()
|
||||
|
||||
|
||||
input_folder = input_folder_name + "\\"
|
||||
|
||||
print(f'{Colors.HEADER} input folder : {Colors.ENDC}{safe_relpath(input_folder)}')
|
||||
print(f'{Colors.HEADER} output folder : {Colors.ENDC}{globalDat.outputspath}')
|
||||
print(f'{Colors.HEADER}****************************************************************')
|
||||
|
||||
ThtoQGis(input_folder, globalDat.outputspath)
|
||||
|
||||
|
||||
elif args.option == "test" :
|
||||
exit(1)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user