Пример #1
0
def observations_to_gpx(
    observations: List[JsonResponse], output_file: str = "observations.gpx", track: bool = True
):
    """Convert a list of observations to a set of GPX waypoints or a GPX track

    Args:
        observations: JSON observations
        output_file: File path to write to
        track: Create an ordered GXP track; otherwise, create unordered GPX waypoints
    """
    gpx = GPX()
    logger.info(f"Converting {len(observations)} to GPX points")
    points = [observation_to_gpx_point(obs, track=track) for obs in observations]

    if track:
        gpx_track = GPXTrack()
        gpx.tracks.append(gpx_track)
        gpx_segment = GPXTrackSegment()
        gpx_track.segments.append(gpx_segment)
        gpx_segment.points = points
    else:
        gpx.waypoints = points

    # Save to file
    logger.info(f"Writing GPX data to {output_file}")
    with open(output_file, "w") as f:
        f.write(gpx.to_xml())
Пример #2
0
def optimize_segment(seg):
    """ RDP algorithm """
    result = GPXTrackSegment()
    arr = np.array(list(map(lambda p: [p.latitude, p.longitude], seg.points)))
    mask = rdp(arr, algo="iter", return_mask=True, epsilon=EPSILON)
    parr = np.array(list(seg.points))
    result.points = list(parr[mask])
    return result
Пример #3
0
def to_gpx(observations: AnyObservations,
           filename: str = None,
           track: bool = True) -> str:
    """Convert a list of observations to a set of GPX waypoints or a GPX track

    Example:

        >>> from pyinaturalist import get_observations
        >>> from pyinaturalist_convert import to_gpx
        >>>
        >>> results = get_observations(
        ...     project_id=36883,         # ID of the 'Sugarloaf Ridge State Park' project
        ...     created_d1='2020-01-01',  # Get observations from January 2020...
        ...     created_d2='2020-09-30',  # ...through September 2020
        ...     geo=True,                 # Only get observations with coordinates
        ...     geoprivacy='open',        # Only get observations with public coordinates
        ...     page='all',               # Paginate through all response pages
        ... )
        >>> to_gpx(results, '~/tracks/observations-36883.gpx')

    Args:
        observations: JSON observations
        filename: Optional file path to write to
        track: Create an ordered GPX track; otherwise, create unordered GPX waypoints

    Returns:
        GPX XML as a string
    """
    gpx = GPX()
    points = [
        to_gpx_point(obs, track=track) for obs in to_dict_list(observations)
    ]

    if track:
        gpx_track = GPXTrack()
        gpx.tracks.append(gpx_track)
        gpx_segment = GPXTrackSegment()
        gpx_track.segments.append(gpx_segment)
        gpx_segment.points = points
    else:
        gpx.waypoints = points

    gpx_xml = gpx.to_xml()
    if filename:
        write(gpx_xml, filename)
    return gpx_xml
Пример #4
0
def save_to_gpx(nav_df: pd.DataFrame, fileOutPN, gpx_obj_namef=None, waypoint_symbf=None, cfg_proc=None, gpx=None):  #
    """
    Save navigation from dataframe to *.gpx file. track or waypoints.
    Generate waypoints names and selects symbols from cfg['out']['gpx_symbols'] based on current row in nav_df
    :param nav_df: DataFrame with fields:
        if waypoint_symbf: itbl, ...
    :param fileOutPN:       *.gpx file full name without extension. Set None to not write (useful if need only gpx)
    :param gpx_obj_namef:   str or fun(waypoint number). If None then we set it to fileOutPN.stem
    :param waypoint_symbf:  str or fun(nav_df record = row). If None saves track
    :param cfg_proc:
        'simplify_tracks_error_m'
        'dt_per_file'
        'b_missed_coord_to_zeros'
        period_segments or period_tracks: to split track by this in one file
    :param gpx: gpx object to update. If None (default) then will be created here, updated and saved
    :return: None
    """

    if nav_df.empty:
        l.warning('no data')
        return
    if gpx_obj_namef is None:
        gpx_obj_namef = Path(fileOutPN).stem
    if cfg_proc is None:
        cfg_proc = {'dt_per_file': None}
    elif not 'dt_per_file' in cfg_proc:
        cfg_proc['dt_per_file'] = None
    if gpx is None:
        gpx = GPX()

    if waypoint_symbf:
        # , fun_symbol= 'Waypoint', fun_name= str
        if isinstance(waypoint_symbf, str):
            s = waypoint_symbf
            waypoint_symbf = lambda x: s
        b_useDepEcho = 'DepEcho' in nav_df.columns and any(nav_df['DepEcho'])

        w_names = set()
        # w_name = None # same perpose for not all conditions but faster
        # nav_dft= nav_df.reset_index().set_index('itbl', drop=False, append=True) #, inplace=True
        # for t in range(nav_dft.itbl.min(), nav_dft.itbl.max()+1):  #.ptp() = -
        for t, nav_dft in nav_df.groupby(['itbl']):  # .reset_index()
            for i, r in enumerate(nav_dft.itertuples()):  # .loc[t] name=None
                str_time_short = '{:%d %H:%M}'.format(r.Index.to_pydatetime())
                timeUTC = r.Index.tz_convert(None).to_pydatetime()
                str_time_long = '{:%d.%m.%y %H:%M:%S}'.format(timeUTC)
                name = gpx_obj_namef if isinstance(gpx_obj_namef, str) else gpx_obj_namef(i, r, t)

                # remove duplicates by add letter
                name_test_dup = name
                i_dup = 0
                while name_test_dup in w_names:  # name== w_name or :
                    name_test_dup = name + chr(97 + i_dup)  # chr(97) = 'a'
                    i_dup += 1
                else:
                    name = name_test_dup
                w_names.add(name)

                gpx_waypoint = GPXWaypoint(
                    latitude=r.Lat,
                    longitude=r.Lon,
                    time=timeUTC,
                    description=str_time_long,
                    comment=str_time_short,
                    name=name,
                    symbol=waypoint_symbf(r),
                    elevation=-r.DepEcho if b_useDepEcho and np.isfinite(
                        r.DepEcho) else None)  # , description=, type=, comment=
                # if not i_dup:
                #     w_name = name  # to check duplicates on next cycle

                gpx.waypoints.append(gpx_waypoint)
        if isinstance(gpx_obj_namef, str):
            gpx.description = gpx_obj_namef
        if fileOutPN:
            gpx.author_email = '*****@*****.**'
            write_file(fileOutPN, gpx.to_xml())
    else:  # tracks

        # loc= np.zeros_like(nav_df.index, dtype= int)
        # Lat= np.zeros_like(nav_df.index, dtype= np.float64)
        # Lon= np.zeros_like(nav_df.index, dtype= np.float64)
        # T= np.zeros_like(nav_df.index, dtype= pd.Timedelta)

        b_have_depth = ('DepEcho' in nav_df.columns)
        #b_have_speed = ('Speed' in nav_df.columns)
        period_split = cfg_proc.get('period_segments') or cfg_proc.get('period_tracks')
        if period_split:
            period_split = pd_period_to_timedelta(period_split)
            t_intervals_start = pd.date_range(
                start=nav_df.index[0].normalize(),
                end=max(nav_df.index[-1],
                        nav_df.index[-1].normalize() + period_split),
                freq=period_split)[1:]  # make last t_interval_start >= all_data[-1]
            #format_time =
        else:
            t_intervals_start = nav_df.index[-1:]  # series with 1 last value
        t_interval_end = nav_df.index[0]
        n_intervals_without_data = 0
        part = 0
        nav_df = nav_df.tz_convert('utc', copy=False)
        Tprev = nav_df.index[0].to_pydatetime()
        Tcur = Tprev
        if not cfg_proc.get('period_tracks'):
            gpx_track = gpx_track_create(gpx, gpx_obj_namef)
        for t_interval_start in t_intervals_start:
            t_interval = slice(t_interval_end, t_interval_start)  # from previous last
            # USEtime = [[t_interval_end.isoformat(), t_interval_start.isoformat()]]
            nav_df_cur = nav_df.truncate(t_interval_end, t_interval_start, copy=True)
            t_interval_end = t_interval_start
            # load_interval
            if not len(nav_df_cur):
                print('empty interval')
                n_intervals_without_data += 1
                if n_intervals_without_data > 30:
                    print('30 intervals without data => think it is the end')
                    break
                continue
            gpx_segment = GPXTrackSegment()
            if cfg_proc.get('period_tracks'):
                fmt = '%y-%m-%d' if t_interval_start.second==0 and t_interval_start.hour==0 else '%y-%m-%d %H:%M'
                track_name = f'{gpx_obj_namef} {t_interval_start:{fmt}}'
                gpx_track = gpx_track_create(gpx, track_name)
                gpx_track[track_name].segments.append(gpx_segment)
            else:
                gpx_track[gpx_obj_namef].segments.append(gpx_segment)

            for i, r in enumerate(nav_df_cur.itertuples()):
                Tcur = r.Index.to_pydatetime()
                gpx_point = GPXTrackPoint(
                    latitude=r.Lat, longitude=r.Lon,
                    elevation=r.DepEcho if b_have_depth and not np.isnan(r.DepEcho) else None,
                    time=Tcur)  # , speed= speed_b, comment= Comment
                gpx_segment.points.append(gpx_point)
                # if i==1:
                # gpx.description= gpx_obj_namef
                # gpx.author_email= '*****@*****.**'
                # gpxxml= gpx.to_xml()
                # tree = ET.parse(gpxxml)
                # root = tree.getroot()

            if cfg_proc.get('simplify_tracks_error_m'):
                try:
                    gpx_segment.points = gpxpy_simplify_polyline(gpx_segment.points,
                                                                 cfg_proc['simplify_tracks_error_m'])
                except RecursionError as e:
                    recursion_limit = sys.getrecursionlimit()
                    l.error('Check time in data! Why increasing old recursion limit (%s) is needed? Trying: x10...',
                            recursion_limit)
                    try:
                        sys.setrecursionlimit(recursion_limit * 10)
                        gpx_segment.points = gpxpy_simplify_polyline(gpx_segment.points,
                                                                     cfg_proc['simplify_tracks_error_m'])
                        l.warning('now track simplified successfuly')
                    except Exception as e:
                        l.exception('not succes. skip simplifying tracks', recursion_limit)

            if cfg_proc['dt_per_file'] and Tcur - Tprev > cfg_proc['dt_per_file']:  # save to next file
                part += 1
                if fileOutPN:
                    gpx_proc_and_save(gpx, gpx_obj_namef, cfg_proc, f'{fileOutPN}part{part}')
                gpx_track = gpx_track_create(gpx, gpx_obj_namef)
                Tprev = Tcur
        if fileOutPN:
            gpx_proc_and_save(gpx, gpx_obj_namef, cfg_proc, fileOutPN)

    return gpx