A Coordinates Transformation Method for Standard Blocks
Posted onEdited on
1. Introduction
In offshore petroleum exploration practice, all the coordinates of blocks designed by geologists serving a particular purpose should be provided in Degree-Minute-Second format, in which degree uses 2 or 3 digits, minute uses 2 digits, and second uses 2 digits in one of four options 00, 15, 30, 45 ( increment with a quarter of a minute ). All coordinates should list in clockwise order starts from the name vertex (the most northeast vertex). Such a block is called a “standard block”.
Design blocks is a daily work in resource exploration industry. Blocks serve in many purpose, including but not limited to administration (relinquish part of a block, dissolve several blocks into one block), block bid campaign, etc.
Geologists usually draft blocks on a projected geological setting basemap. Unfortunately some mapping software doesn’t have snap functionality. This software can only export polygons (drafted blocks) in projected XY format. At least it can display a geodetic reference grid in 15 seconds.
A new workflow is designed to deal with the blocks:
Geologists design blocks with vertices of which are visually but not precisely spot on the nodes of a 15-second geodetic reference grid.
All blocks reside in a layer, with valid identifiers.
Export the design layer of blocks in projection format as text file.
Write a program to read the file and generate standard blocks.
3. Implementation
This rudimentary implementation of coordinate transform tool in Python is called “transco”. The name stands for “TRANSformation of COordinates”.
Transco uses pyproj to do actual transformation. Pyproj is a python interface to PROJ. Shaplely is also used in this program. Shapely is a python wrapper of GEOS.
3.1 class
Transco use 3 classes to represent the data: Layer, Block and Point. points are property of Block instance, and blocks are property of Layer instance.
classBlock: """A raw block resides in a Layer object."""
def__init__(self, layer, block): self.layer = layer self.location = self.layer.location self.order = 0 self.crs = self.layer.crs self.transformer = self.layer.transformer name_str = block[0][0] try: name, order = name_str.split("--") order = int(order) if name.strip(): self.name = name.strip().replace("/", "").replace("\\", "") if order >= 1: self.order = order except: self.name = name_str.rstrip("--").strip().replace("/", "").replace("\\", "") ifnot self.name: raise Exception("Failed to create a name for this Block") self._points = [] for p in block: point = Point(self, p) self._points.append(point) self.__regular_points() self.__unparallel_points() self.get_code_name()
@property defpoints(self): return self._points
The block object gets CRS information from its Layer object.
Like before, the point instance gets CRS from its Block object.
3.2 Function
So the basic idea is, original XY coordinates are transformed into Degrees, then snap to the nearest geodetic reference grid node by _offset method.
There are some functions to modidy the blocks, like rotate points if it’s not clockwise, remove reduandant points, check errors, compute block name by spatial relationship, etc.
defd2dms(x): """ Transform a single float degree value to DMS format. return a tuple of degree, minute, second in float. >>> d2dms(113.5) (113.0, 30.0, 0.0) >>> d2dms(20.254166667) (20.0, 15.0, 15.000001199999247) """ d_decimal, d = math.modf(x) m_decimal, m = math.modf(d_decimal * 60) s = m_decimal * 60 return d, m, s
The last setup is defining functions to call methods of objects.
defavailable_crs(defined_crs): all_crs = {} for key, value in defined_crs.items(): if value: all_crs[key] = value return all_crs
defshow_help(**kw): print(__desc__) print("Version {}".format(__version__)) print("Author: ", __author__) print(__transco_help__) location = kw.get("location", None) if location: try: crs = get_crs("proj", "wgs84utm", location) except: print("Failed to get CRS.") raise if crs: print("Current CRS:") print(crs.name) print("Area of Use:") print(crs.area_of_use)
defmain(): parser = argparse.ArgumentParser(description=__desc__) parser.add_argument( "file", help="the input file, default is 'df_design.txt'.", nargs="?" ) parser.add_argument( "location", help="the location for projection selection, default is 'location'.", nargs="?", ) parser.add_argument( "-a", "--all", action="store_true", help="save all output files" ) parser.add_argument("--version", action="store_true", help="show version") parser.add_argument("--allcrs", action="store_true", help="show all available CRS") args = parser.parse_args() try: ...
After that, I can call the main function in CLI, or call functions and calss directly in a interactive python environment, or use it in a Jupyter notebook.