コード例 #1
0
ファイル: processing.py プロジェクト: johannjacobsohn/geonum
 def det_profile(self, interpolate=True, resolution=5.0, itp_type="linear"):
     """Determines the elevation profile
     
     Searches the closest tiles in the topo data grid for both observer and
     endpoint and based on these two points the elevation profile is
     extracted using a :class:`LineOnGrid` object (which extracts altitudes 
     from the topo data along the connection vector of the 2 points using 
     2D spline intepolation)
     
     Parameters
     ----------
     interpolate : bool
         if True, the profile is interpolated to a certain horizontal resolution
     resolution : float
         desired grid resolution in m for interpolation. Interpolation is 
         performed if :attr:`interpolate` is True and if the actual 
         resolution of the topo data is smaller than input, else, nothing is done
     itp_type : str
         interpolation type (e.g. "linear", "cubic")
     
     Returns
     -------
             
     """
     data = self.topo_data
     idx_lon_0 = argmin(abs(data.lons - self.observer.lon.decimal_degree))
     idx_lat_0 = argmin(abs(data.lats - self.observer.lat.decimal_degree))
     idx_lon_1 = argmin(abs(data.lons - self.endpoint.lon.decimal_degree))
     idx_lat_1 = argmin(abs(data.lats - self.endpoint.lat.decimal_degree))
     
     self.line = l = LineOnGrid(idx_lon_0, idx_lat_0, 
                                idx_lon_1, idx_lat_1)
     
     z = l.get_line_profile(data.data)
     self._observer_topogrid = GeoPoint(data.lats[idx_lat_0],\
                             data.lons[idx_lon_0], topo_data = data)
     self._endpoint_topogrid = GeoPoint(data.lats[idx_lat_1],\
                             data.lons[idx_lon_1], topo_data = data)
     dists = linspace(0, self.dist_hor, l.length + 1)
     if interpolate:
         try:
             res0 = (dists[1] - dists[0]) * 1000
             fac = int(ceil(res0 / resolution))
             if fac > 1:
                 fz = interp1d(dists, z, kind=itp_type)
                 dists = linspace(0, self.dist_hor, l.length * fac)
                 z = fz(dists)
         except Exception as e:
             warn("Failed to perform interpolation of retrieved elevation "
                  "profile. Error msg: %s" %repr(e))
     self.dists = dists
     self.profile = z
コード例 #2
0
def find_viewing_direction(meas_geometry, draw_result=True):
    """Correct viewing direction using location of Etna SE crater.

    Defines location of Etna SE crater within images (is plotted into current
    plume onband image of dataset) and uses its geo location to retrieve the
    camera viewing direction

    :param meas_geometry: :class:`MeasGeometry` object

    """
    # Position of SE crater in the image (x, y)
    se_crater_img_pos = [806, 736]

    # Geographic position of SE crater (extracted from Google Earth)
    # The GeoPoint object (geonum library) automatically retrieves the altitude
    # using SRTM data
    se_crater = GeoPoint(37.747757, 15.002643, name="SE crater")

    print("Retrieved altitude SE crater (SRTM): %s" % se_crater.altitude)

    # The following method finds the camera viewing direction based on the
    # position of the south east crater.
    new_elev, new_azim, _, basemap =\
        meas_geometry.find_viewing_direction(pix_x=se_crater_img_pos[0],
                                             pix_y=se_crater_img_pos[1],
                                             # for uncertainty estimate
                                             pix_pos_err=100,
                                             geo_point=se_crater,
                                             draw_result=draw_result,
                                             update=True)  # overwrite settings

    print("Updated camera azimuth and elevation in MeasGeometry, new values: "
          "elev = %.1f, azim = %.1f" % (new_elev, new_azim))

    return meas_geometry, basemap
コード例 #3
0
    def create_test_data(self):
        """Create exemplary test data set"""
        source = GeoPoint(37.751005, 14.993435, name="Etna")
        instrument = GeoPoint(37.765755, 15.016696, name="Observatory")
        self.add_geo_points(source, instrument)
        self.set_borders_from_points()
        plume = GeoVector3D(azimuth=83,
                            dist_hor=self.magnitude,
                            elevation=0,
                            anchor=source,
                            name="plume")
        view_dir = GeoVector3D(azimuth=160,
                               dist_hor=self.magnitude,
                               elevation=8,
                               anchor=instrument,
                               name="cfov")

        self.add_geo_vectors(plume, view_dir)
コード例 #4
0
 def new_geo_point(self, *args, **kwargs):
     """Create new geo_point and add to collection
     
     :param **kwargs: see :class:`GeoPoint` for initiation info
     """
     try:
         self.add_geo_point(GeoPoint(*args, **kwargs))
     except (TypeError, ValueError):
         return
     except:
         raise Exception(print_exc())
コード例 #5
0
def find_view_dir(geom):
    """Perform a correction of the viewing direction using crater in img.

    :param MeasGeometry geom: measurement geometry
    :param str which_crater: use either "ne" (northeast) or "se" (south east)
    :return: - MeasGeometry, corrected geometry
    """
    # Use position of NE crater in image
    posx, posy = 1051, 605  # pixel position of NE crate in image
    # Geo location of NE crater (info from Google Earth)
    ne_crater = GeoPoint(37.754788, 14.996673, 3287, name="NE crater")

    geom.find_viewing_direction(pix_x=posx, pix_y=posy, pix_pos_err=100,
                                geo_point=ne_crater, draw_result=True)
    return geom
コード例 #6
0
def show_coordinate(geo_point=None,
                    lat_pt=None,
                    lon_pt=None,
                    extend_km=10.0,
                    *args,
                    **kwargs):
    """Draw overview map for a given point
    
    Parameters
    ----------
    geo_point : GeoPoint
        Geographical location around which overview map is drawn
    lat_pt : float
        Latitude of geographical location around which overview map is 
        drawn (only considered if :attr:`geo_point` is invalid)
    lon_pt : float
        Longitude of geographical location around which overview map is 
        drawn (only considered if :attr:`geo_point` is invalid)
    extend_km : float
        map extend in km around considered geolocation
    *args :
        non-keyword arguments passed to :func:`plot_2d` of the 
        :class:`GeoSetup` instance that is created in order to draw the map
    
    Returns
    -------
    Map
        instance of :class:`geonum.Map`
        
    """
    if not isinstance(geo_point, GeoPoint):
        try:
            geo_point = GeoPoint(lat=lat_pt, lon=lon_pt)
        except:
            raise TypeError("Invalid input, please provide information "
                            "about location of GeoPoint")
    stp = GeoSetup(points=[geo_point])
    stp.set_borders_from_points(extend_km=extend_km)
    m = stp.plot_2d(*args, **kwargs)
    return m
コード例 #7
0
    def set_borders_from_points(self, extend_km=1, to_square=True):
        """Set range of setup (lower left and upper right coordinates) 
        considering all points in this collection
        
        :param float extend_km: extend range from the outermost points by 
            this number in km
        :param float to_square (True): extend the shorter base side to the 
            size of the longer one (quadratic range)
        """
        lats, lons = self._all_lats_lons()
        if not len(lats) > 0:
            #print "Borders could not be initiated, no objects found..."
            return False

        lat_ll, lon_ll, lat_tr, lon_tr = (nanmin(lats), nanmin(lons),
                                          nanmax(lats), nanmax(lons))

        pll, ptr = GeoPoint(lat_ll, lon_ll, 0.0), GeoPoint(lat_tr, lon_tr, 0.0)

        if to_square:
            v = ptr - pll
            add = (abs(v.dx) - abs(v.dy)) / 2
            if add > 0:  #E/W extend (dx) is smaller than N/S extend (dy)
                pll = pll.offset(azimuth=180, dist_hor=add)
                ptr = ptr.offset(azimuth=0, dist_hor=add)
            else:
                pll = pll.offset(azimuth=270, dist_hor=-add)
                ptr = ptr.offset(azimuth=90, dist_hor=-add)

        self.set_geo_point(
            "ll", pll.offset(azimuth=-135,
                             dist_hor=float(extend_km),
                             name="ll"))

        self.set_geo_point(
            "tr", ptr.offset(azimuth=45, dist_hor=float(extend_km), name="tr"))
        return True
コード例 #8
0
"""geonum example script 1

Introduction into the geonum base classes GeoPoint and GeoVector3D
"""

from geonum.base import GeoPoint
from SETTINGS import OPTPARSE
from numpy import testing as npt
from matplotlib.pyplot import show
### Create 2 GeoPoint objects

# Summit region of Mt. Etna
p1 = GeoPoint(lat=37.751005, lon=14.993435, altitude=3264.0, name="Etna")

# Position of volcanological observatory of Mt. Etna
p2 = GeoPoint(37.765755, 15.016696, name="Observatory")

# Print info (string represenation of both points")
print(("Point1: %s, point 2: %s" % (p1, p2)))

# Get and print the connection vector of both points
connection_vector = p2 - p1

print(connection_vector)

# Import script options
(options, args) = OPTPARSE.parse_args()

# If applicable, do some tests. This is done only if TESTMODE is active:
# testmode can be activated globally (see SETTINGS.py) or can also be
# activated from the command line when executing the script using the
コード例 #9
0
ファイル: processing.py プロジェクト: johannjacobsohn/geonum
class ElevationProfile(object):
    """Class for calculating elevation profiles
    
    The profile is calculated between two geo point objects using a 
    provided topographic data grid which must cover the range spanned by 
    both points
    
    Parameters
    ----------
    topo_data : TopoData 
        topography data object
    observer : :obj:`GeoPoint` or :obj:`LatLon`
        starting point of profile 
    endpoint : :obj:`GeoPoint` or :obj:`LatLon`
        stop point of profile 
    interpolate : bool
        if True, the profile is interpolated to a certain horizontal resolution
    resolution : float
        desired grid resolution in m for interpolation. Interpolation is 
        performed if :attr:`interpolate` is True and if the actual 
        resolution of the topo data is smaller than input, else, nothing is done
    itp_type : str
        interpolation type (e.g. "linear", "cubic")
    """
# =============================================================================
#     def __new__(cls, topo_data, observer, endpoint, interpolate=True, 
#                 resolution=5.0, itp_type="linear"):
#         """These objects strictly can only be created with the right input, 
#         
#         For input specs see __init__ method
#         """
#         if not isinstance(topo_data, TopoData):
#             raise ValueError("ElevationProfile instance could not be created\n"
#                 "Wrong input type: topo_data, need TopoData object...")
#         for p in [observer, endpoint]:
#             if not any([p.type() == x for x in ["LatLon", "GeoPoint"]]):
#                 raise ValueError ("ElevationProfile instance could not be "
#                     "created\nWrong input type: observer/endpoint, need GeoPoint "
#                     "or LatLon object...")
#             elif not topo_data.includes_coordinate(p.lat.decimal_degree,
#                                                    p.lon.decimal_degree):
#                 raise ValueError ("ElevationProfile instance could not be "
#                     "created\nWrong input point not covered by input topodata")
#         
#         return super(ElevationProfile, cls).__new__(cls, topo_data, observer,
#                                                     endpoint, interpolate,
#                                                     resolution, 
#                                                     itp_type)
# =============================================================================
        
    def __init__(self, topo_data, observer, endpoint, interpolate=True, 
                 resolution=5.0, itp_type="linear"):
     
        self.topo_data = topo_data
        self.observer = observer #: coordinate of observer (start of profile)
        self.endpoint = endpoint #: coordinate of endpoint of profile
        
        self._observer_topogrid = None #: closest to observer on topo data grid
        self._endpoint_topogrid = None #: closest to endpoint in topo data grid
        
        # In the following parameters the results will be stored
        self.line = None
        self.profile = None
        self.dists = None

        try:
            self.det_profile(interpolate, resolution, itp_type)
        except Exception as e:
            warn("Failed to compute elevation profile. Error msg: %s"
                 %repr(e))
        
    @property
    def dist_hor(self):
        """Returns the horizontal distance between the 2 points"""
        return (self._endpoint_topogrid - self._observer_topogrid).dist_hor
        
    
    @property
    def azimuth(self):
        """Returns the azimuth angle of the profile"""
        return (self._endpoint_topogrid - self._observer_topogrid).azimuth
        
    @property
    def resolution(self):
        """Get profile x (distance) resolution (averaged from distance array)
        
        .. note::

            Only works if profile was already determined
            
        """
        return abs((self.dists[1:] - self.dists[:-1]).mean())
    
    @property
    def gradient(self):
        """Return gradient of profile
        
        Uses numpy function ``gradient``
        """
        return gradient(self.profile)
    
    @property
    def slope(self):
        """Returns slope of profile 
        
        Determines dx and dy arrays and returns slope vector::
        
            slope = dy / dx = gradient(self.profile) / 
                (gradient(self.dists) * 1000.0)
            
        """
        return gradient(self.profile) / (gradient(self.dists) * 1000)
    
    def slope_angles(self, decimal_degrees=True):
        """Returns slope angle of profile (in each sample point)
        
        :param bool decimal_degrees: rad or degrees (default True)
        """
        a = tan(self.slope)
        if decimal_degrees:
            a = rad2deg(a)
        return a
        
    def slope_angle(self, dist):
        """Returns slope angle of profile at input distance
        
        :param float dist: distance in km
        """
        idx = argmin(abs(self.dists - dist))
        return self.slope_angles()[idx]
        
    def det_profile(self, interpolate=True, resolution=5.0, itp_type="linear"):
        """Determines the elevation profile
        
        Searches the closest tiles in the topo data grid for both observer and
        endpoint and based on these two points the elevation profile is
        extracted using a :class:`LineOnGrid` object (which extracts altitudes 
        from the topo data along the connection vector of the 2 points using 
        2D spline intepolation)
        
        Parameters
        ----------
        interpolate : bool
            if True, the profile is interpolated to a certain horizontal resolution
        resolution : float
            desired grid resolution in m for interpolation. Interpolation is 
            performed if :attr:`interpolate` is True and if the actual 
            resolution of the topo data is smaller than input, else, nothing is done
        itp_type : str
            interpolation type (e.g. "linear", "cubic")
        
        Returns
        -------
                
        """
        data = self.topo_data
        idx_lon_0 = argmin(abs(data.lons - self.observer.lon.decimal_degree))
        idx_lat_0 = argmin(abs(data.lats - self.observer.lat.decimal_degree))
        idx_lon_1 = argmin(abs(data.lons - self.endpoint.lon.decimal_degree))
        idx_lat_1 = argmin(abs(data.lats - self.endpoint.lat.decimal_degree))
        
        self.line = l = LineOnGrid(idx_lon_0, idx_lat_0, 
                                   idx_lon_1, idx_lat_1)
        
        z = l.get_line_profile(data.data)
        self._observer_topogrid = GeoPoint(data.lats[idx_lat_0],\
                                data.lons[idx_lon_0], topo_data = data)
        self._endpoint_topogrid = GeoPoint(data.lats[idx_lat_1],\
                                data.lons[idx_lon_1], topo_data = data)
        dists = linspace(0, self.dist_hor, l.length + 1)
        if interpolate:
            try:
                res0 = (dists[1] - dists[0]) * 1000
                fac = int(ceil(res0 / resolution))
                if fac > 1:
                    fz = interp1d(dists, z, kind=itp_type)
                    dists = linspace(0, self.dist_hor, l.length * fac)
                    z = fz(dists)
            except Exception as e:
                warn("Failed to perform interpolation of retrieved elevation "
                     "profile. Error msg: %s" %repr(e))
        self.dists = dists
        self.profile = z
     
    def get_altitudes_view_dir(self, elev_angle, view_above_topo_m=1.5):
        """Get vector containing altitudes for a viewing direction 
        
        The viewing direction is specified by the azimuth angle of the 
        connection vector between observer and endpoint, the elevation angle
        needs to be specified via the input parameters, and the start point
        (first elevation value) corresponds to the altitude at the observer
        position plus an offset in m which can be specified.
        
        :param float elev_angle: elevation angle of viewing direction
        :param float view_above_topo_m (1.5): altitude offset of start point 
            in m
        :return:
            - vector with altitude values (same length as ``self.profile``)
            
        """
        return (1000 * tan(radians(elev_angle)) * self.dists + 
                self.profile[0] + view_above_topo_m)
    
    def find_horizon_elev(self, elev_start=0.0, elev_stop=60.0, step_deg=0.1,
                          **kwargs):
        """Find first elevation angle which does not intersect with topo
        
        :param float elev_start: start search elevation angle
        :param float elev_stop: stop search elevation angle
        :param float step_deg: angle step for search (coarser is faster)
        :param **kwargs: additional keyword agruments passed to 
            :func:`get_first_intersection`
        """
        elevs = arange(elev_start, elev_stop + step_deg, step_deg)
        elev_sects = []
        dists_sects = []
        for elev in elevs:
            (dist, 
             dist_err, 
             intersect, 
             view_elevations, 
             _) = self.get_first_intersection(elev, **kwargs)
            if dist is None:
                return elev, elev_sects, dists_sects
            else:
                dists_sects.append(dist), elev_sects.append(elev)
        raise Exception("Unexpected exception..")
        
    def get_first_intersection(self, elev_angle, view_above_topo_m=1.5,
                               min_dist=None, local_tolerance=3, plot=False):
                                
        """Finds first intersection of a viewing direction with topography
        
        Start point of the viewing vector is the observer position (or more
        accurately, the center position of the closest topography tile in 
        the topography data set). The relative altitude of the start point 
        with respect to the topography altitude at the observer position 
        can be controlled on input (arg ``view_above_topo_m``) as well as 
        the elevation angle of the viewing direction. The azimuth angle 
        corresponds to the profile azimuth (i.e. azimuth between observer 
        position and endpoint of this profile).
        
        The signal analysed for finding the intersection is a vector 
        containing relative elevation positions of the viewing direction 
        with respect to the profile altitude::
        
            diff_signal = self.get_altitudes_view_dir - self.profile
            
        The first intersection of the viewing direction with the topography
        is identified by the first zero crossing of this ``diff_signal``.
        
        :param float elev_angle: elevation angle of viewing direction (in 
            decimal degrees)
        :param int view_above_topo_m: altitude offset of start point 
            (``observer``) in m (default: 1.5)
        :param float min_dist: minimum distance (in km) of first 
            considered intersection with topography from observer. If None, 
            use 1% of distance between ``observer`` and ``endpoint``.
        :param float local_tolerance: tolerance factor to estimate 
            distance uncertainty based on topo grid resolution  (default: 3)
        :param bool plot: creates a plot of the profile including the
            intersection information

        
        """
         
        if min_dist == None:
            min_dist = self.dist_hor*.01
        max_diff = self.resolution * 1000 
        view_elevations = self.get_altitudes_view_dir(elev_angle,
                                                      view_above_topo_m)
        
        #: determine the difference signal
        diff_signal = view_elevations - self.profile 
        #: First condition: consider only points in certain distance from observer
        cond1 = self.dists > min_dist
        #: Second condition: consider only "close to zero" points (this might be
        #: redundant, I'll leave it for now because it works)
        cond2 = abs(diff_signal) < max_diff 
        
        #: create array with all distances matching the 2 conditions
        dists_0 = self.dists[cond1 * cond2] 
        dist, dist_err, intersect = None, None, None
        #: relax condition 2 if nothing was found 
        if not len(dists_0) > 0:
            max_diff = self.resolution * 1000 * local_tolerance
            cond2 = abs(diff_signal) < max_diff
            dists_0 = self.dists[cond1 * cond2]
                   
        try:
            #: get all diff vals matching the 2 conditions
            diff_vals = diff_signal[cond1 * cond2]
            #: get the index of the first zero crossing
            first_idx = where(diff(sign(diff_vals)))[0][0]
            
            #: get distance and local tolerance value
            dist, tol = dists_0[first_idx], self.resolution * local_tolerance
            #: make mask to access all distances within tolerance range
            cond4 = logical_and(dist - tol <= dists_0, dist + tol >= dists_0)
            #: estimate distance error from standard deviation of all 
            #: distances within tolerance range
            dist_err = dists_0[cond4].std()
            
            #: create geopoint corresponding to intersection
            intersect = self._observer_topogrid.offset(self.azimuth, dist)
            
            #: set the altitude of this point (retrieved from profile)
            intersect.altitude = self.get_altitude_at_distance(dist) # / 1000.
           
        except IndexError as e:
            print(("No intersections could be detected, err: %s" %repr(e)))
        
        ax = None
        if plot:
            ax = self._plot_intersect_search_result(view_elevations, dist)
            if dist == None:
                dist = nan
            ax.set_title("Azim: %.1f, Elev: %.1f, Intersect @ "
                "dist =%.1f km" %(self.azimuth, elev_angle, dist))
            
        return dist, dist_err, intersect, view_elevations, ax
    
    def _plot_intersect_search_result(self, view_elevations, dist=None):
        ax = self.plot()
        ax.plot(self.dists, view_elevations, label = "Viewing direction")
        try:
            ax.axvline(dist, ls = "--", label = "Intersection")
        except:
            pass
        ax.legend(loc="best", fancybox=True, framealpha=0.4)
        return ax
        
        
    def get_altitude_at_distance(self, dist):
        """Returns altitude at a ceratain distance from observer
        
        :param float dist: horizontal distance from obsever along profile        
        """
        idx = argmin(abs(self.dists - dist))
        return self.profile[idx]
    
    @property
    def start_point(self):
        """Return position of observer"""
        return self.observer
        
    @property
    def min(self):
        """Return minimum altitude in profile"""
        return nanmin(self.profile)
        
    @property
    def max(self):
        """Return maximum altitude in profile"""
        return nanmax(self.profile)
    
    @property
    def alt_range(self):
        """Return altitude range of profile
        """
        return self.max - self.min
        
    def __call__(self, dist):
        """Returns altitude at a certain distance
        
        :param float dist: distance in km
        """
        return self.get_altitude_at_distance(dist)
        
    def plot(self, ax = None):
        """Plot the profile into an axis object
        
        :param ax: matplotlib axis object
        """
        if ax is None:
            fig, ax = subplots(1,1)
        ax.fill_between(self.dists, self.profile, facecolor="#994d00",
                        alpha=0.20)
        ax.set_xlabel("Distance [km]")
        ax.set_ylabel("Altitude [m]")
        ax.set_ylim([self.min - .1 * self.alt_range, 
                     self.max + .1 * self.alt_range])
        return ax
コード例 #10
0
def test_GeoPoint():
    """Test basic arithmetic operations on GeoPoints."""
    assert (GeoPoint(1, 1))