Пример #1
0
    def run_pipeline(self, xx, yy, zz, incrs, region_name):
        """
        MLLW to NAVD88
        """

        req_hcrs_epsg = 26919
        req_vcrs_epsg = 'mllw'
        out_vcrs_epsg = 5703

        # parse the provided CRS
        cmpd_incrs = CompoundCRS.from_wkt(incrs.to_wkt())
        if len(cmpd_incrs.sub_crs_list) == 2:
            inhcrs, invcrs = cmpd_incrs.sub_crs_list
            assert inhcrs.to_epsg() == req_hcrs_epsg
            assert invcrs.to_epsg() == req_vcrs_epsg
        elif not cmpd_incrs.is_vertical:
            assert incrs.to_epsg() == req_hcrs_epsg

        # build the output crs
        comp_crs = CompoundCRS(
            name="NAD83 UTM19 + NAVD88",
            components=[f"EPSG:{req_hcrs_epsg}", f"EPSG:{out_vcrs_epsg}"])
        # get the transform at the sparse points
        transformer = Transformer.from_pipeline(f'proj=pipeline \
                                                  step inv proj=utm zone=19 \
                                                  step inv proj=vgridshift grids={self.config.grid_files[f"{region_name}/mllw.gtx"]} \
                                                  step proj=vgridshift grids={self.config.grid_files[f"{region_name}/tss.gtx"]} \
                                                  step proj=utm zone=19')

        result = transformer.transform(xx=xx, yy=yy, zz=zz)
        self.config.logger.debug('Applying pipeline: {}'.format(transformer))
        return result, comp_crs
Пример #2
0
def test_compund_crs():
    vertcrs = VerticalCRS(
        name="NAVD88 height",
        datum="North American Vertical Datum 1988",
        vertical_cs=VerticalCS(),
        geoid_model="GEOID12B",
    )
    projcrs = ProjectedCRS(
        name="NAD83 / Pennsylvania South",
        conversion=LambertConformalConic2SPConversion(
            latitude_false_origin=39.3333333333333,
            longitude_false_origin=-77.75,
            latitude_first_parallel=40.9666666666667,
            latitude_second_parallel=39.9333333333333,
            easting_false_origin=600000,
            northing_false_origin=0,
        ),
        geodetic_crs=GeographicCRS(datum="North American Datum 1983"),
        cartesian_cs=Cartesian2DCS(),
    )
    compcrs = CompoundCRS(
        name="NAD83 / Pennsylvania South + NAVD88 height", components=[projcrs, vertcrs]
    )
    assert compcrs.name == "NAD83 / Pennsylvania South + NAVD88 height"
    assert compcrs.type_name == "Compound CRS"
    assert compcrs.sub_crs_list[0].type_name == "Projected CRS"
    assert compcrs.sub_crs_list[1].type_name == "Vertical CRS"
Пример #3
0
def test_compund_crs__from_methods():
    crs = CompoundCRS.from_string("EPSG:4326+5773")
    with pytest.raises(CRSError, match="Invalid type"):
        CompoundCRS.from_epsg(4326)
    assert_maker_inheritance_valid(crs, CompoundCRS)
    with pytest.raises(CRSError, match="Invalid type"):
        CompoundCRS.from_proj4("+proj=longlat +datum=WGS84 +vunits=m")
    assert_maker_inheritance_valid(CompoundCRS.from_user_input(crs),
                                   CompoundCRS)
    assert_maker_inheritance_valid(CompoundCRS.from_json(crs.to_json()),
                                   CompoundCRS)
    assert_maker_inheritance_valid(
        CompoundCRS.from_json_dict(crs.to_json_dict()), CompoundCRS)
Пример #4
0
    def _update_and_build_compound(self):
        """
        Create a compound crs object attribute and mark the object as valid if there is a horizontal
        crs, a vertical crs, and if the vertical crs includes the pipeline and regions in the remarks.

        Returns
        -------
        None.

        """
        if self._vert and self._regions:
            self._vert, self._vyperdatum_str, self._pipeline_str = build_valid_vert_crs(
                self._vert, self._regions, self.datum_data)
        if self._hori and self._vert and self._valid_vert():
            compound_name = f'{self._hori.name} + {self._vert.name}'
            self.ccrs = CompoundCRS(compound_name, [self._hori, self._vert])
            self._is_valid = True
            vert_axis = self._vert.axis_info[0]
            if vert_axis.direction == 'up':
                self._is_height = True
            elif vert_axis.direction == 'down':
                self._is_height = False
            else:
                raise ValueError('no direction defined for the vertical crs.')
Пример #5
0
class VyperPipelineCRS:
    """
    A container for developing and validating compound crs objects built around pyproj crs objects
    and the vypercrs.VerticalPipelineCRS object.
    """
    def __init__(self,
                 datum_data: object,
                 new_crs: Union[str, int, tuple] = None,
                 regions: [str] = None):
        self.datum_data = datum_data
        self.vdatum_version = datum_data.vdatum_version
        self._is_valid = False
        self._ccsr = None
        self._vert = None
        self._hori = None
        self._regions = []
        self._vyperdatum_str = None
        self._pipeline_str = None
        self._is_height = None
        if new_crs is not None:
            self.set_crs(new_crs, regions)

    def set_crs(self, new_crs: Union[str, int, tuple], regions: [str] = None):
        """
        Set the object vertical and / or horizontal crs using either an epsg code or wkt.
        Regions are also optionally set with a list of VDatum region strings.

        Parameters
        ----------
        new_crs : Union[str, int, tuple]
            A wkt string or a vyperdatum vertical datum definition string or an epsg code
            may be provided either on its own or as a tuple pair.  While the order (horizontal or vertical)
            does not matter, if they contain information for the same frame (horizontal or vertical) the
            second will overwrite the first.
            
            Once the provided object datums are set, the object compound attribute is set if the horizontal,
            verical, and region information is available.
            
        regions : [str], optional
            A list of VDatum region strings.  These values will overwrite the values contained in the vertical
            WKT if they exist. The default is None.

        Raises
        ------
        ValueError
            If a tuple of length greater than two is provided or if the provided value(s) are not a string
            or an integer.

        Returns
        -------
        None.

        """
        # check the input type
        if type(new_crs) == tuple:
            if len(new_crs) > 2:
                len_crs = len(new_crs)
                raise ValueError(
                    f'The provided crs {new_crs} is {len_crs} in length but should have a length of two.'
                )
        else:
            new_crs = (new_crs, )
        # create pyproj crs based on the input type
        for entry in new_crs:
            if type(entry) == str:
                crs_str = entry
                if entry.lower() in datum_definition:
                    if entry == 'ellipse' and self._hori:
                        entry = f'{self._hori.name}_ellipse'
                    tmp_crs = VerticalPipelineCRS(datum_data=self.datum_data,
                                                  vert_datum_name=entry)
                    crs_str = tmp_crs.to_wkt()
                crs = CRS.from_wkt(crs_str)
                self._set_single(crs)
            elif type(entry) == int:
                crs = CRS.from_epsg(entry)
                self._set_single(crs)
            else:
                raise ValueError(
                    f'The crs description type {entry} is not recognized.')

        if regions:
            self._regions = regions

        self._update_and_build_compound()

    def update_regions(self, regions: [str]):
        """
        Update the regions object attribute.

        Parameters
        ----------
        regions : [str]
            DESCRIPTION.

        Returns
        -------
        None.

        """
        self._regions = regions

        self._update_and_build_compound()

    def _set_single(self, crs: CRS):
        """
        Assign the provided pyproj crs object to the object attribute representing either the
        vertical crs or the horizontal crs.  If the object contains a new vertical crs and
        contains the remarks for the regions, the regions are also extracted and assigned to
        the associated object attribute.

        Parameters
        ----------
        crs : pyproj.crs.CRS
            The new crs.

        Returns
        -------
        None.

        """
        new_vert = False
        if crs_is_compound(crs):
            self.ccrs = crs
            self._hori = crs.sub_crs_list[0]
            self._vert = crs.sub_crs_list[1]
            new_vert = True
        elif crs.is_geocentric:
            raise ValueError(
                'Geocentric cooridinate systems are not supported.')
        elif len(crs.axis_info) > 2:
            # assuming 3D crs if not compound but axis length is > 2. Break into compound crs.
            if crs.to_epsg() == NAD83_3D:  # if 3d nad83, go to 2d nad83
                self._hori = CRS.from_epsg(NAD83_2D)
            elif crs.to_epsg() == ITRF2008_3D:  # 3d wgs84/itrf2008, go to 2d
                self._hori = CRS.from_epsg(ITRF2008_2D)
            elif crs.to_epsg() == ITRF2014_3D:  # 3d itrf2014, go to 2d
                self._hori = CRS.from_epsg(ITRF2014_2D)
            else:
                raise NotImplementedError(
                    f'A 3D coordinate system was provided that is not yet implemented: {crs.to_epsg()}'
                )
            self._vert = VerticalPipelineCRS(
                datum_data=self.datum_data,
                vert_datum_name=f'{self._hori.name}_ellipse').to_crs()
            new_vert = True
        elif crs.is_vertical:
            self._vert = crs
            new_vert = True
        else:
            self._hori = crs
        if new_vert:
            # get the regions from the wkt if available
            tmp_vert = VerticalPipelineCRS(datum_data=self.datum_data)
            tmp_vert.from_wkt(self._vert.to_wkt())
            if len(tmp_vert.regions) > 0:
                self._regions = tmp_vert.regions
            # ideally we would pull the vyperdatum name string here too

    def _update_and_build_compound(self):
        """
        Create a compound crs object attribute and mark the object as valid if there is a horizontal
        crs, a vertical crs, and if the vertical crs includes the pipeline and regions in the remarks.

        Returns
        -------
        None.

        """
        if self._vert and self._regions:
            self._vert, self._vyperdatum_str, self._pipeline_str = build_valid_vert_crs(
                self._vert, self._regions, self.datum_data)
        if self._hori and self._vert and self._valid_vert():
            compound_name = f'{self._hori.name} + {self._vert.name}'
            self.ccrs = CompoundCRS(compound_name, [self._hori, self._vert])
            self._is_valid = True
            vert_axis = self._vert.axis_info[0]
            if vert_axis.direction == 'up':
                self._is_height = True
            elif vert_axis.direction == 'down':
                self._is_height = False
            else:
                raise ValueError('no direction defined for the vertical crs.')

    def _valid_vert(self) -> bool:
        """
        See if there is a vertical crs in the object and if it has regions and
        and a pipeline in the remarks.

        Returns
        -------
        bool
            True if there is a vertical crs object and the remarks include
            the pipeline and regions.

        """
        valid = False
        if self._vert and self._vert.remarks:
            have_region = 'regions' in self._vert.remarks
            have_pipeline = 'pipeline' in self._vert.remarks
            have_version = 'vyperdatum' in self._vert.remarks
            have_datum = 'base_datum' in self._vert.remarks
            if have_region and have_pipeline and have_version and have_datum:
                valid = True
        return valid

    @property
    def is_valid(self):
        return self._is_valid

    @property
    def vertical(self):
        if self._valid_vert():
            return self._vert
        else:
            return None

    @property
    def horizontal(self):
        return self._hori

    @property
    def regions(self):
        return self._regions

    @property
    def vyperdatum_str(self):
        return self._vyperdatum_str

    @property
    def pipeline_string(self):
        return self._pipeline_str

    @property
    def is_height(self):
        return self._is_height

    def to_wkt(self):
        if self._is_valid:
            return self.ccrs.to_wkt()
        else:
            return None