# -*- coding: utf-8 -*-
# License: BSD-3-Clause
# Author: LKouadio <etanoyau@gmail.com>
# Created date: Thu Sep 22 10:50:13 2022
"""
Core geology
=====================
The core module deals with geological data, the structural infos
and borehole data.
"""
import os
import warnings
import shutil
from six.moves import urllib
from pprint import pprint
from ..utils.funcutils import (
smart_format,
sPath,
)
from ..utils._dependency import (
import_optional_dependency )
from ..property import (
Config
)
from ..exceptions import (
GeoPropertyError,
)
from .._watexlog import watexlog
_logger = watexlog().get_watex_logger(__name__ )
__all__=[
"GeoBase",
"set_agso_properties",
"mapping_stratum",
"fetching_data_from_repo",
"get_agso_properties"
]
[docs]class GeoBase:
"""
Base class of container of geological informations for stratigraphy model
log creation of exploration area.
Each station is condidered as an attribute and framed by two closest
points from the station offsets. The class deals with the true resistivity
values collected on exploration area from drilling or geolgical companies.
Indeed, the input true resistivity values into the occam2d inversion data
could yield an accuracy underground map. The challenge to build a pseudolog
framed between two stations allow to know the layers disposal or supperposition
from to top to the investigation depth. The aim is to emphasize a large
conductive zone usefful in of groundwater exploration.
The class `Geodrill` deals at this time with Occam 2D inversion files or
Bo Yang model (x,y,z) files. We intend to extend later with other external
softwares like the Modular System EM (MODEM) and else.
It's also possible to generate output straighforwardly for others external
softwares like Golder sofwares('surfer')or Oasis Montaj:
- Surfer: :https://www.goldensoftware.com/products/surfer)
- Oasis: http://updates.geosoft.com/downloads/files/how-to-guides/Oasis_montaj_Gridding.pdf
<https://www.seequent.com/products-solutions/geosoft-oasis-montaj/>
Note:
If the user has a golder software installed on its computer, it 's
possible to use the output files generated here to yield a 2D map so
to compare both maps to see the difference between model map (
only inversion files and detail-sequences map after including the
input true resistivity values and layer names)
Futhermore, the "pseudosequences model" could match and describe better
the layers disposal (thcikness and contact) in underground than the
raw model map which seems to be close to the reality when `step descent`
parameter is not too small at all.
"""
def __init__(self, verbose: int =0 , **kwargs):
self._logging = watexlog.get_watex_logger(self.__class__.__name__)
self.verbose= verbose
for key in list(kwargs.keys()):
setattr(self, key, kwargs[key])
#++++ configure the geological rocks from files:AGSO & AGSO.STCODES +++++++++++
__agso_properties =dict(
GIT_REPO = 'https://github.com/WEgeophysics/watex',
GIT_ROOT ='https://raw.githubusercontent.com/WEgeophysics/watex/master/',
props_dir = 'watex/etc/',
props_files = ['AGSO.csv', 'AGSO_STCODES.csv'],
props_codes = ['code', 'label', 'name', 'pattern','size',
'density', 'thickness', 'color']
)
[docs]def set_agso_properties (download_files = True ):
""" Set the rocks and their properties from inner files located in
< 'watex/etc/'> folder."""
msg= ''.join([
"Please don't move or delete the properties files located in",
f" <`{__agso_properties['props_dir']}`> directory."])
mf =list()
__agso= [ os.path.join(os.path.realpath(__agso_properties['props_dir']),
f) for f in __agso_properties['props_files']]
for f in __agso:
agso_exists = os.path.isfile(f)
if not agso_exists:
mf.append(f)
continue
if len(mf)==0: download_files=False
if download_files:
for file_r in mf:
success = fetching_data_from_repo(props_files = file_r,
savepath = os.path.join(
os.path.realpath('.'), __agso_properties['props_dir'])
)
if not success:
msg_ = ''.join([ "Unable to retreive the geostructure ",
f"{os.path.basename(file_r)!r} property file from",
f" {__agso_properties['GIT_REPO']!r}."])
warnings.warn(f"Geological structure file {file_r} "
f"is missing. {msg_}")
_logger.warn( msg_)
raise GeoPropertyError(
f"No property file {os.path.basename(file_r)!r}"
f" is found. {msg}.")
for f in __agso:
with open(f,'r' , encoding ='utf8') as fs:
yield([stratum.strip('\n').split(',')
for stratum in fs.readlines()])
[docs]def mapping_stratum(download_files =True):
""" Map the rocks properties from _geocodes files and fit
each rock to its properties.
:param download_files: bool
Fetching data from repository if the geostrutures files are missing.
:return: Rocks and structures data in two diferent dictionnaries
"""
# get code description _index
ix_= __agso_properties['props_codes'].index('name')
def mfunc_(d):
""" Set individual layer in dict of properties """
_p= {c: k.lower() if c not in ('code', 'label', 'name') else k
for c, k in zip(__agso_properties['props_codes'], d) }
id_= d[ix_].replace('/', '_').replace(
' ', '_').replace('"', '').replace("'", '').lower()
return id_, _p
rock_and_structural_props =list()
for agso_data in tuple(set_agso_properties(download_files)):
# remove the header of the property file
rock_and_structural_props.append(
dict(map( lambda x: mfunc_(x), agso_data[1:])))
return tuple(rock_and_structural_props)
[docs]def fetching_data_from_repo(repo_file, savepath =None ):
""" Try to retrieve data from github repository.
:param repo_file: str or Path-like object
Give the full path from the repository root to the file name.
For instance, we want to retrieve the file 'AGSO.csv' which is located
in <watex/etc/> directory then the full path
is: --> 'watex/etc/AGSO.csv'
:return:`status`: Either ``False` for failed downloading
or ``True`` for successfully downloading
"""
fmsg =['... 1rst attempt...','... 2nd attempt...','... 3rd attempt...']
status=False
git_repo = __agso_properties['GIT_REPO']
git_root = __agso_properties['GIT_ROOT']
# Install bar progression
import_optional_dependency ("tqdm")
from tqdm.notebook import trange
# max attempts =3 :
print("---> Please wait while fetching"
f" {repo_file!r} from {git_repo!r}...")
for k in trange(3, ascii=True, desc ='geotools', ncols =107):
#for i in tqdm(range(3), ascii=True, desc ='WEgeophysics', ncols =107):
for _ in trange(1, ascii=True ,desc =fmsg [k],ncols =107):
try :
urllib.request.urlretrieve(git_root, repo_file )
except:
try :
with urllib.request.urlopen(git_root) as response:
with open( repo_file,'wb') as out_file:
data = response.read() # a `bytes` object
out_file.write(data)
except TimeoutError:
if k ==2:
print("---> Established connection failed "
" because connected host has failed to respond.")
except:pass
else : status=True
if status: break
if status: print(f"---> Downloading {repo_file!r} from {git_repo!r} "
"was successfully done!")
else: print(f"---> Failed to download {repo_file!r} from {git_repo!r}!")
# now move the file to the right place and create path if dir not exists
if savepath is not None:
if not os.path.isdir(savepath):
sPath (savepath)
shutil.move(os.path.realpath(repo_file), savepath )
if not status:pprint(connect_reason )
return status
[docs]def get_agso_properties(config_file =None, orient ='series'):
""" Get the geostructures files from <'watex/etc/'> and
set the properties according to the desire type. When `orient` is
``series`` it will return a dictionnary with key equal to
properties name and values are the properties items.
:param config_file: Path_Like or str
Can be any property file provided hat its obey the disposal of
property files found in `__agso_properties`.
:param orient: string value, ('dict', 'list', 'series', 'split',
'records’, ''index') Defines which dtype to convert
Columns(series into).For example, 'list' would return a
dictionary of lists with Key=Column name and Value=List
(Converted series). For furthers details, please refer to
https://www.geeksforgeeks.org/python-pandas-dataframe-to_dict/
:Example:
>>> import watex.utils.geotools as GU
>>> data=get_agso_properties('watex/etc/AGSO_STCODES.csv')
>>> code_descr={key:value for key , value in zip (data["CODE"],
data['__DESCRIPTION'])}
"""
msg= ''.join(["<`{0}`> is the software property file. Please don't move "
" or delete the properties files located in <`{1}`> directory."])
pd_pos_read = Config().parsers
ext='none'
if config_file is None:
config_file = os.path.join(os.path.realpath('.'), os.path.join(
__agso_properties['props_dir'],
__agso_properties ['props_files'][0]))
if config_file is not None:
is_config = os.path.isfile(config_file)
if not is_config :
if os.path.basename(config_file) in __agso_properties['props_files']:
_logger.error(f"Unable to find the geostructure property"
f"{os.path.basename(config_file)!r} file."
)
warnings.warn(msg.format(os.path.basename(config_file) ,
__agso_properties['props_dir']))
raise FileExistsError(f"File `{config_file}`does not exist")
_, ext = os.path.splitext(config_file)
if ext not in pd_pos_read.keys():
_logger.error(f"Unable to read {config_file!r}. Acceptable formats"
f" are {smart_format(list(pd_pos_read.keys()))}.")
raise TypeError(
f"Format {ext!r} cannot be read. Can only read "
f"{smart_format(list(pd_pos_read.keys()))} files."
)
agso_rock_props = pd_pos_read[ext](config_file).to_dict(orient)
if ('name' or 'NAME') in agso_rock_props.keys():
agso_rock_props['__DESCRIPTION'] = agso_rock_props ['name']
del agso_rock_props['name']
return agso_rock_props
##############connection git error ##########################
connect_reason ="""<ConnectionRefusedError><No connection could '
be made because the target machine actively refused it>.
There are some possible reasons for that:
1. The server is not running. Hence it wont listen to that port.
If it's a service you may want to restart the service.
2. The server is running but that port is blocked by Windows Firewall
or other firewall. You can enable the program to go through
firewall in the Inbound list.
3. There is a security program on your PC, i.e a Internet Security
or Antivirus that blocks several ports on your PC.
"""
#+++++ end AGSO & AGSO.STCODES configuration +++++++++++++++++++++++++++++++++