# -*- coding: utf-8 -*-
# Licence:BSD 3-Clause
# Author: LKouadio
from __future__ import annotations
import numpy as np
import pandas as pd
from ..utils.coreutils import makeCoords
from ..utils.gistools import ll_to_utm
from ..utils.exmath import fitfunc
from ..utils.box import Boxspace
__all__ =["make_erp", "make_ves"]
[docs]def make_erp (
*,
n_stations:int= 42,
max_rho:float= 1e3 ,
min_rho:float= 1e0,
step:float=20.,
reflong:float|str='110:29:09.00',
reflat:float|str='26:03:05.00' ,
utm_zone:str='29N',
order:str='+',
full_coordinates:bool=True,
raise_warning:bool=False,
as_frame:bool=False,
seed:int=None,
**coord_kws
):
r""" Generate Electrical Resistivity Profiling (ERP) data from stations
and coordinates points.
To generate samples from specific area, it is better to provide both
latitude and longitude values from a single station of this area as arguments
passed to parameters `reflat` and `reflong` respectively. Also specify the
`utm_zone` for the lat/lon coordinates conversion into UTM if necessary.
If not useful, can turn off the parameter `full_coordinates` to ``False``.
Parameters
-----------
n_stations: int, default=42
number of measurements stations
max_rho: float, default=1e3
maximum resistivity value on the survey area in :math:`\Omega.m`
min_rho: float, default=1e0
minimum resistivity value on the survey area in :math:`\Omega.m`
reflong: float or string or list of [start, stop], default='110:29:09.00'
Reference longitude in degree decimal or in DD:MM:SS for the first
station considered as the origin of the landmark.
reflat: float or string or list of [start, stop], default='26:03:05.00'
Reference latitude in degree decimal or in DD:MM:SS for the reference
site considered as the landmark origin. If value is given in a list,
it can contain the start point and the stop point.
step: float or str , default=20
Offset or the distance of seperation between different sites in meters.
If the value is given as string type, except the ``km``, it should be
considered as a ``m`` value. Only meters and kilometers are accepables.
order: str , default='-'
Direction of the projection line. By default the projected line is
in ascending order i.e. from SW to NE with angle `r` set to ``45``
degrees. Could be ``-`` for descending order. Any other value should
be in ascending order.
utm_zone: string (##N or ##S), default='29N'
utm zone in the form of number and North or South hemisphere, 10S or 03N
Must be given if `utm2deg` is set to ``True``.
full_coordinates: bool, default=True,
Convert latitude and longitude to approximate UTM values. Easting and
northing are gotten using the reference ellipsoid =23 with WGS84.
If ``False``, easting and northing are not computed and set to null.
raise_warning: bool, default=True,
Raises warnings if GDAL is not set or the coordinates accurately status.
as_frame: bool, default=False,
if ``True``, outputs the data into as a pandas dataframe,
:class:`~watex.utils.box.Boxspace` object otherwise.
seed: int, Optional,
It allows reproducing the same data. If value is passed, it reproduces
the same data at that sample points.
coord_kws: dict,
Additional keywords passed to :func:`~watex.utils.coreutils.makeCoords`.
Returns
--------
(pd.Dataframe | :class:`~watex.utils.box.Boxspace` )
Examples
----------
>>> from watex.datasets.gdata import make_erp
>>> erp_data = make_erp (n_stations =50 , step =30 , as_frame =True)
>>> erp_data.head(3)
Out[256]:
station longitude latitude easting northing resistivity
0 0 -13.488511 0.000997 668210.580864 110.183287 225.265306
1 30 -13.488511 0.000997 668210.581109 110.183482 327.204082
2 60 -13.488510 0.000997 668210.581355 110.183676 204.877551
"""
stations = np.arange (0 , n_stations * step , step )
resistivity =np.linspace(min_rho, max_rho , n_stations)
if seed is not None:
np.random.seed(seed)
np.random.shuffle(resistivity)
rlons, rlats = makeCoords(reflong, reflat, nsites=n_stations, is_utm=True,
utm_zone =utm_zone, step = step,
raise_warning=raise_warning, **coord_kws)
easting = np.zeros_like(rlons) ; northing = np.zeros_like(rlats)
if full_coordinates:
en = [ll_to_utm (23 , lat , lon )
for lat, lon in zip (rlats, rlons)]
_, easting , northing = zip (*en)
d = {
"station": stations,
"longitude":rlons,
"latitude": rlats,
"easting": easting,
"northing": northing,
"resistivity": resistivity
}
dx ={"frame": pd.DataFrame (d), "data": pd.DataFrame (d).values }
data = Boxspace(**d, **dx )
return data.frame if as_frame else data
[docs]def make_ves (
*,
samples:int= 31,
min_rho:float=1e1,
max_rho:float= 1e3,
max_depth:float= 100.,
order:str='-',
as_frame:bool=False,
seed:int=None,
iorder:float=3,
):
r""" Generate Vertical Electrical Sounding (VES) data from
pseudo-depth measurements.
For a large pseudo-depth measurements, one can change the number of samples
to a large values. The default samples presumed collected is ``samples=31``
measurements in deeper.
Parameters
-----------
samples: int, default=42
number of measurements depth AB/2 in meters.
max_rho: float, default=1e3
maximum resistivity value expected in deeeper on the survey area
in :math:`\\Omega.m`
min_rho: float, default=1e1
minimum resistivity value expected in deeper on the survey area
in :math:`\\Omega.m`
order: str , default='-'
Direction of the projection line. By default the projected line is
in ascending order i.e. from SW to NE with angle `r` set to ``45``
degrees. Could be ``-`` for descending order. Any other value should
be in ascending order.
max_depth: float, default=100
Value of the measurement in deeper expected to reach by AB/2 in meters.
as_frame: bool, default=False,
if ``True``, outputs the data into as a pandas dataframe,
:class:`~watex.utils.box.Boxspace` object otherwise.
seed: int, Optional,
It allows reproducing the same data. If value is passed, it reproduces
the same data at that sample points.
iorder: float, default=3
Inflexion order. If ``None`` should compute using the length of
extrema (local + global). Must be lower as possible to let the
fitting VES curve more realistic.
Returns
---------
(pd.Dataframe | :class:`~watex.utils.box.Boxspace` )
Notes
-------
when returning the :class:`~watex.utils.box.Boxspace` object, each columns
of 'VES' data can be retrieved as an attributes. Check the examples below
Examples
---------
>>> from watex.datasets.gdata import make_ves
>>> b = make_ves (samples =50 , order ='+') # 50 measurements in deeper
>>> b.resistivity [:-7]
Out[314]:
array([429.873 , 434.255 , 438.5707, 442.8203, 447.0042, 451.1228,
457.5775])
>>> b.frame.head(3)
Out[315]:
AB MN resistivity
0 1.0 0.6 429.872999
1 2.0 0.6 434.255018
2 3.0 0.6 438.570675
"""
ix , mnv= [ 4, 16 , 26 , 31 ], [.4 , 1., 5., 10.]
abv = [1, 2, 4, 10 ]
ix = np.array ( np.array (ix) * samples / 31 , dtype =int )
mnv = np.array(mnv, dtype =float ) * samples / 31
abv = np.array(abv, dtype =float ) * samples / 31
MN_max = max_depth //10 #
MN = _generate_ABMN_samples(indexes= ix, fixed_values=mnv,
threshold_multiplicator= MN_max )
AB = _generate_ABMN_samples(indexes = ix , fixed_values=abv,
threshold_multiplicator=MN_max, kind ="AB")
# make resistivity
g = np.linspace ( max_rho, min_rho , samples
) if order =='-' else np.linspace (min_rho, max_rho , samples )
if seed is not None:
np.random.seed(seed )
np.random.shuffle(g)
f, *_ = fitfunc (AB, g, deg= iorder )
resistivity = f (AB )
d= {"AB":AB, "MN": MN, "resistivity":resistivity}
dx ={"frame": pd.DataFrame (d), "data": pd.DataFrame (d).values }
data = Boxspace(**d, **dx )
return data.frame if as_frame else data
def _generate_ABMN_samples (
indexes, fixed_values, threshold_multiplicator, kind ='MN'):
""" Isolated part for 'VES' data generating. """
# use index MN for test
MN=list()
nsam = np.array (np.diff (indexes ), dtype = int )
for ii, (ind, v) in enumerate (zip (indexes, fixed_values)):
if ii==0:
s = [ v * threshold_multiplicator / 10 for i in range (
ind ) ] if kind =="MN" else list(
np.linspace (1, indexes [0], indexes [0]))
else:
s = [v* threshold_multiplicator / 10 for i in range (
indexes[ii -1], indexes[ii])] if kind=="MN" else list(
np.arange (0, nsam[ii-1] * v, v ) + s[-1] + fixed_values[ii-1])
MN.extend (s)
return np.around (MN, 1 )