zonevu 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zonevu/DataModels/Company.py +18 -0
- zonevu/DataModels/Curve.py +59 -0
- zonevu/DataModels/DataModel.py +40 -0
- zonevu/DataModels/Formation.py +55 -0
- zonevu/DataModels/Geomodels/DataGrid.py +27 -0
- zonevu/DataModels/Geomodels/Geomodel.py +27 -0
- zonevu/DataModels/Geomodels/SimpleGrid.py +26 -0
- zonevu/DataModels/Geomodels/Structure.py +16 -0
- zonevu/DataModels/Geomodels/__init__.py +0 -0
- zonevu/DataModels/Geospatial/Coordinate.py +22 -0
- zonevu/DataModels/Geospatial/Crs.py +44 -0
- zonevu/DataModels/Geospatial/Enums.py +8 -0
- zonevu/DataModels/Geospatial/GeoBox.py +44 -0
- zonevu/DataModels/Geospatial/GeoLocation.py +52 -0
- zonevu/DataModels/Geospatial/GridCorner.py +14 -0
- zonevu/DataModels/Geospatial/GridGeometry.py +91 -0
- zonevu/DataModels/Geospatial/__init__.py +0 -0
- zonevu/DataModels/Geosteering/Conditioning.py +42 -0
- zonevu/DataModels/Geosteering/CurveDef.py +26 -0
- zonevu/DataModels/Geosteering/Horizon.py +50 -0
- zonevu/DataModels/Geosteering/Interpretation.py +35 -0
- zonevu/DataModels/Geosteering/Pick.py +27 -0
- zonevu/DataModels/Geosteering/__init__.py +0 -0
- zonevu/DataModels/Helpers.py +49 -0
- zonevu/DataModels/Map/UserLayer.py +43 -0
- zonevu/DataModels/Map/__init__.py +0 -0
- zonevu/DataModels/Note.py +20 -0
- zonevu/DataModels/NoteCategory.py +10 -0
- zonevu/DataModels/Project.py +80 -0
- zonevu/DataModels/Station.py +23 -0
- zonevu/DataModels/StratColumn.py +19 -0
- zonevu/DataModels/Styles/Colors.py +31 -0
- zonevu/DataModels/Styles/FillStyle.py +22 -0
- zonevu/DataModels/Styles/LineStyle.py +22 -0
- zonevu/DataModels/Styles/__init__.py +0 -0
- zonevu/DataModels/Survey.py +86 -0
- zonevu/DataModels/Well.py +196 -0
- zonevu/DataModels/WellEntry.py +24 -0
- zonevu/DataModels/Wellbore.py +71 -0
- zonevu/DataModels/Welllog.py +50 -0
- zonevu/DataModels/Welltop.py +24 -0
- zonevu/DataModels/__init__.py +0 -0
- zonevu/Samples/DataMgmt/CopyGeosteeringInterp.py +64 -0
- zonevu/Samples/DataMgmt/CopyLog.py +106 -0
- zonevu/Samples/DataMgmt/CopySurvey.py +72 -0
- zonevu/Samples/DataMgmt/CopyWell.py +43 -0
- zonevu/Samples/DataMgmt/DeleteProjectWells.py +50 -0
- zonevu/Samples/DataMgmt/DeleteWell.py +24 -0
- zonevu/Samples/DataMgmt/GeomodelSample.py +64 -0
- zonevu/Samples/DataMgmt/ProjectCopy.py +34 -0
- zonevu/Samples/DataMgmt/__init__.py +0 -0
- zonevu/Samples/Plot/CrossSection.py +138 -0
- zonevu/Samples/Plot/__init__.py +0 -0
- zonevu/Samples/__init__.py +0 -0
- zonevu/Services/Client.py +156 -0
- zonevu/Services/CompanyService.py +45 -0
- zonevu/Services/CoordinatesService.py +186 -0
- zonevu/Services/CurveService.py +37 -0
- zonevu/Services/EndPoint.py +43 -0
- zonevu/Services/Error.py +39 -0
- zonevu/Services/FormationService.py +44 -0
- zonevu/Services/GeomodelService.py +102 -0
- zonevu/Services/GeosteeringService.py +67 -0
- zonevu/Services/MapService.py +52 -0
- zonevu/Services/NoteService.py +68 -0
- zonevu/Services/ProjectService.py +91 -0
- zonevu/Services/SurveyService.py +62 -0
- zonevu/Services/Utils.py +17 -0
- zonevu/Services/WellData.py +56 -0
- zonevu/Services/WellEntryService.py +24 -0
- zonevu/Services/WellService.py +198 -0
- zonevu/Services/WelllogService.py +94 -0
- zonevu/Services/WelltopService.py +53 -0
- zonevu/Services/__init__.py +0 -0
- zonevu/Zonevu.py +119 -0
- zonevu/__init__.py +0 -0
- zonevu-1.0.0.dist-info/METADATA +24 -0
- zonevu-1.0.0.dist-info/RECORD +79 -0
- zonevu-1.0.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from dataclasses_json import dataclass_json, LetterCase
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass_json(letter_case=LetterCase.PASCAL)
|
|
6
|
+
@dataclass
|
|
7
|
+
class Company:
|
|
8
|
+
Version: str = '' # API Version
|
|
9
|
+
RuntimeVersion: str = '?' # ZoneVu server runtime version
|
|
10
|
+
CompanyName: str = ''
|
|
11
|
+
Notice: str = ''
|
|
12
|
+
|
|
13
|
+
def printNotice(self):
|
|
14
|
+
print("Zonevu Web API Version %s. Zonevu Server Version %s." % (self.Version, self.RuntimeVersion))
|
|
15
|
+
print(self.Notice)
|
|
16
|
+
print("ZoneVu account '%s'" % self.CompanyName)
|
|
17
|
+
print()
|
|
18
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from dataclasses_json import dataclass_json, LetterCase
|
|
5
|
+
from .DataModel import DataModel
|
|
6
|
+
import numpy as np
|
|
7
|
+
from strenum import StrEnum
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AppMnemonicCodeEnum(StrEnum):
|
|
11
|
+
NotSet = "NotSet"
|
|
12
|
+
DEPT = "DEPT" # "Hole depth (MD)"
|
|
13
|
+
GR = "GR" # "Gamma ray"
|
|
14
|
+
ROP = "ROP" # "Rate of penetration"
|
|
15
|
+
WOB = "WOB" # "Weight on bit"
|
|
16
|
+
INCL = "INCL" # "Inclination"
|
|
17
|
+
AZIM = "AZIM" # "Azimuth"
|
|
18
|
+
GAS = "GAS" # "Total Gas"
|
|
19
|
+
BIT = "BIT" # "Bit depth"
|
|
20
|
+
GRDEPT = "GRDEPT" # "Gamma ray depth"
|
|
21
|
+
DENS = "DENS" # "Density"
|
|
22
|
+
RESS = "RESS" # "Shallow Resistivity"
|
|
23
|
+
RESM = "RESM" # "Medium Resistivity"
|
|
24
|
+
RESD = "RESD" # "Deep Resistivity"
|
|
25
|
+
DTC = "DTC" # "Compressional Sonic Travel Time"
|
|
26
|
+
DTS = "DTS" # "Shear Sonic Travel Time"
|
|
27
|
+
SP = "SP" # "Spontaneous Potential"
|
|
28
|
+
PHIN = "PHIN" # "Neutron Porosity"
|
|
29
|
+
PHID = "PHID" # "Density Porosity"
|
|
30
|
+
NMR = "NMR" # "Nuclear Magnetic Resonance"
|
|
31
|
+
PE = "PE" # "Photoelectric cross section"
|
|
32
|
+
AGR = "AGR" # "Azimuthal Gamma Ray"
|
|
33
|
+
PHIE = "PHIE" # "Porosity"
|
|
34
|
+
SW = "SW" # "Water Saturation"
|
|
35
|
+
VSHL = "VSHL" # "Shale Content"
|
|
36
|
+
HCP = "HCP" # "HydocarbonPorosity"
|
|
37
|
+
TIME = "TIME" # "Time (UTC)"
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def _missing_(cls, value):
|
|
41
|
+
return AppMnemonicCodeEnum.NotSet
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass_json(letter_case=LetterCase.PASCAL)
|
|
45
|
+
@dataclass
|
|
46
|
+
class Curve(DataModel):
|
|
47
|
+
description: Optional[str] = None
|
|
48
|
+
mnemonic: str = ''
|
|
49
|
+
system_mnemonic: AppMnemonicCodeEnum = field(default_factory=lambda: AppMnemonicCodeEnum.NotSet)
|
|
50
|
+
unit: Optional[str] = None
|
|
51
|
+
samples: Optional[np.ndarray] = None # numpy float array
|
|
52
|
+
registered: bool = True
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
def from_dict(cls, dict_obj):
|
|
56
|
+
instance = cls(**dict_obj)
|
|
57
|
+
return instance
|
|
58
|
+
|
|
59
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from typing import Optional, Type
|
|
2
|
+
from dataclasses import dataclass, field, fields
|
|
3
|
+
from dataclasses_json import dataclass_json, LetterCase
|
|
4
|
+
from strenum import StrEnum
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ChangeAgentEnum(StrEnum):
|
|
8
|
+
Unknown = 'Unknown'
|
|
9
|
+
GuiCreate = 'GuiCreate'
|
|
10
|
+
GuiImport = 'GuiImport'
|
|
11
|
+
GuiBulkImport = 'GuiBulkImport'
|
|
12
|
+
WebApi = 'WebApi'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class WellElevationUnitsEnum(StrEnum):
|
|
16
|
+
Undefined = 'Undefined'
|
|
17
|
+
Meters = 'Meters'
|
|
18
|
+
Feet = 'Feet'
|
|
19
|
+
FeetUS = 'FeetUS'
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass_json(letter_case=LetterCase.PASCAL)
|
|
23
|
+
@dataclass
|
|
24
|
+
class DataModel:
|
|
25
|
+
id: int = -1 # Mandatory
|
|
26
|
+
name: Optional[str] = None
|
|
27
|
+
row_version: Optional[str] = None
|
|
28
|
+
|
|
29
|
+
def merge_from(self, source: 'DataModel'):
|
|
30
|
+
self.__dict__.update(source.__dict__)
|
|
31
|
+
|
|
32
|
+
def copy_ids_from(self, source: 'DataModel'):
|
|
33
|
+
self.id = source.id
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def merge_lists(dst_list: list['DataModel'], src_list: list['DataModel']):
|
|
37
|
+
for (dst, src) in zip(dst_list, src_list):
|
|
38
|
+
if dst is not None and src is not None:
|
|
39
|
+
dst.copy_ids_from(src)
|
|
40
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from dataclasses_json import dataclass_json, LetterCase, config
|
|
4
|
+
from .DataModel import DataModel
|
|
5
|
+
from strenum import StrEnum, PascalCaseStrEnum
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GeoPeriodEnum(StrEnum):
|
|
9
|
+
Quaternary = 'Quaternary',
|
|
10
|
+
Neogene = 'Neogene',
|
|
11
|
+
Paleogene = 'Paleogene',
|
|
12
|
+
Cretaceous = 'Cretaceous',
|
|
13
|
+
Jurassic = 'Jurassic',
|
|
14
|
+
Triassic = 'Triassic',
|
|
15
|
+
Permian = 'Permian',
|
|
16
|
+
Carboniferous = 'Carboniferous',
|
|
17
|
+
Devonian = 'Devonian',
|
|
18
|
+
Silurian = 'Silurian',
|
|
19
|
+
Ordovician = 'Ordovician',
|
|
20
|
+
Cambrian = 'Cambrian',
|
|
21
|
+
Precambrian = 'Precambrian'
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class LithologyTypeEnum(PascalCaseStrEnum):
|
|
25
|
+
Unset = 'Unset',
|
|
26
|
+
Sandstone = 'Sandstone',
|
|
27
|
+
Shale = 'Shale',
|
|
28
|
+
Limestone = 'Limestone',
|
|
29
|
+
Dolomite = 'Dolomite',
|
|
30
|
+
Chalk = 'Chalk',
|
|
31
|
+
Marl = 'Marl',
|
|
32
|
+
MudstoneRich = 'MudstoneRich',
|
|
33
|
+
MudstoneLean = 'MudstoneLean',
|
|
34
|
+
Bentonite = 'Bentonite',
|
|
35
|
+
Coal = 'Coal',
|
|
36
|
+
Chert = 'Chert',
|
|
37
|
+
Anhydrite = 'Anhydrite',
|
|
38
|
+
Siltstone = 'Siltstone',
|
|
39
|
+
ShalySand = 'ShalySand'
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass_json(letter_case=LetterCase.PASCAL)
|
|
43
|
+
@dataclass
|
|
44
|
+
class Formation(DataModel):
|
|
45
|
+
formation_name: Optional[str] = None
|
|
46
|
+
member_name: Optional[str] = None
|
|
47
|
+
strat_col_order: int = -1
|
|
48
|
+
symbol: str = ''
|
|
49
|
+
color: Optional[str] = None
|
|
50
|
+
description: Optional[str] = None
|
|
51
|
+
period: Optional[GeoPeriodEnum] = None
|
|
52
|
+
lithology_type: Optional[LithologyTypeEnum] = None
|
|
53
|
+
|
|
54
|
+
def __post_init__(self):
|
|
55
|
+
self.name = self.formation_name
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from enum import StrEnum
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from dataclasses_json import dataclass_json, LetterCase
|
|
5
|
+
from zonevu.zonevu.DataModels.DataModel import DataModel
|
|
6
|
+
from zonevu.zonevu.DataModels.Geospatial.GridGeometry import GridGeometry
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class GridUsageEnum(StrEnum):
|
|
11
|
+
Undefined = 'Undefined'
|
|
12
|
+
Structural = 'Structural'
|
|
13
|
+
Isopach = 'Isopach'
|
|
14
|
+
Attribute = 'Attribute'
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass_json(letter_case=LetterCase.PASCAL)
|
|
18
|
+
@dataclass
|
|
19
|
+
class DataGrid(DataModel):
|
|
20
|
+
geometry: GridGeometry = None
|
|
21
|
+
description: Optional[str] = None
|
|
22
|
+
usage: GridUsageEnum = GridUsageEnum.Undefined
|
|
23
|
+
average_value: Optional[float] = None
|
|
24
|
+
min_value: Optional[float] = None
|
|
25
|
+
max_value: Optional[float] = None
|
|
26
|
+
z_values: Optional[np.ndarray] = None # numpy 32-bit (single precision float array). Must be 32-bit floats!
|
|
27
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from dataclasses_json import dataclass_json, LetterCase
|
|
4
|
+
from zonevu.zonevu.DataModels.DataModel import DataModel, ChangeAgentEnum
|
|
5
|
+
from zonevu.zonevu.DataModels.Geomodels.DataGrid import DataGrid
|
|
6
|
+
from zonevu.zonevu.DataModels.Geomodels.Structure import Structure
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from zonevu.zonevu.DataModels.Helpers import MakeIsodataOptionalField
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass_json(letter_case=LetterCase.PASCAL)
|
|
12
|
+
@dataclass
|
|
13
|
+
class Geomodel(DataModel):
|
|
14
|
+
external_id: Optional[str] = None
|
|
15
|
+
external_source: Optional[str] = None
|
|
16
|
+
creator: Optional[str] = None
|
|
17
|
+
change_agent: ChangeAgentEnum = ChangeAgentEnum.Unknown
|
|
18
|
+
creation_date: Optional[datetime] = MakeIsodataOptionalField()
|
|
19
|
+
last_modified_date: Optional[datetime] = MakeIsodataOptionalField()
|
|
20
|
+
division_id: int = 0
|
|
21
|
+
division: str = ''
|
|
22
|
+
strat_column_id: int = 0
|
|
23
|
+
strat_column_name: str = ''
|
|
24
|
+
description: Optional[str] = None
|
|
25
|
+
data_grids: list[DataGrid] = field(default_factory=list[DataGrid])
|
|
26
|
+
structures: list[Structure] = field(default_factory=list[Structure])
|
|
27
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from typing import List
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from ..Geospatial.Coordinate import Coordinate
|
|
5
|
+
from ..Geospatial.Crs import CrsSpec
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class SimpleGrid:
|
|
10
|
+
"""
|
|
11
|
+
Represents a simple grid.
|
|
12
|
+
Use float('-inf') to represent empty grid values
|
|
13
|
+
"""
|
|
14
|
+
name: str
|
|
15
|
+
origin: Coordinate # x,y coordinate of grid origin
|
|
16
|
+
inclination: float # rotation of grid clockwise
|
|
17
|
+
dx: float # grid spacing in x-direction, when not rotated by inclination
|
|
18
|
+
dy: float # grid spacing in y-direction
|
|
19
|
+
num_rows: int # number of rows in grid. Rows are laid out along the x-direction (non-rotated)
|
|
20
|
+
num_cols: int # number of columns in grid. Columns are laid out along the y-direction (non-rotated)
|
|
21
|
+
crs: CrsSpec # The projected coordinate system of the x,y origin.
|
|
22
|
+
z_values: List[float] # Z values of grid, as a 1-D array of 32-bit floats in row major order
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from dataclasses_json import dataclass_json, LetterCase
|
|
4
|
+
from zonevu.zonevu.DataModels.DataModel import DataModel
|
|
5
|
+
from zonevu.zonevu.DataModels.Geospatial.GridGeometry import GridGeometry
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass_json(letter_case=LetterCase.PASCAL)
|
|
10
|
+
@dataclass
|
|
11
|
+
class Structure(DataModel):
|
|
12
|
+
formation_id: int = 0
|
|
13
|
+
formation_name: str = ''
|
|
14
|
+
geometry: GridGeometry = None
|
|
15
|
+
description: Optional[str] = None
|
|
16
|
+
z_values: Optional[np.ndarray] = None # numpy float array
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from dataclasses_json import dataclass_json, LetterCase
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass_json(letter_case=LetterCase.PASCAL)
|
|
6
|
+
@dataclass
|
|
7
|
+
class Coordinate:
|
|
8
|
+
x: float = 0
|
|
9
|
+
y: float = 0
|
|
10
|
+
|
|
11
|
+
# def rotate(self, angle: float) -> 'Coordinate':
|
|
12
|
+
# """
|
|
13
|
+
# Rotates this point around the origin by an angle
|
|
14
|
+
# @param angle: angle from x-axis in radians
|
|
15
|
+
# @return: the rotated point
|
|
16
|
+
# """
|
|
17
|
+
# x = self.x * math.cos(angle) - self.y * math.sin(angle)
|
|
18
|
+
# y = self.x * math.sin(angle) + self.y * math.cos(angle)
|
|
19
|
+
# return Coordinate(x, y)
|
|
20
|
+
#
|
|
21
|
+
# def __add__(self, other: 'Coordinate') -> 'Coordinate':
|
|
22
|
+
# return Coordinate(self.x + other.x, self.y + other.y)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from dataclasses_json import dataclass_json, LetterCase
|
|
4
|
+
from zonevu.zonevu.DataModels.Geospatial.Enums import DistanceUnitsEnum
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Projected coordinate system specification
|
|
8
|
+
@dataclass_json(letter_case=LetterCase.PASCAL)
|
|
9
|
+
@dataclass
|
|
10
|
+
class CrsSpec:
|
|
11
|
+
"""
|
|
12
|
+
Used to specify a projected coordinate system
|
|
13
|
+
The EPSG code takes preference if both are specified.
|
|
14
|
+
The Units data member if provided overrides the default units of the specified coordinate system
|
|
15
|
+
"""
|
|
16
|
+
epsg_code: Optional[int] = None
|
|
17
|
+
projection: Optional[str] = None # DotSpatial string for projection
|
|
18
|
+
zone: Optional[str] = None # DotSpatial string for zone
|
|
19
|
+
units: Optional[DistanceUnitsEnum] = None # Distance units override.
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def from_epsg_code(cls, code: int, units: DistanceUnitsEnum = None) -> 'CrsSpec':
|
|
23
|
+
return cls(code, None, None, units)
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def from_names(cls, projection: str, zone: str, units: DistanceUnitsEnum = None) -> 'CrsSpec':
|
|
27
|
+
return cls(None, projection, zone, units)
|
|
28
|
+
|
|
29
|
+
def to_string(self) -> str:
|
|
30
|
+
has_epsg = self.epsg_code is not None
|
|
31
|
+
if has_epsg:
|
|
32
|
+
return 'CRS (EPSG=%s, UNITS=%s)' % (self.epsg_code, self.units)
|
|
33
|
+
else:
|
|
34
|
+
return 'CRS (PROJ=%s, ZONE=%s, UNITS=%s)' % (self.projection, self.zone, self.units)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass_json(letter_case=LetterCase.PASCAL)
|
|
38
|
+
@dataclass
|
|
39
|
+
class CrsEntry:
|
|
40
|
+
id: str # DotSpatial string for projection
|
|
41
|
+
name: str # DotSpatial string for zone
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from typing import List, Tuple
|
|
2
|
+
from functools import reduce
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from dataclasses_json import dataclass_json, LetterCase
|
|
5
|
+
from .GeoLocation import GeoLocation
|
|
6
|
+
from zonevu.zonevu.Services.Error import ZonevuError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass_json(letter_case=LetterCase.PASCAL)
|
|
10
|
+
@dataclass
|
|
11
|
+
class GeoBox:
|
|
12
|
+
"""
|
|
13
|
+
Represents the bounds of a map in geolocations
|
|
14
|
+
"""
|
|
15
|
+
lower_left: GeoLocation
|
|
16
|
+
upper_right: GeoLocation
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def from_locations(cls, locations: List[GeoLocation]):
|
|
20
|
+
if len(locations) == 0:
|
|
21
|
+
raise ZonevuError.local('cannot create a GeoBox from a zero length list of geolocations')
|
|
22
|
+
|
|
23
|
+
lower_left = reduce(lambda l1, l2: GeoLocation.lower_left_of(l1, l2), locations)
|
|
24
|
+
upper_right = reduce(lambda l1, l2: GeoLocation.upper_right_of(l1, l2), locations)
|
|
25
|
+
g = cls(upper_right, lower_left)
|
|
26
|
+
return g
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def from_boxes(cls, b1: 'GeoBox', b2: 'GeoBox') -> 'GeoBox':
|
|
30
|
+
lower_left = GeoLocation.lower_left_of(b1.lower_left, b2.lower_left)
|
|
31
|
+
upper_right = GeoLocation.upper_right_of(b1.upper_right, b2.upper_right)
|
|
32
|
+
return cls(lower_left, upper_right)
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def from_box_list(cls, boxes: List['GeoBox']) -> 'GeoBox':
|
|
36
|
+
largest_box = reduce(lambda b1, b2: GeoBox.from_boxes(b1, b2), boxes)
|
|
37
|
+
return largest_box
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def tuple(self) -> Tuple[float, float, float, float]:
|
|
41
|
+
geobox_tuple = self.lower_left.tuple + self.upper_right.tuple
|
|
42
|
+
return geobox_tuple
|
|
43
|
+
|
|
44
|
+
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from dataclasses_json import dataclass_json, LetterCase
|
|
3
|
+
import math
|
|
4
|
+
from typing import Tuple
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass_json(letter_case=LetterCase.PASCAL)
|
|
8
|
+
@dataclass
|
|
9
|
+
class GeoLocation:
|
|
10
|
+
latitude: float
|
|
11
|
+
longitude: float
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def tuple(self) -> Tuple[float, float]:
|
|
15
|
+
return self.longitude, self.latitude
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
def error(l1: 'GeoLocation', l2: 'GeoLocation') -> float:
|
|
19
|
+
loc_err_lat = l1.latitude - l2.latitude
|
|
20
|
+
loc_err_lon = l1.longitude - l2.longitude
|
|
21
|
+
e = math.sqrt(math.pow(loc_err_lat, 2) + math.pow(loc_err_lon, 2))
|
|
22
|
+
return e
|
|
23
|
+
|
|
24
|
+
def arc_latitude(self) -> float:
|
|
25
|
+
return self.latitude * math.pi / 180
|
|
26
|
+
|
|
27
|
+
def arc_longitude(self) -> float:
|
|
28
|
+
return self.longitude * math.pi / 180
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def bearing(s: 'GeoLocation', f: 'GeoLocation') -> float:
|
|
32
|
+
latitude1 = s.arc_latitude()
|
|
33
|
+
latitude2 = f.arc_latitude()
|
|
34
|
+
longitudeDifference = f.arc_longitude() - s.arc_longitude()
|
|
35
|
+
y = math.sin(longitudeDifference) * math.cos(latitude2)
|
|
36
|
+
x = math.cos(latitude1) * math.sin(latitude2) - \
|
|
37
|
+
math.sin(latitude1) * math.cos(latitude2) * math.cos(longitudeDifference)
|
|
38
|
+
bearing = (math.atan2(y, x) * 180 / math.pi + 360) % 360
|
|
39
|
+
return bearing
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def lower_left_of(cls, l1: 'GeoLocation', l2: 'GeoLocation') -> 'GeoLocation':
|
|
43
|
+
min_latitude = min(l1.latitude, l2.latitude)
|
|
44
|
+
min_longitude = min(l1.longitude, l2.longitude)
|
|
45
|
+
return cls(min_latitude, min_longitude)
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def upper_right_of(cls, l1: 'GeoLocation', l2: 'GeoLocation') -> 'GeoLocation':
|
|
49
|
+
max_latitude = max(l1.latitude, l2.latitude)
|
|
50
|
+
max_longitude = max(l1.longitude, l2.longitude)
|
|
51
|
+
return cls(max_latitude, max_longitude)
|
|
52
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from dataclasses_json import dataclass_json, LetterCase
|
|
3
|
+
from .Coordinate import Coordinate
|
|
4
|
+
from .GeoLocation import GeoLocation
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass_json(letter_case=LetterCase.PASCAL)
|
|
9
|
+
@dataclass
|
|
10
|
+
class GridCorner:
|
|
11
|
+
inline: int
|
|
12
|
+
crossline: int
|
|
13
|
+
p: Coordinate
|
|
14
|
+
lat_long: Optional[GeoLocation] = None
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from dataclasses_json import dataclass_json, LetterCase
|
|
4
|
+
from .GridCorner import GridCorner
|
|
5
|
+
from .Crs import CrsSpec
|
|
6
|
+
from ..Geomodels.SimpleGrid import SimpleGrid
|
|
7
|
+
from .Coordinate import Coordinate
|
|
8
|
+
import math
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class GridAxisInfo:
|
|
13
|
+
start: int
|
|
14
|
+
stop: int
|
|
15
|
+
count: int
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class GridInfo:
|
|
20
|
+
inline_range: GridAxisInfo
|
|
21
|
+
crossline_range: GridAxisInfo
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def num_samples(self) -> int:
|
|
25
|
+
return self.inline_range.count * self.crossline_range.count
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass_json(letter_case=LetterCase.PASCAL)
|
|
29
|
+
@dataclass
|
|
30
|
+
class GridGeometry:
|
|
31
|
+
corner1: GridCorner
|
|
32
|
+
corner2: GridCorner
|
|
33
|
+
corner3: GridCorner
|
|
34
|
+
corner4: GridCorner
|
|
35
|
+
coordinate_system: CrsSpec
|
|
36
|
+
inclination: Optional[float] = None
|
|
37
|
+
geo_inclination: Optional[float] = None
|
|
38
|
+
inline_bin_interval: Optional[float] = None
|
|
39
|
+
crossline_bin_interval: Optional[float] = None
|
|
40
|
+
area: Optional[float] = None
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def grid_info(self) -> GridInfo:
|
|
44
|
+
c1 = self.corner1
|
|
45
|
+
c3 = self.corner3
|
|
46
|
+
inline_info = GridAxisInfo(c1.inline, c3.inline, c3.inline - c1.inline + 1)
|
|
47
|
+
crossline_info = GridAxisInfo(c1.crossline, c3.crossline, c3.crossline - c1.crossline + 1)
|
|
48
|
+
return GridInfo(inline_info, crossline_info)
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def from_simple_grid(cls, grid: SimpleGrid) -> 'GridGeometry':
|
|
52
|
+
"""
|
|
53
|
+
Method to set up a simple grid geometry.
|
|
54
|
+
For inclination = 0, rows are west to east, and columns south to north
|
|
55
|
+
Must provide origin, dx, and dy in distance units of provided CRS
|
|
56
|
+
@param grid: a simple grid definition
|
|
57
|
+
@return: a partially filled GridGeometry, where geolocations and geo-inclination not populated.
|
|
58
|
+
|
|
59
|
+
NOTES:
|
|
60
|
+
We set up here a CW grid geometry, where rows (inlines) run from East to West, and columns (crosslines)
|
|
61
|
+
run South to North.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
radians = math.radians(grid.inclination)
|
|
65
|
+
x_length = grid.dx * grid.num_cols
|
|
66
|
+
y_length = grid.dy * grid.num_rows
|
|
67
|
+
area = x_length * y_length
|
|
68
|
+
if grid.crs.units.lower() == 'meters':
|
|
69
|
+
area = area / 1000000
|
|
70
|
+
else:
|
|
71
|
+
area = area / 27878400
|
|
72
|
+
|
|
73
|
+
c1 = GridCorner(1, 1, grid.origin, None) # LL Un-rotated case
|
|
74
|
+
|
|
75
|
+
c2_offset = Coordinate(x_length, 0)
|
|
76
|
+
c2_point = c2_offset.rotate(radians) + grid.origin
|
|
77
|
+
c2 = GridCorner(grid.num_cols, 1, c2_point, None) # LR Un-rotated case
|
|
78
|
+
|
|
79
|
+
c3_offset = Coordinate(x_length, y_length)
|
|
80
|
+
c3_point = c3_offset.rotate(radians) + grid.origin
|
|
81
|
+
c3 = GridCorner(grid.num_cols, grid.num_rows, c3_point, None) # UR Un-rotated case
|
|
82
|
+
|
|
83
|
+
c4_offset = Coordinate(0, y_length)
|
|
84
|
+
c4_point = c4_offset.rotate(radians) + grid.origin
|
|
85
|
+
c4 = GridCorner(1, grid.num_rows, c4_point, None) # UL Un-rotated case
|
|
86
|
+
|
|
87
|
+
g = cls(c1, c2, c3, c4, grid.crs, grid.inclination, None, grid.dy, grid.dx, area)
|
|
88
|
+
return g
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
File without changes
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from dataclasses_json import dataclass_json, LetterCase, config
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass_json(letter_case=LetterCase.PASCAL)
|
|
7
|
+
@dataclass
|
|
8
|
+
class Conditioning:
|
|
9
|
+
"""
|
|
10
|
+
Represents a ZoneVu geosteering curve conditioning parameter set
|
|
11
|
+
"""
|
|
12
|
+
# Amplitude Range filter
|
|
13
|
+
AmplClip: bool = False
|
|
14
|
+
AmplClipRangeMin: Optional[float] = None
|
|
15
|
+
AmplClipRangeMax: Optional[float] = None
|
|
16
|
+
AmplClipInclusiveNotExclusive: Optional[bool] = None
|
|
17
|
+
|
|
18
|
+
# MD Range filter
|
|
19
|
+
MDClip: bool = False
|
|
20
|
+
MDClipRangeMin: Optional[float] = None
|
|
21
|
+
MDClipRangeMax: Optional[float] = None
|
|
22
|
+
MDClipInclusiveNotExclusive: Optional[bool] = None
|
|
23
|
+
|
|
24
|
+
# Despiking filter.
|
|
25
|
+
Despike: bool = False
|
|
26
|
+
VarianceThreshold: Optional[
|
|
27
|
+
float] = None # Absolute variance that is acceptable as a number in the units of the curve data.
|
|
28
|
+
DespikeLen: Optional[int] = None # Should be an odd number. Number of pts in despiking filter.
|
|
29
|
+
|
|
30
|
+
# Interpolation filter.
|
|
31
|
+
Interpolate: bool = False
|
|
32
|
+
MaxGap: Optional[int] = None
|
|
33
|
+
|
|
34
|
+
# SmoothingFilter filter.
|
|
35
|
+
Smooth: bool = False
|
|
36
|
+
SmoothingLen: Optional[int] = None # Number of points in smoothing filter.
|
|
37
|
+
|
|
38
|
+
# Amplitude normalization filter.
|
|
39
|
+
Normalize: bool = False
|
|
40
|
+
Bias: Optional[float] = None # Amplitude shift
|
|
41
|
+
Scalar: Optional[float] = None # Amplitude multiplier
|
|
42
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from dataclasses_json import dataclass_json, LetterCase, config
|
|
4
|
+
from ..DataModel import DataModel
|
|
5
|
+
from .Conditioning import Conditioning
|
|
6
|
+
from strenum import StrEnum
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CurveGroupRoleEnum(StrEnum):
|
|
10
|
+
Image = 'Image'
|
|
11
|
+
Litho = 'Litho'
|
|
12
|
+
Splice = 'Splice'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass_json(letter_case=LetterCase.PASCAL)
|
|
16
|
+
@dataclass
|
|
17
|
+
class CurveDef(DataModel):
|
|
18
|
+
"""
|
|
19
|
+
Represents a ZoneVu geosteering curve definition
|
|
20
|
+
"""
|
|
21
|
+
curve_id: Optional[int] = None
|
|
22
|
+
curve_group_id: Optional[int] = None
|
|
23
|
+
role: Optional[CurveGroupRoleEnum] = None
|
|
24
|
+
active: bool = False
|
|
25
|
+
conditioning: Optional[Conditioning] = None
|
|
26
|
+
|