Source code for tracktable.rw.point

#
# Copyright (c) 2014-2023 National Technology and Engineering
# Solutions of Sandia, LLC. Under the terms of Contract DE-NA0003525
# with National Technology and Engineering Solutions of Sandia, LLC,
# the U.S. Government retains certain rights in this software.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""tracktable.rw.point: Readers and writers for points

Here you will find convenience functions for instantiating and
configuring point readers (trajectory points or base points)
given the name of the domain and the parameters you want to apply.

TODO: Add support for point writers as well.
"""

from tracktable.domain import domain_module_from_name

[docs] def trajectory_point_reader( infile, comment_character='#', domain='terrestrial', delimiter=',', object_id_column=0, timestamp_column=1, longitude_column=2, latitude_column=3, x_column=2, y_column=3, z_column=4, string_fields=dict(), real_fields=dict(), time_fields=dict() ): """Instantiate and configure a trajectory point reader. Tracktable point readers live in the domain modules: `tracktable.domain.<domain_name>.TrajectoryPointReader` where `domain_name` is one of `terrestrial`, `cartesian2d` or `cartesian3d`. This is a convenience function to set one up. A trajectory point must have all of the following elements: 1. An object ID. We use the object ID to group points into trajectories. 2. A timestamp. For the purposes of this function, that timestamp must be in the format 'YYYY-mm-dd HH:MM:SS' where YYYY is the 4-digit year, mm is the 2-digit month, dd is the 2-digit day of the month, and HH:MM:SS are the 2-digit hours, minutes, and seconds within the day, respectively. 3. Coordinates. For the terrestrial domain, these coordinates are longitude and latitude. For the cartesian2d domain, these are x and y. The cartesian3d domain adds z. By default, we look for the object ID in column 0, the timestamp in column 1, longitude (or x) in column 2, latitude (or y) in column 3, and the Z coordinate in column 4. We only pay attention to the coordinate assignments for the selected point domain. You may also specify other fields to be added to each point. See the documentation for the `string_fields`, `real_fields` and `time_fields` keyword arguments. This function will probably move into the main library at some point soon. We will keep a binding here to avoid breaking existing code. Arguments: infile (file-like object): Source for trajectory point data Keyword arguments: comment_character (character): Any row where this is the first non-whitespace character will be ignored. (default '#') delimiter (character): What character separates columns? (default ',') domain (str): Which point domain will the points belong to? (default 'terrestrial') object_id_column (int): Which column contains the object ID (default 0) timestamp_column (int): Which column contains the timestamp (default 1) longitude_column (int): Which column contains the longitude for each point? (default 2, assumes terrestrial domain) latitude_column (int): Which column contains the latitude for each point? (default 3, assumes terrestrial domain) x_column (int): Which column contains the X coordinate for each point? (default 2, assumes cartesian2d or cartesian3d) y_column (int): Which column contains the Y coordinate for each point? (default 3, assumes cartesian2d or cartesian3d) z_column (int): Which column contains the X coordinate for each point? (default 4, assumes cartesian3d) string_fields (dict, int -> string): Columns in the input that should be attached to each point as a string. The keys in this dictionary are column numbers. Their corresponding values are the name of the field that will be added to the point's properties. (default: empty) real_fields (dict, int -> string): Columns in the input that should be attached to each point as a real number. The keys in this dictionary are column numbers. Their corresponding values are the name of the field that will be added to the point's properties. (default: empty) time_fields (dict, int -> string): Columns in the input that should be attached to each point as a timestamp. The keys in this dictionary are column numbers. Their corresponding values are the name of the field that will be added to the point's properties. The timestamps must be in the same format as for the point as a whole, namely `YYYY-mm-dd HH:MM:SS`. (default: empty) Returns: Trajectory point reader from the appropriate domain with all fields configured according to arguments. Iterate over the reader object to retrieve the points. """ domain_module = domain_module_from_name(domain) reader = domain_module.TrajectoryPointReader() reader.input = infile _configure_reader_coordinates( reader, domain.lower(), longitude_column=longitude_column, latitude_column=latitude_column, x_column=x_column, y_column=y_column, z_column=z_column ) _configure_reader_trajectory_point_fields( reader, object_id_column=object_id_column, timestamp_column=timestamp_column, string_fields=string_fields, real_fields=real_fields, time_fields=time_fields ) _configure_reader_file_properties( reader, comment_character=comment_character, delimiter=delimiter ) return reader
# ---------------------------------------------------------------------
[docs] def base_point_reader( infile, comment_character='#', domain='terrestrial', delimiter=',', object_id_column=0, timestamp_column=1, longitude_column=2, latitude_column=3, x_column=2, y_column=3, z_column=4, string_fields=dict(), real_fields=dict(), time_fields=dict() ): """Instantiate and configure an undecoated point reader. Tracktable point readers live in the domain modules: `tracktable.domain.<domain_name>.PointReader` where `domain_name` is one of `terrestrial`, `cartesian2d` or `cartesian3d`. This is a convenience function to set one up. A base (undecorated) point must have Coordinates. For the terrestrial domain, these coordinates are longitude and latitude. For the cartesian2d domain, these are x and y. The cartesian3d domain adds z. By default, we expect longitude (or x) in column 2, latitude (or y) in column 3, and the Z coordinate in column 4. We only pay attention to the coordinate assignments for the selected point domain. The difference between base points and trajectory points is that base points don't have an object ID, a timestamp, or any metadata fields: just coordinates. This function will probably move into the main library at some point soon. We will keep a binding here to avoid breaking existing code. Arguments: infile (file-like object): Source for trajectory point data Keyword arguments: comment_character (character): Any row where this is the first non-whitespace character will be ignored. (default '#') delimiter (character): What character separates columns? (default ',') domain (str): Which point domain will the points belong to? (default 'terrestrial') longitude_column (int): Which column contains the longitude for each point? (default 2, assumes terrestrial domain) latitude_column (int): Which column contains the latitude for each point? (default 3, assumes terrestrial domain) x_column (int): Which column contains the X coordinate for each point? (default 2, assumes cartesian2d or cartesian3d) y_column (int): Which column contains the Y coordinate for each point? (default 3, assumes cartesian2d or cartesian3d) z_column (int): Which column contains the X coordinate for each point? (default 4, assumes cartesian3d) Returns: Base point reader from the appropriate domain with all fields configured according to arguments. Iterate over the reader object to retrieve the points. """ domain_module = domain_module_from_name(domain) reader = domain_module.BasePointReader() reader.input = infile _configure_reader_file_properties( reader, comment_character=comment_character, delimiter=delimiter ) _configure_reader_coordinates( reader, domain.lower(), x_column=x_column, y_column=y_column, z_column=z_column, longitude_column=longitude_column, latitude_column=latitude_column) return reader
### ### Below this point are the utility functions that apply arguments ### to readers. ### def _configure_reader_trajectory_point_fields( reader, object_id_column=0, timestamp_column=1, string_fields=dict(), real_fields=dict(), time_fields=dict() ): """Configure a point reader to load metadata fields This is a utility function. See the arguments for configure_trajectory_point_reader for information on the parameters here. """ reader.object_id_column = object_id_column reader.timestamp_column = timestamp_column for (field, column) in string_fields.items(): reader.set_string_field_column(field, column) for (field, column) in real_fields.items(): reader.set_real_field_column(field, column) for (field, column) in time_fields.items(): reader.set_time_field_column(field, column) def _configure_reader_coordinates( reader, domain, **kwargs): """Configure coordinate fields on a point reader This is a utility function. This is where we select which coordinate arguments the reader should have. """ if domain == 'terrestrial': reader.coordinates[0] = kwargs['longitude_column'] reader.coordinates[1] = kwargs['latitude_column'] elif domain in ['cartesian2d', 'cartesian3d']: reader.coordinates[0] = kwargs['x_column'] reader.coordinates[1] = kwargs['y_column'] if domain == 'cartesian3d': reader.coordinates[2] = kwargs['z_column'] else: raise KeyError('Unsupported domain: {}'.format(domain)) def _configure_reader_file_properties(reader, comment_character='#', delimiter=','): """Configure delimiter and comment character for a reader This is a utility function. """ reader.comment_character = comment_character reader.delimiter = delimiter