Tom Deposit Exploration#

import pandas as pd
import geolime as geo
from pyproj import CRS
import pyvista as pv
import rioxarray
import numpy as np

geo.Project().set_crs(CRS("EPSG:26909"))
pv.set_jupyter_backend("html")
downloading libjulime_deb.zip 2.5.0 (posix-x86_64 system) to /home/runner/work/fireweed-report/fireweed-report/.venv/lib/python3.11/site-packages/julime/2.5.0

Drillholes CSV Loading#

assay_tom = pd.read_csv("../data/assay_tom.csv")
collar_tom = pd.read_csv("../data/collar_tom.csv")
survey_tom = pd.read_csv("../data/survey_tom.csv")
collar_tom
HoleID HoleType Year Property Prospect Grid Easting Northing Elevation NewsRelease Length_m Dip Azimuth
0 TS001 DDH 1952 TOM Tom West NAD83 / UTM zone 9N 441888.14 7004051.35 1532.57 Historical 301.75 -35.0 65.03
1 TS002 DDH 1952 TOM Tom West NAD83 / UTM zone 9N 441942.15 7003966.21 1539.31 Historical 216.71 -35.0 65.03
2 TS003 DDH 1952 TOM Tom Exploration NAD83 / UTM zone 9N 441826.15 7004181.90 1527.04 Historical 79.55 -35.0 65.03
3 TS004 DDH 1952 TOM Tom West NAD83 / UTM zone 9N 441983.46 7003850.58 1526.92 Historical 93.88 -35.0 65.03
4 TS005 DDH 1952 TOM Tom Exploration NAD83 / UTM zone 9N 441796.11 7004298.67 1516.00 Historical 64.62 -35.0 65.03
... ... ... ... ... ... ... ... ... ... ... ... ... ...
213 TYK007 DDH 2011 TOM Tom West NAD83 / UTM zone 9N 441803.28 7003963.84 1482.02 Historical 227.00 -80.0 65.68
214 TYK008 DDH 2011 TOM Tom West NAD83 / UTM zone 9N 441825.15 7003908.66 1484.17 Historical 177.62 -50.0 45.67
215 TYK009 DDH 2011 TOM Tom West NAD83 / UTM zone 9N 441824.96 7003908.59 1481.79 Historical 245.00 -80.0 55.67
216 TYK010 DDH 2011 TOM Tom West NAD83 / UTM zone 9N 441868.48 7003867.59 1492.28 Historical 180.00 -55.0 65.66
217 TYK011 DDH 2011 TOM Tom West NAD83 / UTM zone 9N 441868.48 7003867.59 1492.28 Historical 204.42 -78.0 71.03

218 rows × 13 columns

assay_tom
HoleID From_m To_m Ag_ppm Pb_pct Zn_pct BD_tonnes_m3 Method Comment LowRecovery_<=85pct
0 TS001 11.77 12.65 0.25 0.200 1.50 2.598 REG NaN N
1 TS001 19.81 21.34 0.25 0.200 3.40 2.663 REG NaN N
2 TS001 21.34 22.86 0.25 0.500 3.10 2.663 REG NaN N
3 TS001 22.86 23.84 13.03 1.100 3.50 2.697 REG NaN N
4 TS001 23.84 24.60 0.25 0.005 0.70 2.564 REG NaN N
... ... ... ... ... ... ... ... ... ... ...
3635 TYK011 191.00 192.00 2.00 0.940 1.09 2.750 REG NaN N
3636 TYK011 192.00 193.00 4.00 1.180 1.34 2.800 REG NaN N
3637 TYK011 193.00 194.00 1.00 0.090 1.77 2.820 REG NaN N
3638 TYK011 194.00 195.00 1.00 0.030 0.61 2.710 REG NaN N
3639 TYK011 195.00 196.00 1.00 0.050 0.31 2.710 REG NaN N

3640 rows × 10 columns

survey_tom
HoleID Depth_m Dip Azimuth
0 TS001 0.00 -35.0 65.03
1 TS001 30.48 -35.5 65.03
2 TS001 60.96 -30.0 65.03
3 TS001 91.44 -25.0 65.03
4 TS001 121.92 -25.0 65.03
... ... ... ... ...
1254 TYK011 113.00 -71.6 80.89
1255 TYK011 137.00 -70.1 80.89
1256 TYK011 161.00 -70.4 80.59
1257 TYK011 185.00 -70.1 80.69
1258 TYK011 204.00 -69.4 79.29

1259 rows × 4 columns

Topo DXF Loading#

tom_topo = geo.surface_from_dxf("Tom", "../data/Topo/TOM_Topo_DEM_Downsampled.dxf")
/home/runner/work/fireweed-report/fireweed-report/.venv/lib/python3.11/site-packages/pyogrio/raw.py:337: RuntimeWarning: Non closed ring detected. To avoid accepting it, set the OGR_GEOMETRY_ACCEPT_UNCLOSED_RING configuration option to NO
  table = reader.read_all()

GeoLime Drillholes creation#

Creation of a Drillholes object using the collar, assay & survey DataFrame. As the holes are deviated we use the minimum curvature method for the desurveying. In the collar file the Z information is provided as Elevation and we have to specify it as GeoLime does not recognizes it by default.

dh_tom = geo.create_drillholes(
    name="Tom", 
    collar=collar_tom, 
    assays=assay_tom, 
    survey=survey_tom,
    method=geo.DesurveyingMethod.MINIMUM_CURVATURE,
)
2025-10-03 14:57:45,639 [INFO] GeoLime Project - |GEOLIME|.drillholes_api.py : Following mapping has been identified: {
    "HOLEID": "HoleID",
    "X_COLLAR": "Easting",
    "Y_COLLAR": "Northing",
    "Z_COLLAR": "Elevation",
    "AZIMUTH": "Azimuth",
    "DEPTH_SURVEY": "Depth_m",
    "DIP": "Dip",
    "FROM": "From_m",
    "TO": "To_m"
}
2025-10-03 14:57:45,741 [WARNING] GeoLime Project - |GEOLIME|.drillholes_api.py : Non alpha-numeric characters found: they will be replaced with "_". LowRecovery_<=85pct => LowRecovery___85pct

Like Pandas, we can use describe on a GeoLime GeoRefObject.

dh_tom.describe()
__TOPO_LEVEL__ __TOPO_TYPE__ __TOPO_ELEMENT_V1__ __TOPO_ELEMENT_V2__ FROM TO X_COLLAR Y_COLLAR Z_COLLAR X_M ... Z_B X_E Y_E Z_E Ag_ppm Pb_pct Zn_pct BD_tonnes_m3 Year Length_m
count 3640.0 3640.0 3640.000000 3640.000000 3640.000000 3640.000000 3640.000000 3.640000e+03 3640.000000 3640.000000 ... 3640.000000 3640.000000 3.640000e+03 3640.000000 3540.000000 3570.000000 3577.000000 3597.000000 3640.000000 3640.000000
mean 2.0 1.0 1906.136538 1907.136538 133.119192 134.179437 441960.385791 7.003908e+06 1526.061869 442001.102646 ... 1415.225451 442001.320438 7.003935e+06 1414.552817 39.283319 3.346034 5.228746 3.100153 1985.638462 186.574047
std 0.0 0.0 1098.897314 1098.897314 152.536734 152.617765 172.712561 3.179138e+02 96.533003 160.104935 ... 134.099141 160.075625 3.031908e+02 134.209040 90.123908 6.211137 4.795881 0.475464 22.339533 158.409563
min 2.0 1.0 0.000000 1.000000 0.000000 0.120000 439208.010000 7.002957e+06 1198.548000 439215.109175 ... 884.556258 439215.080706 7.003235e+06 883.883245 0.200000 0.001300 0.000200 1.610000 1952.000000 4.270000
25% 2.0 1.0 962.500000 963.500000 43.000000 44.200000 441876.740000 7.003735e+06 1443.740000 441921.051149 ... 1353.081132 441921.167652 7.003743e+06 1352.570332 1.000000 0.210000 1.200000 2.740000 1970.000000 99.060000
50% 2.0 1.0 1893.500000 1894.500000 99.000000 100.000000 441972.970000 7.003907e+06 1522.195000 442015.240070 ... 1441.640000 442015.427853 7.003915e+06 1441.620000 3.430000 1.200000 4.790000 2.940000 1980.000000 163.225000
75% 2.0 1.0 2869.250000 2870.250000 156.015000 157.000000 442025.340000 7.004051e+06 1583.835000 442060.480308 ... 1501.308163 442060.717111 7.004069e+06 1500.462286 24.850000 3.107873 7.150000 3.438000 2011.000000 208.170000
max 2.0 1.0 3796.000000 3797.000000 944.200000 945.200000 442855.140000 7.005252e+06 1847.500000 442767.895702 ... 1683.868409 442767.754167 7.005264e+06 1683.176623 900.000000 65.400000 35.400000 5.223000 2017.000000 945.200000

8 rows × 24 columns

The following methods give us insights on the number of holes ans samples of a Drillholes object.

len(dh_tom.holeids())
158
dh_tom.element_count()
3640

Data can be plotted on a 2D map colored by continuous or categorical properties. Here are displayed the different prospects, which are used to add another subselection of deposits within the entire MacPass project.

dh_tom.plot_2d(property="Prospect", agg_method="first", interactive_map=True)
Make this Notebook Trusted to load map: File -> Trust Notebook

Numerical Data can be specified as category using the categorical boolean parameter.

dh_tom.plot_2d(property="Year", agg_method="first", interactive_map=True, categorical=True)
Make this Notebook Trusted to load map: File -> Trust Notebook

3D Visualisation#

dh_tom_pv = dh_tom.to_pyvista('Zn_pct')
tom_topo_pv = tom_topo.to_pyvista()
pl = pv.Plotter()

pl.add_mesh(dh_tom_pv.tube(radius=10))
pl.add_mesh(tom_topo_pv, opacity=0.7)

pl.show()
2025-10-03 14:57:46.049 (  20.814s) [    7F690BA79B80]vtkXOpenGLRenderWindow.:1458  WARN| bad X server connection. DISPLAY=

Add Satellite Image#

ds = rioxarray.open_rasterio("../data/TOM.tif")
ds = ds.rio.reproject("EPSG:26909")
## Fetch the texture as an image
image = np.moveaxis(ds.values, 0, -1)

## Create the ground control points for texture mapping
o = ds.x.min(), ds.y.min(), 0.0 # Bottom Left
u = ds.x.max(), ds.y.min(), 0.0 # Bottom Right
v = ds.x.min(), ds.y.max(), 0.0 # Lop left
## Note: Z-coordinate doesn't matter

## Use the GCPs to map the tex coords
mapped_surf = tom_topo_pv.texture_map_to_plane(o, u, v)

# Plot using texture 
pl = pv.Plotter()

pl.add_mesh(mapped_surf, texture=pv.numpy_to_texture(image))
pl.add_mesh(dh_tom_pv.tube(radius=10))

pl.show()
/tmp/ipykernel_3048/3034908606.py:11: PyVistaDeprecationWarning: 
../../../../../../tmp/ipykernel_3048/3034908606.py:11: Arguments 'origin', 'point_u', 'point_v' must be passed as keyword arguments to function 'DataSetFilters.texture_map_to_plane'.
From version 0.50, passing these as positional arguments will result in a TypeError.
  mapped_surf = tom_topo_pv.texture_map_to_plane(o, u, v)

Exploration of different Prospects#

set transforms any sequence to a set of unique values. We can also the unique fonction from numpy in order to have the unique values as a numpy array.

set(dh_tom['Prospect'])
{'Exploration', 'Tom East', 'Tom Exploration', 'Tom Southeast', 'Tom West'}
prospect_list = np.unique(dh_tom['Prospect'])
for prospect in prospect_list:
    hid = dh_tom.data("HOLEID", f'Prospect == "{prospect}"')
    print(f"Prospect {prospect} has {len(set(hid))} holes")
Prospect Exploration has 1 holes
Prospect Tom East has 36 holes
Prospect Tom Exploration has 18 holes
Prospect Tom Southeast has 9 holes
Prospect Tom West has 94 holes

Mean values of Zinc in the Tom East prospect can be displayed using the region filter in the plot_2d method

dh_tom.plot_2d(
    property="Zn_pct", 
    agg_method="mean", 
    region="Prospect == 'Tom East'",
    region_agg='all',
    interactive_map=True
)
Make this Notebook Trusted to load map: File -> Trust Notebook

Export GeoLime File#

dh_tom.to_file("../data/dh_tom")