├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── config └── custom_rosconsole.conf ├── doc ├── Makefile ├── alvinxy.rst ├── conf.py ├── geonav_transform.rst ├── html │ ├── .buildinfo │ ├── .doctrees │ │ ├── alvinxy.doctree │ │ ├── environment.pickle │ │ ├── geonav_transform.doctree │ │ ├── index.doctree │ │ └── modules.doctree │ ├── _modules │ │ ├── alvinxy │ │ │ └── alvinxy.html │ │ ├── geonav_transform │ │ │ └── geonav_conversions.html │ │ └── index.html │ ├── _sources │ │ ├── alvinxy.txt │ │ ├── geonav_transform.txt │ │ ├── index.txt │ │ └── modules.txt │ ├── _static │ │ ├── ajax-loader.gif │ │ ├── basic.css │ │ ├── comment-bright.png │ │ ├── comment-close.png │ │ ├── comment.png │ │ ├── default.css │ │ ├── doctools.js │ │ ├── down-pressed.png │ │ ├── down.png │ │ ├── file.png │ │ ├── jquery.js │ │ ├── minus.png │ │ ├── plus.png │ │ ├── pygments.css │ │ ├── searchtools.js │ │ ├── sidebar.js │ │ ├── underscore.js │ │ ├── up-pressed.png │ │ ├── up.png │ │ └── websupport.js │ ├── alvinxy.html │ ├── genindex.html │ ├── geonav_transform.html │ ├── index-msg.html │ ├── index.html │ ├── modules.html │ ├── msg-styles.css │ ├── objects.inv │ ├── py-modindex.html │ ├── search.html │ └── searchindex.js ├── index.rst ├── manifest.yaml └── modules.rst ├── examples ├── compare_alvin_geonav.py ├── gazebo_elestero_ex.py ├── geonav_alvin_ex.py └── utm_ex.py ├── include └── geonav_transform │ ├── geonav_transform.h │ ├── geonav_utilities.h │ └── navsat_conversions.h ├── launch ├── cavr.launch ├── elestero.launch └── geonav_transform.launch ├── matlab ├── alvinxy │ ├── deg2degmin.m │ ├── degmin2deg.m │ ├── ll2xy.m │ ├── ll2xy2ll_example_at340.xls │ ├── ll2xyv.m │ ├── mdeglat.m │ ├── mdeglon.m │ ├── xy2ll.m │ └── xy2lldegmin.m ├── geonav │ ├── ll2xy.cpp │ ├── ll2xy.m │ ├── make_mex.sh │ ├── xy2ll.cpp │ └── xy2ll.m └── matlab_ex.m ├── package.xml ├── rosdoc.yaml ├── rviz └── frames.rviz ├── setup.py ├── src ├── alvinxy │ ├── __init__.py │ ├── alvinxy.py │ └── alvinxy_example.py ├── geonav_transform.cpp ├── geonav_transform │ ├── __init__.py │ ├── geonav_conversions.py │ └── utmtest.py ├── geonav_transform_node.cpp └── geonav_utilities.cpp └── test └── pubodom.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.*~ 2 | *~ 3 | *.*# 4 | #*.*# 5 | *.pyc 6 | 7 | Build-related files 8 | docs/_build/ 9 | .coverage 10 | .tox 11 | *.egg-info 12 | *.egg 13 | build/ 14 | dist/ 15 | htmlcov/ 16 | MANIFEST -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | project(geonav_transform) 3 | 4 | ## Find catkin macros and libraries 5 | ## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) 6 | ## is used, also find other catkin packages 7 | find_package(catkin REQUIRED COMPONENTS 8 | roscpp 9 | geographic_msgs 10 | geometry_msgs 11 | nav_msgs 12 | sensor_msgs 13 | std_msgs 14 | tf2 15 | tf2_geometry_msgs 16 | tf2_ros 17 | ) 18 | 19 | # Attempt to find Eigen using its own CMake module. 20 | # If that fails, fall back to cmake_modules package. 21 | #find_package(Eigen3) 22 | find_package(Eigen) 23 | set(EIGEN_PACKAGE EIGEN3) 24 | if(NOT EIGEN3_FOUND) 25 | find_package(cmake_modules REQUIRED) 26 | find_package(Eigen REQUIRED) 27 | set(EIGEN3_INCLUDE_DIRS ${EIGEN_INCLUDE_DIRS}) 28 | set(EIGEN3_LIBRARIES ${EIGEN_LIBRARIES}) 29 | set(EIGEN_PACKAGE Eigen) 30 | endif() 31 | 32 | add_definitions(-DEIGEN_NO_DEBUG -DEIGEN_MPL2_ONLY) 33 | 34 | ## Uncomment this if the package has a setup.py. This macro ensures 35 | ## modules and global scripts declared therein get installed 36 | ## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html 37 | catkin_python_setup() 38 | 39 | 40 | ################################### 41 | ## catkin specific configuration ## 42 | ################################### 43 | ## The catkin_package macro generates cmake config files for your package 44 | 45 | catkin_package( 46 | INCLUDE_DIRS include 47 | LIBRARIES geonav_transform 48 | CATKIN_DEPENDS 49 | roscpp 50 | cmake_modules 51 | geographic_msgs 52 | geometry_msgs 53 | nav_msgs 54 | sensor_msgs 55 | std_msgs 56 | tf2 57 | tf2_geometry_msgs 58 | tf2_ros 59 | DEPENDS ${EIGEN_PACKAGE} 60 | ) 61 | 62 | ########### 63 | ## Build ## 64 | ########### 65 | 66 | ## Specify additional locations of header files 67 | ## Your package locations should be listed before other locations 68 | # include_directories(include) 69 | include_directories(include ${catkin_INCLUDE_DIRS} ${EIGEN3_INCLUDE_DIRS}) 70 | 71 | ## Declare a C++ library 72 | add_library(geonav_transform 73 | src/geonav_transform.cpp 74 | src/geonav_utilities.cpp 75 | ) 76 | 77 | ## Add cmake target dependencies of the library 78 | ## as an example, code may need to be generated before libraries 79 | ## either from message generation or dynamic reconfigure 80 | #add_dependencies(geonav_transform ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) 81 | 82 | ## Declare a C++ executable 83 | add_executable(geonav_transform_node src/geonav_transform_node.cpp) 84 | 85 | ## Add cmake target dependencies of the executable 86 | ## same as for the library above 87 | # add_dependencies(geonav_transform_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) 88 | 89 | ## Specify libraries to link a library or executable target against 90 | target_link_libraries(geonav_transform 91 | ${catkin_LIBRARIES} 92 | ${EIGEN3_LIBRARIES} 93 | ) 94 | target_link_libraries(geonav_transform_node geonav_transform 95 | ${catkin_LIBRARIES} 96 | ) 97 | 98 | 99 | ############# 100 | ## Install ## 101 | ############# 102 | 103 | # all install targets should use catkin DESTINATION variables 104 | # See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html 105 | 106 | ## Mark executable scripts (Python etc.) for installation 107 | ## in contrast to setup.py, you can choose the destination 108 | # install(PROGRAMS 109 | # scripts/my_python_script 110 | # DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 111 | # ) 112 | 113 | ## Mark executables and/or libraries for installation 114 | # install(TARGETS geonav_transform geonav_transform_node 115 | # ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} 116 | # LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} 117 | # RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 118 | # ) 119 | 120 | ## Mark cpp header files for installation 121 | # install(DIRECTORY include/${PROJECT_NAME}/ 122 | # DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} 123 | # FILES_MATCHING PATTERN "*.h" 124 | # PATTERN ".svn" EXCLUDE 125 | # ) 126 | 127 | ## Mark other files for installation (e.g. launch and bag files, etc.) 128 | # install(FILES 129 | # # myfile1 130 | # # myfile2 131 | # DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} 132 | # ) 133 | 134 | ############# 135 | ## Testing ## 136 | ############# 137 | 138 | ## Add gtest based cpp test target and link libraries 139 | # catkin_add_gtest(${PROJECT_NAME}-test test/test_geonav_transform.cpp) 140 | # if(TARGET ${PROJECT_NAME}-test) 141 | # target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}) 142 | # endif() 143 | 144 | ## Add folders to be run by python nosetests 145 | # catkin_add_nosetests(test) 146 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # geonav_transform 2 | 3 | The goal of this package is to simplify the integration of accurate/precise geographic navigation information (typically from a sensor) into the ROS localization and navigation workflows. To those ends, the geonav_tranform_node can perform the following functions: 4 | 5 | * Takes incoming Odometry messages, typically from a sensor, that contain a geographic position and sensor-frame orientation and velocities. (Note, it would be nice to have a new message the specifies this type of input - using Odometry is a bit of a hack.) 6 | * Tranforms these message to new Odometry message that express the information in the following frames 7 | * utm 8 | * odom 9 | * Broadcasts the following tf2 tranforms 10 | * utm->odom 11 | * odom->base_link 12 | 13 | The use-case that motivated this project is integrating sensors that provide a GPS-aided INS solution (e.g., microstrain, advanced navigation, Xsens, etc.). This situation is analogous to using an ekf/ukf node from robot_localization package to fuse IMU and GPS information, but in this case the processing is done by the sensor. The purpose of this package is to allow integration of this type of sensor directly into the ROS navigation stack. 14 | 15 | ## Parameters 16 | 17 | * ~datum: The origin of the local "odom" frame specified as a three element array [Latitude, Logitude, Altitude]. Lat/Lon are in decimal degrees; altitude is in meters. Default is [0.0 0.0 0.0] and this is probably not what you want! 18 | * ~frequency: The frequency of broadcasting the tf2 tranforms. The Odometry messages are published at the same rate as the incoming Odometry messages. Default is 10 Hz 19 | * ~broadcast_utm2odom_transform: Whether or not to broadcast the utm->odom tranform. Default is True. 20 | * ~broadcast_odom2base_transform: Whether or not to broadcast the odom->base_link tranform. Default is True. 21 | * ~zero_altitude 22 | * ~base_link_frame_id: Default is "base_link" 23 | * ~odom_frame_id: Default is "odom" 24 | * ~utm_frame_id: Default is "utm" 25 | 26 | 27 | ## Subscribed Topics 28 | 29 | * /odometry/nav: A nav_msgs/Odometry message with geographic position and velocity data. The message is organized as follows: 30 | * The header.frame_id and child_frame_id values are ignored. 31 | * pose.pose.position is 32 | * .y = Latitude [dec. degrees] 33 | * .x = Longitude [dec. degrees] 34 | * .z = Altitude [m] 35 | * pose.pose.orientation of the base_link relative to a fixed ENU coordinate frame 36 | * If the ~orientation_ned parameter is set to true, the node will convert the orientation from NED to ENU. 37 | * For now we are assuming the orientation is true (not magnetic). Typically the magnetic declination will be set internal to the sensor providing the information. 38 | * pose.covariance is expressed in meters for position and radians for orientation (REP-103) 39 | * twist.twist.linear/angular is the velocity in the base_link frame 40 | * twist.covariance is expressed in m/s and rad/s. 41 | 42 | 43 | ## Published Topics 44 | 45 | The following message are published at the same rate as incoming /odometry/nav messages. Consistent with the navigation stack and robot_localization, the Odometry messages contain position pose information relative to the fixed frame (utm or odom, reported as the header.frame_id). The velocity (twist) information is relative to the mobile frame (base_link, reported as the .child_frame). 46 | 47 | * /odometry/odom: A nav_msgs/Odometry message in the local odom frame (relative to the datum) 48 | * /odometry/utm: A nav_msgs/Odometry message in the UTM frame 49 | * This is published as a static tranform: http://wiki.ros.org/tf2_ros http://wiki.ros.org/tf2/Tutorials/Writing%20a%20tf2%20static%20broadcaster%20(C%2B%2B) 50 | 51 | ## Published Transforms 52 | 53 | * utm->odom 54 | * odom->base_link 55 | 56 | ## Frames 57 | 58 | * utm: The global UTM coordinate frame. The origin of this frame (which UTM zone we are in) is determined by the datum parameter 59 | * odom: The local, fixed odom frame has an orgin specified by the datum parameter. We have assumed that there is no orientation between UTM and the odom frame. While this is not as general as possible, it simplifies the implementation, usage and interpretation. 60 | * base_link: This mobile frame typically coincides with the sensor frame. 61 | -------------------------------------------------------------------------------- /config/custom_rosconsole.conf: -------------------------------------------------------------------------------- 1 | # 2 | # You can define your own by e.g. copying this file and setting 3 | # ROSCONSOLE_CONFIG_FILE (in your environment) to point to the new file 4 | # 5 | log4j.logger.ros=INFO 6 | log4j.logger.ros.geonav_transform=DEBUG 7 | log4j.logger.ros.roscpp.superdebug=WARN -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = .build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/geonav_transform.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/geonav_transform.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/geonav_transform" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/geonav_transform" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /doc/alvinxy.rst: -------------------------------------------------------------------------------- 1 | alvinxy package 2 | =============== 3 | 4 | Submodules 5 | ---------- 6 | 7 | alvinxy.alvinxy module 8 | ---------------------- 9 | 10 | .. automodule:: alvinxy.alvinxy 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | alvinxy.alvinxy_example module 16 | ------------------------------ 17 | 18 | .. automodule:: alvinxy.alvinxy_example 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: alvinxy 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # geonav_transform documentation build configuration file, created by 4 | # sphinx-quickstart on Tue Mar 7 15:42:59 2017. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | import catkin_pkg.package 18 | 19 | catkin_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 20 | catkin_package = catkin_pkg.package.parse_package(os.path.join(catkin_dir, catkin_pkg.package.PACKAGE_MANIFEST_FILENAME)) 21 | 22 | # If extensions (or modules to document with autodoc) are in another directory, 23 | # add these directories to sys.path here. If the directory is relative to the 24 | # documentation root, use os.path.abspath to make it absolute, like shown here. 25 | #sys.path.insert(0, os.path.abspath('.')) 26 | 27 | # -- General configuration ------------------------------------------------ 28 | 29 | # If your documentation needs a minimal Sphinx version, state it here. 30 | #needs_sphinx = '1.0' 31 | 32 | # Add any Sphinx extension module names here, as strings. They can be 33 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 34 | # ones. 35 | extensions = [ 36 | 'sphinx.ext.autodoc', 37 | 'sphinx.ext.doctest', 38 | 'sphinx.ext.intersphinx', 39 | 'sphinx.ext.todo', 40 | 'sphinx.ext.coverage', 41 | 'sphinx.ext.mathjax', 42 | 'sphinx.ext.ifconfig', 43 | 'sphinx.ext.viewcode', 44 | ] 45 | 46 | # Add any paths that contain templates here, relative to this directory. 47 | templates_path = ['.templates'] 48 | 49 | # The suffix of source filenames. 50 | source_suffix = '.rst' 51 | 52 | # The encoding of source files. 53 | #source_encoding = 'utf-8-sig' 54 | 55 | # The master toctree document. 56 | master_doc = 'index' 57 | 58 | # General information about the project. 59 | project = u'geonav_transform' 60 | copyright = u'2017, Brian Bingham' 61 | 62 | # The version info for the project you're documenting, acts as replacement for 63 | # |version| and |release|, also used in various other places throughout the 64 | # built documents. 65 | # 66 | # The short X.Y version. 67 | version = catkin_package.version 68 | release = catkin_package.version 69 | 70 | # The language for content autogenerated by Sphinx. Refer to documentation 71 | # for a list of supported languages. 72 | #language = None 73 | 74 | # There are two options for replacing |today|: either, you set today to some 75 | # non-false value, then it is used: 76 | #today = '' 77 | # Else, today_fmt is used as the format for a strftime call. 78 | #today_fmt = '%B %d, %Y' 79 | 80 | # List of patterns, relative to source directory, that match files and 81 | # directories to ignore when looking for source files. 82 | exclude_patterns = ['.build'] 83 | 84 | # The reST default role (used for this markup: `text`) to use for all 85 | # documents. 86 | #default_role = None 87 | 88 | # If true, '()' will be appended to :func: etc. cross-reference text. 89 | #add_function_parentheses = True 90 | 91 | # If true, the current module name will be prepended to all description 92 | # unit titles (such as .. function::). 93 | #add_module_names = True 94 | 95 | # If true, sectionauthor and moduleauthor directives will be shown in the 96 | # output. They are ignored by default. 97 | #show_authors = False 98 | 99 | # The name of the Pygments (syntax highlighting) style to use. 100 | pygments_style = 'sphinx' 101 | 102 | # A list of ignored prefixes for module index sorting. 103 | #modindex_common_prefix = [] 104 | 105 | # If true, keep warnings as "system message" paragraphs in the built documents. 106 | #keep_warnings = False 107 | 108 | 109 | # -- Options for HTML output ---------------------------------------------- 110 | 111 | # The theme to use for HTML and HTML Help pages. See the documentation for 112 | # a list of builtin themes. 113 | html_theme = 'default' 114 | 115 | # Theme options are theme-specific and customize the look and feel of a theme 116 | # further. For a list of options available for each theme, see the 117 | # documentation. 118 | #html_theme_options = {} 119 | 120 | # Add any paths that contain custom themes here, relative to this directory. 121 | #html_theme_path = [] 122 | 123 | # The name for this set of Sphinx documents. If None, it defaults to 124 | # " v documentation". 125 | #html_title = None 126 | 127 | # A shorter title for the navigation bar. Default is the same as html_title. 128 | #html_short_title = None 129 | 130 | # The name of an image file (relative to this directory) to place at the top 131 | # of the sidebar. 132 | #html_logo = None 133 | 134 | # The name of an image file (within the static path) to use as favicon of the 135 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 136 | # pixels large. 137 | #html_favicon = None 138 | 139 | # Add any paths that contain custom static files (such as style sheets) here, 140 | # relative to this directory. They are copied after the builtin static files, 141 | # so a file named "default.css" will overwrite the builtin "default.css". 142 | html_static_path = ['.static'] 143 | 144 | # Add any extra paths that contain custom files (such as robots.txt or 145 | # .htaccess) here, relative to this directory. These files are copied 146 | # directly to the root of the documentation. 147 | #html_extra_path = [] 148 | 149 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 150 | # using the given strftime format. 151 | #html_last_updated_fmt = '%b %d, %Y' 152 | 153 | # If true, SmartyPants will be used to convert quotes and dashes to 154 | # typographically correct entities. 155 | #html_use_smartypants = True 156 | 157 | # Custom sidebar templates, maps document names to template names. 158 | #html_sidebars = {} 159 | 160 | # Additional templates that should be rendered to pages, maps page names to 161 | # template names. 162 | #html_additional_pages = {} 163 | 164 | # If false, no module index is generated. 165 | #html_domain_indices = True 166 | 167 | # If false, no index is generated. 168 | #html_use_index = True 169 | 170 | # If true, the index is split into individual pages for each letter. 171 | #html_split_index = False 172 | 173 | # If true, links to the reST sources are added to the pages. 174 | #html_show_sourcelink = True 175 | 176 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 177 | #html_show_sphinx = True 178 | 179 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 180 | #html_show_copyright = True 181 | 182 | # If true, an OpenSearch description file will be output, and all pages will 183 | # contain a tag referring to it. The value of this option must be the 184 | # base URL from which the finished HTML is served. 185 | #html_use_opensearch = '' 186 | 187 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 188 | #html_file_suffix = None 189 | 190 | # Output file base name for HTML help builder. 191 | htmlhelp_basename = 'geonav_transformdoc' 192 | 193 | 194 | # -- Options for LaTeX output --------------------------------------------- 195 | 196 | latex_elements = { 197 | # The paper size ('letterpaper' or 'a4paper'). 198 | #'papersize': 'letterpaper', 199 | 200 | # The font size ('10pt', '11pt' or '12pt'). 201 | #'pointsize': '10pt', 202 | 203 | # Additional stuff for the LaTeX preamble. 204 | #'preamble': '', 205 | } 206 | 207 | # Grouping the document tree into LaTeX files. List of tuples 208 | # (source start file, target name, title, 209 | # author, documentclass [howto, manual, or own class]). 210 | latex_documents = [ 211 | ('index', 'geonav_transform.tex', u'geonav\\_transform Documentation', 212 | u'Brian Bingham', 'manual'), 213 | ] 214 | 215 | # The name of an image file (relative to this directory) to place at the top of 216 | # the title page. 217 | #latex_logo = None 218 | 219 | # For "manual" documents, if this is true, then toplevel headings are parts, 220 | # not chapters. 221 | #latex_use_parts = False 222 | 223 | # If true, show page references after internal links. 224 | #latex_show_pagerefs = False 225 | 226 | # If true, show URL addresses after external links. 227 | #latex_show_urls = False 228 | 229 | # Documents to append as an appendix to all manuals. 230 | #latex_appendices = [] 231 | 232 | # If false, no module index is generated. 233 | #latex_domain_indices = True 234 | 235 | 236 | # -- Options for manual page output --------------------------------------- 237 | 238 | # One entry per manual page. List of tuples 239 | # (source start file, name, description, authors, manual section). 240 | man_pages = [ 241 | ('index', 'geonav_transform', u'geonav_transform Documentation', 242 | [u'Brian Bingham'], 1) 243 | ] 244 | 245 | # If true, show URL addresses after external links. 246 | #man_show_urls = False 247 | 248 | 249 | # -- Options for Texinfo output ------------------------------------------- 250 | 251 | # Grouping the document tree into Texinfo files. List of tuples 252 | # (source start file, target name, title, author, 253 | # dir menu entry, description, category) 254 | texinfo_documents = [ 255 | ('index', 'geonav_transform', u'geonav_transform Documentation', 256 | u'Brian Bingham', 'geonav_transform', 'One line description of project.', 257 | 'Miscellaneous'), 258 | ] 259 | 260 | # Documents to append as an appendix to all manuals. 261 | #texinfo_appendices = [] 262 | 263 | # If false, no module index is generated. 264 | #texinfo_domain_indices = True 265 | 266 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 267 | #texinfo_show_urls = 'footnote' 268 | 269 | # If true, do not generate a @detailmenu in the "Top" node's menu. 270 | #texinfo_no_detailmenu = False 271 | 272 | 273 | # Example configuration for intersphinx: refer to the Python standard library. 274 | intersphinx_mapping = {'http://docs.python.org/': None} 275 | -------------------------------------------------------------------------------- /doc/geonav_transform.rst: -------------------------------------------------------------------------------- 1 | geonav_transform package 2 | ======================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | geonav_transform.geonav_conversions module 8 | ------------------------------------------ 9 | 10 | .. automodule:: geonav_transform.geonav_conversions 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | geonav_transform.utmtest module 16 | ------------------------------- 17 | 18 | .. automodule:: geonav_transform.utmtest 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: geonav_transform 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /doc/html/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 4 | tags: 5 | -------------------------------------------------------------------------------- /doc/html/.doctrees/alvinxy.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/doc/html/.doctrees/alvinxy.doctree -------------------------------------------------------------------------------- /doc/html/.doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/doc/html/.doctrees/environment.pickle -------------------------------------------------------------------------------- /doc/html/.doctrees/geonav_transform.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/doc/html/.doctrees/geonav_transform.doctree -------------------------------------------------------------------------------- /doc/html/.doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/doc/html/.doctrees/index.doctree -------------------------------------------------------------------------------- /doc/html/.doctrees/modules.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/doc/html/.doctrees/modules.doctree -------------------------------------------------------------------------------- /doc/html/_modules/alvinxy/alvinxy.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | alvinxy.alvinxy — geonav_transform 0.0.1 documentation 10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 44 | 45 |
46 |
47 |
48 |
49 | 50 |

Source code for alvinxy.alvinxy

 51 | '''
 52 | AlvinXY Utility Module
 53 | 
 54 | All of this is based on WHOI's original implementations in C and MATLAB.
 55 | 
 56 | '''
 57 | 
 58 | from math import *
 59 | import numpy as np
 60 | 
 61 | 
[docs]def mdeglat(lat): 62 | ''' 63 | Provides meters-per-degree latitude at a given latitude 64 | 65 | Args: 66 | lat (float): latitude 67 | 68 | Returns: 69 | float: meters-per-degree value 70 | ''' 71 | latrad = lat*2.0*pi/360.0 ; 72 | 73 | dy = 111132.09 - 566.05 * cos(2.0*latrad) \ 74 | + 1.20 * cos(4.0*latrad) \ 75 | - 0.002 * cos(6.0*latrad) 76 | return dy 77 |
78 |
[docs]def mdeglon(lat): 79 | ''' 80 | Provides meters-per-degree longitude at a given latitude 81 | 82 | Args: 83 | lat (float): latitude in decimal degrees 84 | 85 | Returns: 86 | float: meters per degree longitude 87 | ''' 88 | latrad = lat*2.0*pi/360.0 89 | dx = 111415.13 * cos(latrad) \ 90 | - 94.55 * cos(3.0*latrad) \ 91 | + 0.12 * cos(5.0*latrad) 92 | return dx 93 |
94 |
[docs]def ll2xy(lat, lon, orglat, orglon): 95 | ''' 96 | AlvinXY: Lat/Long to X/Y 97 | Converts Lat/Lon (WGS84) to Alvin XYs using a Mercator projection. 98 | 99 | Args: 100 | lat (float): Latitude of location 101 | lon (float): Longitude of location 102 | orglat (float): Latitude of origin location 103 | orglon (float): Longitude of origin location 104 | 105 | Returns: 106 | tuple: (x,y) where... 107 | x is Easting in m (Alvin local grid) 108 | y is Northing in m (Alvin local grid) 109 | ''' 110 | x = (lon - orglon) * mdeglon(orglat); 111 | y = (lat - orglat) * mdeglat(orglat); 112 | return (x,y) 113 | 114 | 115 |
116 |
[docs]def xy2ll(x, y, orglat, orglon): 117 | 118 | ''' 119 | X/Y to Lat/Lon 120 | Converts Alvin XYs to Lat/Lon (WGS84) using a Mercator projection. 121 | 122 | Args: 123 | x (float): Easting in m (Alvin local grid) 124 | x (float): Northing in m (Alvin local grid) 125 | orglat (float): Latitude of origin location 126 | orglon (float): Longitude of origin location 127 | 128 | Returns: 129 | tuple: (lat,lon) 130 | ''' 131 | lon = x/mdeglon(orglat) + orglon 132 | lat = y/mdeglat(orglat) + orglat 133 | 134 | return (lat, lon) 135 | 136 | 137 | # Vectorize
138 | vxy2ll = np.vectorize(xy2ll) 139 | vll2xy = np.vectorize(ll2xy) 140 |
141 | 142 |
143 |
144 |
145 |
146 |
147 | 159 | 160 |
161 |
162 |
163 |
164 | 177 | 181 | 182 | -------------------------------------------------------------------------------- /doc/html/_modules/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Overview: module code — geonav_transform 0.0.1 documentation 10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 42 | 43 |
44 |
45 |
46 |
47 | 48 |

All modules for which code is available

49 | 52 | 53 |
54 |
55 |
56 |
57 |
58 | 70 | 71 |
72 |
73 |
74 |
75 | 87 | 91 | 92 | -------------------------------------------------------------------------------- /doc/html/_sources/alvinxy.txt: -------------------------------------------------------------------------------- 1 | alvinxy package 2 | =============== 3 | 4 | Submodules 5 | ---------- 6 | 7 | alvinxy.alvinxy module 8 | ---------------------- 9 | 10 | .. automodule:: alvinxy.alvinxy 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | alvinxy.alvinxy_example module 16 | ------------------------------ 17 | 18 | .. automodule:: alvinxy.alvinxy_example 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: alvinxy 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /doc/html/_sources/geonav_transform.txt: -------------------------------------------------------------------------------- 1 | geonav_transform package 2 | ======================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | geonav_transform.geonav_conversions module 8 | ------------------------------------------ 9 | 10 | .. automodule:: geonav_transform.geonav_conversions 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | geonav_transform.utmtest module 16 | ------------------------------- 17 | 18 | .. automodule:: geonav_transform.utmtest 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: geonav_transform 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /doc/html/_sources/index.txt: -------------------------------------------------------------------------------- 1 | .. geonav_transform documentation master file, created by 2 | sphinx-quickstart on Tue Mar 7 15:42:59 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to geonav_transform's documentation! 7 | ============================================ 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | 15 | 16 | Indices and tables 17 | ================== 18 | 19 | * :ref:`genindex` 20 | * :ref:`modindex` 21 | * :ref:`search` 22 | 23 | -------------------------------------------------------------------------------- /doc/html/_sources/modules.txt: -------------------------------------------------------------------------------- 1 | src 2 | === 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | alvinxy 8 | geonav_transform 9 | -------------------------------------------------------------------------------- /doc/html/_static/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/doc/html/_static/ajax-loader.gif -------------------------------------------------------------------------------- /doc/html/_static/basic.css: -------------------------------------------------------------------------------- 1 | /* 2 | * basic.css 3 | * ~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- basic theme. 6 | * 7 | * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /* -- main layout ----------------------------------------------------------- */ 13 | 14 | div.clearer { 15 | clear: both; 16 | } 17 | 18 | /* -- relbar ---------------------------------------------------------------- */ 19 | 20 | div.related { 21 | width: 100%; 22 | font-size: 90%; 23 | } 24 | 25 | div.related h3 { 26 | display: none; 27 | } 28 | 29 | div.related ul { 30 | margin: 0; 31 | padding: 0 0 0 10px; 32 | list-style: none; 33 | } 34 | 35 | div.related li { 36 | display: inline; 37 | } 38 | 39 | div.related li.right { 40 | float: right; 41 | margin-right: 5px; 42 | } 43 | 44 | /* -- sidebar --------------------------------------------------------------- */ 45 | 46 | div.sphinxsidebarwrapper { 47 | padding: 10px 5px 0 10px; 48 | } 49 | 50 | div.sphinxsidebar { 51 | float: left; 52 | width: 230px; 53 | margin-left: -100%; 54 | font-size: 90%; 55 | } 56 | 57 | div.sphinxsidebar ul { 58 | list-style: none; 59 | } 60 | 61 | div.sphinxsidebar ul ul, 62 | div.sphinxsidebar ul.want-points { 63 | margin-left: 20px; 64 | list-style: square; 65 | } 66 | 67 | div.sphinxsidebar ul ul { 68 | margin-top: 0; 69 | margin-bottom: 0; 70 | } 71 | 72 | div.sphinxsidebar form { 73 | margin-top: 10px; 74 | } 75 | 76 | div.sphinxsidebar input { 77 | border: 1px solid #98dbcc; 78 | font-family: sans-serif; 79 | font-size: 1em; 80 | } 81 | 82 | div.sphinxsidebar #searchbox input[type="text"] { 83 | width: 170px; 84 | } 85 | 86 | div.sphinxsidebar #searchbox input[type="submit"] { 87 | width: 30px; 88 | } 89 | 90 | img { 91 | border: 0; 92 | max-width: 100%; 93 | } 94 | 95 | /* -- search page ----------------------------------------------------------- */ 96 | 97 | ul.search { 98 | margin: 10px 0 0 20px; 99 | padding: 0; 100 | } 101 | 102 | ul.search li { 103 | padding: 5px 0 5px 20px; 104 | background-image: url(file.png); 105 | background-repeat: no-repeat; 106 | background-position: 0 7px; 107 | } 108 | 109 | ul.search li a { 110 | font-weight: bold; 111 | } 112 | 113 | ul.search li div.context { 114 | color: #888; 115 | margin: 2px 0 0 30px; 116 | text-align: left; 117 | } 118 | 119 | ul.keywordmatches li.goodmatch a { 120 | font-weight: bold; 121 | } 122 | 123 | /* -- index page ------------------------------------------------------------ */ 124 | 125 | table.contentstable { 126 | width: 90%; 127 | } 128 | 129 | table.contentstable p.biglink { 130 | line-height: 150%; 131 | } 132 | 133 | a.biglink { 134 | font-size: 1.3em; 135 | } 136 | 137 | span.linkdescr { 138 | font-style: italic; 139 | padding-top: 5px; 140 | font-size: 90%; 141 | } 142 | 143 | /* -- general index --------------------------------------------------------- */ 144 | 145 | table.indextable { 146 | width: 100%; 147 | } 148 | 149 | table.indextable td { 150 | text-align: left; 151 | vertical-align: top; 152 | } 153 | 154 | table.indextable dl, table.indextable dd { 155 | margin-top: 0; 156 | margin-bottom: 0; 157 | } 158 | 159 | table.indextable tr.pcap { 160 | height: 10px; 161 | } 162 | 163 | table.indextable tr.cap { 164 | margin-top: 10px; 165 | background-color: #f2f2f2; 166 | } 167 | 168 | img.toggler { 169 | margin-right: 3px; 170 | margin-top: 3px; 171 | cursor: pointer; 172 | } 173 | 174 | div.modindex-jumpbox { 175 | border-top: 1px solid #ddd; 176 | border-bottom: 1px solid #ddd; 177 | margin: 1em 0 1em 0; 178 | padding: 0.4em; 179 | } 180 | 181 | div.genindex-jumpbox { 182 | border-top: 1px solid #ddd; 183 | border-bottom: 1px solid #ddd; 184 | margin: 1em 0 1em 0; 185 | padding: 0.4em; 186 | } 187 | 188 | /* -- general body styles --------------------------------------------------- */ 189 | 190 | a.headerlink { 191 | visibility: hidden; 192 | } 193 | 194 | h1:hover > a.headerlink, 195 | h2:hover > a.headerlink, 196 | h3:hover > a.headerlink, 197 | h4:hover > a.headerlink, 198 | h5:hover > a.headerlink, 199 | h6:hover > a.headerlink, 200 | dt:hover > a.headerlink { 201 | visibility: visible; 202 | } 203 | 204 | div.body p.caption { 205 | text-align: inherit; 206 | } 207 | 208 | div.body td { 209 | text-align: left; 210 | } 211 | 212 | .field-list ul { 213 | padding-left: 1em; 214 | } 215 | 216 | .first { 217 | margin-top: 0 !important; 218 | } 219 | 220 | p.rubric { 221 | margin-top: 30px; 222 | font-weight: bold; 223 | } 224 | 225 | img.align-left, .figure.align-left, object.align-left { 226 | clear: left; 227 | float: left; 228 | margin-right: 1em; 229 | } 230 | 231 | img.align-right, .figure.align-right, object.align-right { 232 | clear: right; 233 | float: right; 234 | margin-left: 1em; 235 | } 236 | 237 | img.align-center, .figure.align-center, object.align-center { 238 | display: block; 239 | margin-left: auto; 240 | margin-right: auto; 241 | } 242 | 243 | .align-left { 244 | text-align: left; 245 | } 246 | 247 | .align-center { 248 | text-align: center; 249 | } 250 | 251 | .align-right { 252 | text-align: right; 253 | } 254 | 255 | /* -- sidebars -------------------------------------------------------------- */ 256 | 257 | div.sidebar { 258 | margin: 0 0 0.5em 1em; 259 | border: 1px solid #ddb; 260 | padding: 7px 7px 0 7px; 261 | background-color: #ffe; 262 | width: 40%; 263 | float: right; 264 | } 265 | 266 | p.sidebar-title { 267 | font-weight: bold; 268 | } 269 | 270 | /* -- topics ---------------------------------------------------------------- */ 271 | 272 | div.topic { 273 | border: 1px solid #ccc; 274 | padding: 7px 7px 0 7px; 275 | margin: 10px 0 10px 0; 276 | } 277 | 278 | p.topic-title { 279 | font-size: 1.1em; 280 | font-weight: bold; 281 | margin-top: 10px; 282 | } 283 | 284 | /* -- admonitions ----------------------------------------------------------- */ 285 | 286 | div.admonition { 287 | margin-top: 10px; 288 | margin-bottom: 10px; 289 | padding: 7px; 290 | } 291 | 292 | div.admonition dt { 293 | font-weight: bold; 294 | } 295 | 296 | div.admonition dl { 297 | margin-bottom: 0; 298 | } 299 | 300 | p.admonition-title { 301 | margin: 0px 10px 5px 0px; 302 | font-weight: bold; 303 | } 304 | 305 | div.body p.centered { 306 | text-align: center; 307 | margin-top: 25px; 308 | } 309 | 310 | /* -- tables ---------------------------------------------------------------- */ 311 | 312 | table.docutils { 313 | border: 0; 314 | border-collapse: collapse; 315 | } 316 | 317 | table.docutils td, table.docutils th { 318 | padding: 1px 8px 1px 5px; 319 | border-top: 0; 320 | border-left: 0; 321 | border-right: 0; 322 | border-bottom: 1px solid #aaa; 323 | } 324 | 325 | table.field-list td, table.field-list th { 326 | border: 0 !important; 327 | } 328 | 329 | table.footnote td, table.footnote th { 330 | border: 0 !important; 331 | } 332 | 333 | th { 334 | text-align: left; 335 | padding-right: 5px; 336 | } 337 | 338 | table.citation { 339 | border-left: solid 1px gray; 340 | margin-left: 1px; 341 | } 342 | 343 | table.citation td { 344 | border-bottom: none; 345 | } 346 | 347 | /* -- other body styles ----------------------------------------------------- */ 348 | 349 | ol.arabic { 350 | list-style: decimal; 351 | } 352 | 353 | ol.loweralpha { 354 | list-style: lower-alpha; 355 | } 356 | 357 | ol.upperalpha { 358 | list-style: upper-alpha; 359 | } 360 | 361 | ol.lowerroman { 362 | list-style: lower-roman; 363 | } 364 | 365 | ol.upperroman { 366 | list-style: upper-roman; 367 | } 368 | 369 | dl { 370 | margin-bottom: 15px; 371 | } 372 | 373 | dd p { 374 | margin-top: 0px; 375 | } 376 | 377 | dd ul, dd table { 378 | margin-bottom: 10px; 379 | } 380 | 381 | dd { 382 | margin-top: 3px; 383 | margin-bottom: 10px; 384 | margin-left: 30px; 385 | } 386 | 387 | dt:target, .highlighted { 388 | background-color: #fbe54e; 389 | } 390 | 391 | dl.glossary dt { 392 | font-weight: bold; 393 | font-size: 1.1em; 394 | } 395 | 396 | .field-list ul { 397 | margin: 0; 398 | padding-left: 1em; 399 | } 400 | 401 | .field-list p { 402 | margin: 0; 403 | } 404 | 405 | .optional { 406 | font-size: 1.3em; 407 | } 408 | 409 | .versionmodified { 410 | font-style: italic; 411 | } 412 | 413 | .system-message { 414 | background-color: #fda; 415 | padding: 5px; 416 | border: 3px solid red; 417 | } 418 | 419 | .footnote:target { 420 | background-color: #ffa; 421 | } 422 | 423 | .line-block { 424 | display: block; 425 | margin-top: 1em; 426 | margin-bottom: 1em; 427 | } 428 | 429 | .line-block .line-block { 430 | margin-top: 0; 431 | margin-bottom: 0; 432 | margin-left: 1.5em; 433 | } 434 | 435 | .guilabel, .menuselection { 436 | font-family: sans-serif; 437 | } 438 | 439 | .accelerator { 440 | text-decoration: underline; 441 | } 442 | 443 | .classifier { 444 | font-style: oblique; 445 | } 446 | 447 | abbr, acronym { 448 | border-bottom: dotted 1px; 449 | cursor: help; 450 | } 451 | 452 | /* -- code displays --------------------------------------------------------- */ 453 | 454 | pre { 455 | overflow: auto; 456 | overflow-y: hidden; /* fixes display issues on Chrome browsers */ 457 | } 458 | 459 | td.linenos pre { 460 | padding: 5px 0px; 461 | border: 0; 462 | background-color: transparent; 463 | color: #aaa; 464 | } 465 | 466 | table.highlighttable { 467 | margin-left: 0.5em; 468 | } 469 | 470 | table.highlighttable td { 471 | padding: 0 0.5em 0 0.5em; 472 | } 473 | 474 | tt.descname { 475 | background-color: transparent; 476 | font-weight: bold; 477 | font-size: 1.2em; 478 | } 479 | 480 | tt.descclassname { 481 | background-color: transparent; 482 | } 483 | 484 | tt.xref, a tt { 485 | background-color: transparent; 486 | font-weight: bold; 487 | } 488 | 489 | h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { 490 | background-color: transparent; 491 | } 492 | 493 | .viewcode-link { 494 | float: right; 495 | } 496 | 497 | .viewcode-back { 498 | float: right; 499 | font-family: sans-serif; 500 | } 501 | 502 | div.viewcode-block:target { 503 | margin: -1px -10px; 504 | padding: 0 10px; 505 | } 506 | 507 | /* -- math display ---------------------------------------------------------- */ 508 | 509 | img.math { 510 | vertical-align: middle; 511 | } 512 | 513 | div.body div.math p { 514 | text-align: center; 515 | } 516 | 517 | span.eqno { 518 | float: right; 519 | } 520 | 521 | /* -- printout stylesheet --------------------------------------------------- */ 522 | 523 | @media print { 524 | div.document, 525 | div.documentwrapper, 526 | div.bodywrapper { 527 | margin: 0 !important; 528 | width: 100%; 529 | } 530 | 531 | div.sphinxsidebar, 532 | div.related, 533 | div.footer, 534 | #top-link { 535 | display: none; 536 | } 537 | } -------------------------------------------------------------------------------- /doc/html/_static/comment-bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/doc/html/_static/comment-bright.png -------------------------------------------------------------------------------- /doc/html/_static/comment-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/doc/html/_static/comment-close.png -------------------------------------------------------------------------------- /doc/html/_static/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/doc/html/_static/comment.png -------------------------------------------------------------------------------- /doc/html/_static/default.css: -------------------------------------------------------------------------------- 1 | /* 2 | * default.css_t 3 | * ~~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- default theme. 6 | * 7 | * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: sans-serif; 18 | font-size: 100%; 19 | background-color: #11303d; 20 | color: #000; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.document { 26 | background-color: #1c4e63; 27 | } 28 | 29 | div.documentwrapper { 30 | float: left; 31 | width: 100%; 32 | } 33 | 34 | div.bodywrapper { 35 | margin: 0 0 0 230px; 36 | } 37 | 38 | div.body { 39 | background-color: #ffffff; 40 | color: #000000; 41 | padding: 0 20px 30px 20px; 42 | } 43 | 44 | div.footer { 45 | color: #ffffff; 46 | width: 100%; 47 | padding: 9px 0 9px 0; 48 | text-align: center; 49 | font-size: 75%; 50 | } 51 | 52 | div.footer a { 53 | color: #ffffff; 54 | text-decoration: underline; 55 | } 56 | 57 | div.related { 58 | background-color: #133f52; 59 | line-height: 30px; 60 | color: #ffffff; 61 | } 62 | 63 | div.related a { 64 | color: #ffffff; 65 | } 66 | 67 | div.sphinxsidebar { 68 | } 69 | 70 | div.sphinxsidebar h3 { 71 | font-family: 'Trebuchet MS', sans-serif; 72 | color: #ffffff; 73 | font-size: 1.4em; 74 | font-weight: normal; 75 | margin: 0; 76 | padding: 0; 77 | } 78 | 79 | div.sphinxsidebar h3 a { 80 | color: #ffffff; 81 | } 82 | 83 | div.sphinxsidebar h4 { 84 | font-family: 'Trebuchet MS', sans-serif; 85 | color: #ffffff; 86 | font-size: 1.3em; 87 | font-weight: normal; 88 | margin: 5px 0 0 0; 89 | padding: 0; 90 | } 91 | 92 | div.sphinxsidebar p { 93 | color: #ffffff; 94 | } 95 | 96 | div.sphinxsidebar p.topless { 97 | margin: 5px 10px 10px 10px; 98 | } 99 | 100 | div.sphinxsidebar ul { 101 | margin: 10px; 102 | padding: 0; 103 | color: #ffffff; 104 | } 105 | 106 | div.sphinxsidebar a { 107 | color: #98dbcc; 108 | } 109 | 110 | div.sphinxsidebar input { 111 | border: 1px solid #98dbcc; 112 | font-family: sans-serif; 113 | font-size: 1em; 114 | } 115 | 116 | 117 | 118 | /* -- hyperlink styles ------------------------------------------------------ */ 119 | 120 | a { 121 | color: #355f7c; 122 | text-decoration: none; 123 | } 124 | 125 | a:visited { 126 | color: #355f7c; 127 | text-decoration: none; 128 | } 129 | 130 | a:hover { 131 | text-decoration: underline; 132 | } 133 | 134 | 135 | 136 | /* -- body styles ----------------------------------------------------------- */ 137 | 138 | div.body h1, 139 | div.body h2, 140 | div.body h3, 141 | div.body h4, 142 | div.body h5, 143 | div.body h6 { 144 | font-family: 'Trebuchet MS', sans-serif; 145 | background-color: #f2f2f2; 146 | font-weight: normal; 147 | color: #20435c; 148 | border-bottom: 1px solid #ccc; 149 | margin: 20px -20px 10px -20px; 150 | padding: 3px 0 3px 10px; 151 | } 152 | 153 | div.body h1 { margin-top: 0; font-size: 200%; } 154 | div.body h2 { font-size: 160%; } 155 | div.body h3 { font-size: 140%; } 156 | div.body h4 { font-size: 120%; } 157 | div.body h5 { font-size: 110%; } 158 | div.body h6 { font-size: 100%; } 159 | 160 | a.headerlink { 161 | color: #c60f0f; 162 | font-size: 0.8em; 163 | padding: 0 4px 0 4px; 164 | text-decoration: none; 165 | } 166 | 167 | a.headerlink:hover { 168 | background-color: #c60f0f; 169 | color: white; 170 | } 171 | 172 | div.body p, div.body dd, div.body li { 173 | text-align: justify; 174 | line-height: 130%; 175 | } 176 | 177 | div.admonition p.admonition-title + p { 178 | display: inline; 179 | } 180 | 181 | div.admonition p { 182 | margin-bottom: 5px; 183 | } 184 | 185 | div.admonition pre { 186 | margin-bottom: 5px; 187 | } 188 | 189 | div.admonition ul, div.admonition ol { 190 | margin-bottom: 5px; 191 | } 192 | 193 | div.note { 194 | background-color: #eee; 195 | border: 1px solid #ccc; 196 | } 197 | 198 | div.seealso { 199 | background-color: #ffc; 200 | border: 1px solid #ff6; 201 | } 202 | 203 | div.topic { 204 | background-color: #eee; 205 | } 206 | 207 | div.warning { 208 | background-color: #ffe4e4; 209 | border: 1px solid #f66; 210 | } 211 | 212 | p.admonition-title { 213 | display: inline; 214 | } 215 | 216 | p.admonition-title:after { 217 | content: ":"; 218 | } 219 | 220 | pre { 221 | padding: 5px; 222 | background-color: #eeffcc; 223 | color: #333333; 224 | line-height: 120%; 225 | border: 1px solid #ac9; 226 | border-left: none; 227 | border-right: none; 228 | } 229 | 230 | tt { 231 | background-color: #ecf0f3; 232 | padding: 0 1px 0 1px; 233 | font-size: 0.95em; 234 | } 235 | 236 | th { 237 | background-color: #ede; 238 | } 239 | 240 | .warning tt { 241 | background: #efc2c2; 242 | } 243 | 244 | .note tt { 245 | background: #d6d6d6; 246 | } 247 | 248 | .viewcode-back { 249 | font-family: sans-serif; 250 | } 251 | 252 | div.viewcode-block:target { 253 | background-color: #f4debf; 254 | border-top: 1px solid #ac9; 255 | border-bottom: 1px solid #ac9; 256 | } -------------------------------------------------------------------------------- /doc/html/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Sphinx JavaScript utilities for all documentation. 6 | * 7 | * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /** 13 | * select a different prefix for underscore 14 | */ 15 | $u = _.noConflict(); 16 | 17 | /** 18 | * make the code below compatible with browsers without 19 | * an installed firebug like debugger 20 | if (!window.console || !console.firebug) { 21 | var names = ["log", "debug", "info", "warn", "error", "assert", "dir", 22 | "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", 23 | "profile", "profileEnd"]; 24 | window.console = {}; 25 | for (var i = 0; i < names.length; ++i) 26 | window.console[names[i]] = function() {}; 27 | } 28 | */ 29 | 30 | /** 31 | * small helper function to urldecode strings 32 | */ 33 | jQuery.urldecode = function(x) { 34 | return decodeURIComponent(x).replace(/\+/g, ' '); 35 | }; 36 | 37 | /** 38 | * small helper function to urlencode strings 39 | */ 40 | jQuery.urlencode = encodeURIComponent; 41 | 42 | /** 43 | * This function returns the parsed url parameters of the 44 | * current request. Multiple values per key are supported, 45 | * it will always return arrays of strings for the value parts. 46 | */ 47 | jQuery.getQueryParameters = function(s) { 48 | if (typeof s == 'undefined') 49 | s = document.location.search; 50 | var parts = s.substr(s.indexOf('?') + 1).split('&'); 51 | var result = {}; 52 | for (var i = 0; i < parts.length; i++) { 53 | var tmp = parts[i].split('=', 2); 54 | var key = jQuery.urldecode(tmp[0]); 55 | var value = jQuery.urldecode(tmp[1]); 56 | if (key in result) 57 | result[key].push(value); 58 | else 59 | result[key] = [value]; 60 | } 61 | return result; 62 | }; 63 | 64 | /** 65 | * highlight a given string on a jquery object by wrapping it in 66 | * span elements with the given class name. 67 | */ 68 | jQuery.fn.highlightText = function(text, className) { 69 | function highlight(node) { 70 | if (node.nodeType == 3) { 71 | var val = node.nodeValue; 72 | var pos = val.toLowerCase().indexOf(text); 73 | if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) { 74 | var span = document.createElement("span"); 75 | span.className = className; 76 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 77 | node.parentNode.insertBefore(span, node.parentNode.insertBefore( 78 | document.createTextNode(val.substr(pos + text.length)), 79 | node.nextSibling)); 80 | node.nodeValue = val.substr(0, pos); 81 | } 82 | } 83 | else if (!jQuery(node).is("button, select, textarea")) { 84 | jQuery.each(node.childNodes, function() { 85 | highlight(this); 86 | }); 87 | } 88 | } 89 | return this.each(function() { 90 | highlight(this); 91 | }); 92 | }; 93 | 94 | /** 95 | * Small JavaScript module for the documentation. 96 | */ 97 | var Documentation = { 98 | 99 | init : function() { 100 | this.fixFirefoxAnchorBug(); 101 | this.highlightSearchWords(); 102 | this.initIndexTable(); 103 | }, 104 | 105 | /** 106 | * i18n support 107 | */ 108 | TRANSLATIONS : {}, 109 | PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, 110 | LOCALE : 'unknown', 111 | 112 | // gettext and ngettext don't access this so that the functions 113 | // can safely bound to a different name (_ = Documentation.gettext) 114 | gettext : function(string) { 115 | var translated = Documentation.TRANSLATIONS[string]; 116 | if (typeof translated == 'undefined') 117 | return string; 118 | return (typeof translated == 'string') ? translated : translated[0]; 119 | }, 120 | 121 | ngettext : function(singular, plural, n) { 122 | var translated = Documentation.TRANSLATIONS[singular]; 123 | if (typeof translated == 'undefined') 124 | return (n == 1) ? singular : plural; 125 | return translated[Documentation.PLURALEXPR(n)]; 126 | }, 127 | 128 | addTranslations : function(catalog) { 129 | for (var key in catalog.messages) 130 | this.TRANSLATIONS[key] = catalog.messages[key]; 131 | this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); 132 | this.LOCALE = catalog.locale; 133 | }, 134 | 135 | /** 136 | * add context elements like header anchor links 137 | */ 138 | addContextElements : function() { 139 | $('div[id] > :header:first').each(function() { 140 | $('\u00B6'). 141 | attr('href', '#' + this.id). 142 | attr('title', _('Permalink to this headline')). 143 | appendTo(this); 144 | }); 145 | $('dt[id]').each(function() { 146 | $('\u00B6'). 147 | attr('href', '#' + this.id). 148 | attr('title', _('Permalink to this definition')). 149 | appendTo(this); 150 | }); 151 | }, 152 | 153 | /** 154 | * workaround a firefox stupidity 155 | */ 156 | fixFirefoxAnchorBug : function() { 157 | if (document.location.hash && $.browser.mozilla) 158 | window.setTimeout(function() { 159 | document.location.href += ''; 160 | }, 10); 161 | }, 162 | 163 | /** 164 | * highlight the search words provided in the url in the text 165 | */ 166 | highlightSearchWords : function() { 167 | var params = $.getQueryParameters(); 168 | var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; 169 | if (terms.length) { 170 | var body = $('div.body'); 171 | if (!body.length) { 172 | body = $('body'); 173 | } 174 | window.setTimeout(function() { 175 | $.each(terms, function() { 176 | body.highlightText(this.toLowerCase(), 'highlighted'); 177 | }); 178 | }, 10); 179 | $('') 181 | .appendTo($('#searchbox')); 182 | } 183 | }, 184 | 185 | /** 186 | * init the domain index toggle buttons 187 | */ 188 | initIndexTable : function() { 189 | var togglers = $('img.toggler').click(function() { 190 | var src = $(this).attr('src'); 191 | var idnum = $(this).attr('id').substr(7); 192 | $('tr.cg-' + idnum).toggle(); 193 | if (src.substr(-9) == 'minus.png') 194 | $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); 195 | else 196 | $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); 197 | }).css('display', ''); 198 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { 199 | togglers.click(); 200 | } 201 | }, 202 | 203 | /** 204 | * helper function to hide the search marks again 205 | */ 206 | hideSearchWords : function() { 207 | $('#searchbox .highlight-link').fadeOut(300); 208 | $('span.highlighted').removeClass('highlighted'); 209 | }, 210 | 211 | /** 212 | * make the url absolute 213 | */ 214 | makeURL : function(relativeURL) { 215 | return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; 216 | }, 217 | 218 | /** 219 | * get the current relative url 220 | */ 221 | getCurrentURL : function() { 222 | var path = document.location.pathname; 223 | var parts = path.split(/\//); 224 | $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { 225 | if (this == '..') 226 | parts.pop(); 227 | }); 228 | var url = parts.join('/'); 229 | return path.substring(url.lastIndexOf('/') + 1, path.length - 1); 230 | } 231 | }; 232 | 233 | // quick alias for translations 234 | _ = Documentation.gettext; 235 | 236 | $(document).ready(function() { 237 | Documentation.init(); 238 | }); 239 | -------------------------------------------------------------------------------- /doc/html/_static/down-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/doc/html/_static/down-pressed.png -------------------------------------------------------------------------------- /doc/html/_static/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/doc/html/_static/down.png -------------------------------------------------------------------------------- /doc/html/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/doc/html/_static/file.png -------------------------------------------------------------------------------- /doc/html/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/doc/html/_static/minus.png -------------------------------------------------------------------------------- /doc/html/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/doc/html/_static/plus.png -------------------------------------------------------------------------------- /doc/html/_static/pygments.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #eeffcc; } 3 | .highlight .c { color: #408090; font-style: italic } /* Comment */ 4 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 5 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #666666 } /* Operator */ 7 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ 8 | .highlight .cp { color: #007020 } /* Comment.Preproc */ 9 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ 10 | .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ 11 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 12 | .highlight .ge { font-style: italic } /* Generic.Emph */ 13 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 14 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 15 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 16 | .highlight .go { color: #333333 } /* Generic.Output */ 17 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 18 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 19 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 20 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 21 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 22 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 23 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 24 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */ 25 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 26 | .highlight .kt { color: #902000 } /* Keyword.Type */ 27 | .highlight .m { color: #208050 } /* Literal.Number */ 28 | .highlight .s { color: #4070a0 } /* Literal.String */ 29 | .highlight .na { color: #4070a0 } /* Name.Attribute */ 30 | .highlight .nb { color: #007020 } /* Name.Builtin */ 31 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 32 | .highlight .no { color: #60add5 } /* Name.Constant */ 33 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ 34 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ 35 | .highlight .ne { color: #007020 } /* Name.Exception */ 36 | .highlight .nf { color: #06287e } /* Name.Function */ 37 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 38 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 39 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ 40 | .highlight .nv { color: #bb60d5 } /* Name.Variable */ 41 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 42 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 43 | .highlight .mf { color: #208050 } /* Literal.Number.Float */ 44 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */ 45 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */ 46 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */ 47 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ 48 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */ 49 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ 50 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ 51 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ 52 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ 53 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ 54 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */ 55 | .highlight .sr { color: #235388 } /* Literal.String.Regex */ 56 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ 57 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */ 58 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 59 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ 60 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ 61 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ 62 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /doc/html/_static/sidebar.js: -------------------------------------------------------------------------------- 1 | /* 2 | * sidebar.js 3 | * ~~~~~~~~~~ 4 | * 5 | * This script makes the Sphinx sidebar collapsible. 6 | * 7 | * .sphinxsidebar contains .sphinxsidebarwrapper. This script adds 8 | * in .sphixsidebar, after .sphinxsidebarwrapper, the #sidebarbutton 9 | * used to collapse and expand the sidebar. 10 | * 11 | * When the sidebar is collapsed the .sphinxsidebarwrapper is hidden 12 | * and the width of the sidebar and the margin-left of the document 13 | * are decreased. When the sidebar is expanded the opposite happens. 14 | * This script saves a per-browser/per-session cookie used to 15 | * remember the position of the sidebar among the pages. 16 | * Once the browser is closed the cookie is deleted and the position 17 | * reset to the default (expanded). 18 | * 19 | * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. 20 | * :license: BSD, see LICENSE for details. 21 | * 22 | */ 23 | 24 | $(function() { 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | // global elements used by the functions. 34 | // the 'sidebarbutton' element is defined as global after its 35 | // creation, in the add_sidebar_button function 36 | var bodywrapper = $('.bodywrapper'); 37 | var sidebar = $('.sphinxsidebar'); 38 | var sidebarwrapper = $('.sphinxsidebarwrapper'); 39 | 40 | // for some reason, the document has no sidebar; do not run into errors 41 | if (!sidebar.length) return; 42 | 43 | // original margin-left of the bodywrapper and width of the sidebar 44 | // with the sidebar expanded 45 | var bw_margin_expanded = bodywrapper.css('margin-left'); 46 | var ssb_width_expanded = sidebar.width(); 47 | 48 | // margin-left of the bodywrapper and width of the sidebar 49 | // with the sidebar collapsed 50 | var bw_margin_collapsed = '.8em'; 51 | var ssb_width_collapsed = '.8em'; 52 | 53 | // colors used by the current theme 54 | var dark_color = $('.related').css('background-color'); 55 | var light_color = $('.document').css('background-color'); 56 | 57 | function sidebar_is_collapsed() { 58 | return sidebarwrapper.is(':not(:visible)'); 59 | } 60 | 61 | function toggle_sidebar() { 62 | if (sidebar_is_collapsed()) 63 | expand_sidebar(); 64 | else 65 | collapse_sidebar(); 66 | } 67 | 68 | function collapse_sidebar() { 69 | sidebarwrapper.hide(); 70 | sidebar.css('width', ssb_width_collapsed); 71 | bodywrapper.css('margin-left', bw_margin_collapsed); 72 | sidebarbutton.css({ 73 | 'margin-left': '0', 74 | 'height': bodywrapper.height() 75 | }); 76 | sidebarbutton.find('span').text('»'); 77 | sidebarbutton.attr('title', _('Expand sidebar')); 78 | document.cookie = 'sidebar=collapsed'; 79 | } 80 | 81 | function expand_sidebar() { 82 | bodywrapper.css('margin-left', bw_margin_expanded); 83 | sidebar.css('width', ssb_width_expanded); 84 | sidebarwrapper.show(); 85 | sidebarbutton.css({ 86 | 'margin-left': ssb_width_expanded-12, 87 | 'height': bodywrapper.height() 88 | }); 89 | sidebarbutton.find('span').text('«'); 90 | sidebarbutton.attr('title', _('Collapse sidebar')); 91 | document.cookie = 'sidebar=expanded'; 92 | } 93 | 94 | function add_sidebar_button() { 95 | sidebarwrapper.css({ 96 | 'float': 'left', 97 | 'margin-right': '0', 98 | 'width': ssb_width_expanded - 28 99 | }); 100 | // create the button 101 | sidebar.append( 102 | '
«
' 103 | ); 104 | var sidebarbutton = $('#sidebarbutton'); 105 | light_color = sidebarbutton.css('background-color'); 106 | // find the height of the viewport to center the '<<' in the page 107 | var viewport_height; 108 | if (window.innerHeight) 109 | viewport_height = window.innerHeight; 110 | else 111 | viewport_height = $(window).height(); 112 | sidebarbutton.find('span').css({ 113 | 'display': 'block', 114 | 'margin-top': (viewport_height - sidebar.position().top - 20) / 2 115 | }); 116 | 117 | sidebarbutton.click(toggle_sidebar); 118 | sidebarbutton.attr('title', _('Collapse sidebar')); 119 | sidebarbutton.css({ 120 | 'color': '#FFFFFF', 121 | 'border-left': '1px solid ' + dark_color, 122 | 'font-size': '1.2em', 123 | 'cursor': 'pointer', 124 | 'height': bodywrapper.height(), 125 | 'padding-top': '1px', 126 | 'margin-left': ssb_width_expanded - 12 127 | }); 128 | 129 | sidebarbutton.hover( 130 | function () { 131 | $(this).css('background-color', dark_color); 132 | }, 133 | function () { 134 | $(this).css('background-color', light_color); 135 | } 136 | ); 137 | } 138 | 139 | function set_position_from_cookie() { 140 | if (!document.cookie) 141 | return; 142 | var items = document.cookie.split(';'); 143 | for(var k=0; k 3 | 4 | 5 | 6 | 7 | 8 | 9 | alvinxy package — geonav_transform 0.0.1 documentation 10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 42 | 43 |
44 |
45 |
46 |
47 | 48 |
49 |

alvinxy package

50 |
51 |

Submodules

52 |
53 |
54 |

alvinxy.alvinxy module

55 |

AlvinXY Utility Module

56 |

All of this is based on WHOI’s original implementations in C and MATLAB.

57 |
58 |
59 | alvinxy.alvinxy.ll2xy(lat, lon, orglat, orglon)[source]
60 |

AlvinXY: Lat/Long to X/Y 61 | Converts Lat/Lon (WGS84) to Alvin XYs using a Mercator projection.

62 |
63 |
Args:
64 |
lat (float): Latitude of location 65 | lon (float): Longitude of location 66 | orglat (float): Latitude of origin location 67 | orglon (float): Longitude of origin location
68 |
Returns:
69 |
70 |
tuple: (x,y) where...
71 |
x is Easting in m (Alvin local grid) 72 | y is Northing in m (Alvin local grid)
73 |
74 |
75 |
76 |
77 | 78 |
79 |
80 | alvinxy.alvinxy.mdeglat(lat)[source]
81 |

Provides meters-per-degree latitude at a given latitude

82 |
83 |
Args:
84 |
lat (float): latitude
85 |
Returns:
86 |
float: meters-per-degree value
87 |
88 |
89 | 90 |
91 |
92 | alvinxy.alvinxy.mdeglon(lat)[source]
93 |

Provides meters-per-degree longitude at a given latitude

94 |
95 |
Args:
96 |
lat (float): latitude in decimal degrees
97 |
Returns:
98 |
float: meters per degree longitude
99 |
100 |
101 | 102 |
103 |
104 | alvinxy.alvinxy.xy2ll(x, y, orglat, orglon)[source]
105 |

X/Y to Lat/Lon 106 | Converts Alvin XYs to Lat/Lon (WGS84) using a Mercator projection.

107 |
108 |
Args:
109 |
x (float): Easting in m (Alvin local grid) 110 | x (float): Northing in m (Alvin local grid) 111 | orglat (float): Latitude of origin location 112 | orglon (float): Longitude of origin location
113 |
Returns:
114 |
tuple: (lat,lon)
115 |
116 |
117 | 118 |
119 |
120 |

alvinxy.alvinxy_example module

121 |

Here is an example of using lat/lon along with AlvinXY coordinates.

122 |
123 |
124 |

Module contents

125 |
126 |
127 | 128 | 129 |
130 |
131 |
132 |
133 |
134 |

Table Of Contents

135 | 144 | 145 |

This Page

146 | 150 | 162 | 163 |
164 |
165 |
166 |
167 | 179 | 183 | 184 | -------------------------------------------------------------------------------- /doc/html/genindex.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Index — geonav_transform 0.0.1 documentation 11 | 12 | 13 | 14 | 15 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 43 | 44 |
45 |
46 |
47 |
48 | 49 | 50 |

Index

51 | 52 |
53 | A 54 | | G 55 | | L 56 | | M 57 | | U 58 | | X 59 | 60 |
61 |

A

62 | 63 | 73 | 79 |
64 | 65 |
alvinxy (module) 66 |
67 | 68 | 69 |
alvinxy.alvinxy (module) 70 |
71 | 72 |
74 | 75 |
alvinxy.alvinxy_example (module) 76 |
77 | 78 |
80 | 81 |

G

82 | 83 | 93 | 99 |
84 | 85 |
geonav_transform (module) 86 |
87 | 88 | 89 |
geonav_transform.geonav_conversions (module) 90 |
91 | 92 |
94 | 95 |
geonav_transform.utmtest (module) 96 |
97 | 98 |
100 | 101 |

L

102 | 103 | 115 | 121 |
104 | 105 |
ll2xy() (in module alvinxy.alvinxy) 106 |
107 | 108 |
109 | 110 |
(in module geonav_transform.geonav_conversions) 111 |
112 | 113 |
114 |
116 | 117 |
LLtoUTM() (in module geonav_transform.geonav_conversions) 118 |
119 | 120 |
122 | 123 |

M

124 | 125 | 131 | 137 |
126 | 127 |
mdeglat() (in module alvinxy.alvinxy) 128 |
129 | 130 |
132 | 133 |
mdeglon() (in module alvinxy.alvinxy) 134 |
135 | 136 |
138 | 139 |

U

140 | 141 | 147 | 153 |
142 | 143 |
UTMLetterDesignator() (in module geonav_transform.geonav_conversions) 144 |
145 | 146 |
148 | 149 |
UTMtoLL() (in module geonav_transform.geonav_conversions) 150 |
151 | 152 |
154 | 155 |

X

156 | 157 | 169 |
158 | 159 |
xy2ll() (in module alvinxy.alvinxy) 160 |
161 | 162 |
163 | 164 |
(in module geonav_transform.geonav_conversions) 165 |
166 | 167 |
168 |
170 | 171 | 172 | 173 |
174 |
175 |
176 |
177 |
178 | 179 | 180 | 181 | 193 | 194 |
195 |
196 |
197 |
198 | 210 | 214 | 215 | -------------------------------------------------------------------------------- /doc/html/geonav_transform.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | geonav_transform package — geonav_transform 0.0.1 documentation 10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 42 | 43 |
44 |
45 |
46 |
47 | 48 |
49 |

geonav_transform package

50 |
51 |

Submodules

52 |
53 |
54 |

geonav_transform.geonav_conversions module

55 |

Python version of the inline functions defined in the robot_localization, 56 | navsat_conversions.h

57 |
58 |
59 | geonav_transform.geonav_conversions.LLtoUTM(Lat, Long)[source]
60 |
61 | 62 |
63 |
64 | geonav_transform.geonav_conversions.UTMLetterDesignator(Lat)[source]
65 |
66 | 67 |
68 |
69 | geonav_transform.geonav_conversions.UTMtoLL(UTMNorthing, UTMEasting, UTMZone)[source]
70 |
71 | 72 |
73 |
74 | geonav_transform.geonav_conversions.ll2xy(lat, lon, origin_lat, origin_lon)[source]
75 |

Geonav: Lat/Long to X/Y 76 | Convert latitude and longitude in dec. degress to x and y in meters 77 | relative to the given origin location. Converts lat/lon and orgin to UTM and then takes the difference

78 |
79 |
Args:
80 |
lat (float): Latitude of location 81 | lon (float): Longitude of location 82 | orglat (float): Latitude of origin location 83 | orglon (float): Longitude of origin location
84 |
Returns:
85 |
86 |
tuple: (x,y) where...
87 |
x is Easting in m (local grid) 88 | y is Northing in m (local grid)
89 |
90 |
91 |
92 |
93 | 94 |
95 |
96 | geonav_transform.geonav_conversions.xy2ll(x, y, orglat, orglon)[source]
97 |
98 | 99 |
100 |
101 |

geonav_transform.utmtest module

102 |
103 |
104 |

Module contents

105 |
106 |
107 | 108 | 109 |
110 |
111 |
112 |
113 |
114 |

Table Of Contents

115 | 124 | 125 |

This Page

126 | 130 | 142 | 143 |
144 |
145 |
146 |
147 | 159 | 163 | 164 | -------------------------------------------------------------------------------- /doc/html/index-msg.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | geonav_transform Msg/Srv Documentation 4 | 5 | 6 | 7 | 8 |
9 | 10 |

geonav_transform Msg/Srv Documentation

11 | 12 |
13 | See also: 14 | 15 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /doc/html/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Welcome to geonav_transform’s documentation! — geonav_transform 0.0.1 documentation 10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 42 | 43 |
44 |
45 |
46 |
47 | 48 |
49 |

Welcome to geonav_transform’s documentation!

50 |

Contents:

51 |
52 |
    53 |
54 |
55 |
56 |
57 |

Indices and tables

58 | 63 |
64 | 65 | 66 |
67 |
68 |
69 |
70 |
71 |

Table Of Contents

72 | 76 | 77 |

This Page

78 | 82 | 94 | 95 |
96 |
97 |
98 |
99 | 111 | 115 | 116 | -------------------------------------------------------------------------------- /doc/html/modules.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | src — geonav_transform 0.0.1 documentation 10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 42 | 43 |
44 |
45 |
46 | 72 |
73 |
74 |
75 |
76 |

This Page

77 | 81 | 93 | 94 |
95 |
96 |
97 |
98 | 110 | 114 | 115 | -------------------------------------------------------------------------------- /doc/html/msg-styles.css: -------------------------------------------------------------------------------- 1 | html { 2 | background: #333; 3 | color: #333; 4 | font-family: 'Palatino', serif; 5 | font-size: 18px; 6 | line-height: 24px; 7 | text-align: center; 8 | padding: 0px 50px 0px 50px; 9 | } 10 | #container { 11 | background: white; 12 | margin: 25px auto 100px auto; 13 | padding: 50px 50px; 14 | text-align: left; 15 | } 16 | a { 17 | text-decoration: none; 18 | } 19 | a:hover { 20 | text-decoration: underline; 21 | } 22 | .msg-title { 23 | margin-bottom: 40px; 24 | border-bottom: 1px solid #333; 25 | padding: 10px 10px 10px 0px; 26 | } 27 | .filename { 28 | padding: 5px; 29 | background-color: #eee; 30 | border: 1px solid #333; 31 | } 32 | .fancy-msg { 33 | width: auto; 34 | font-size: 14px; 35 | line-height: 16px; 36 | border: 1px solid #333; 37 | padding: 10px; 38 | margin-bottom: 20px; 39 | } 40 | 41 | .raw-msg { 42 | font-family: monospace; 43 | width: auto; 44 | font-size: 14px; 45 | line-height: 16px; 46 | padding: 5px; 47 | border: 1px solid #333; 48 | margin-bottom: 20px; 49 | } 50 | -------------------------------------------------------------------------------- /doc/html/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/doc/html/objects.inv -------------------------------------------------------------------------------- /doc/html/py-modindex.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Python Module Index — geonav_transform 0.0.1 documentation 10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 45 | 46 |
47 |
48 |
49 |
50 | 51 | 52 |

Python Module Index

53 | 54 |
55 | a | 56 | g 57 |
58 | 59 | 60 | 61 | 63 | 64 | 66 | 69 | 70 | 71 | 74 | 75 | 76 | 79 | 80 | 82 | 83 | 85 | 88 | 89 | 90 | 93 | 94 | 95 | 98 |
 
62 | a
67 | alvinxy 68 |
    72 | alvinxy.alvinxy 73 |
    77 | alvinxy.alvinxy_example 78 |
 
81 | g
86 | geonav_transform 87 |
    91 | geonav_transform.geonav_conversions 92 |
    96 | geonav_transform.utmtest 97 |
99 | 100 | 101 |
102 |
103 |
104 |
105 |
106 | 118 | 119 |
120 |
121 |
122 |
123 | 135 | 139 | 140 | -------------------------------------------------------------------------------- /doc/html/search.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Search — geonav_transform 0.0.1 documentation 10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 50 | 51 |
52 |
53 |
54 |
55 | 56 |

Search

57 |
58 | 59 |

60 | Please activate JavaScript to enable the search 61 | functionality. 62 |

63 |
64 |

65 | From here you can search these documents. Enter your search 66 | words into the box below and click "search". Note that the search 67 | function will automatically search for all of the words. Pages 68 | containing fewer words won't appear in the result list. 69 |

70 |
71 | 72 | 73 | 74 |
75 | 76 |
77 | 78 |
79 | 80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | 101 | 105 | 106 | -------------------------------------------------------------------------------- /doc/html/searchindex.js: -------------------------------------------------------------------------------- 1 | Search.setIndex({envversion:42,terms:{origin:[0,2],latitud:[0,2],all:0,alvin:0,utmnorth:2,mercat:0,orglon:[0,2],lon:[0,2],modul:[],"float":[0,2],ll2xy:[0,2],lat:[0,2],navsat_convers:2,"long":[0,2],arg:[0,2],wgs84:0,search:1,orgin:2,index:1,differ:2,locat:[0,2],decim:0,utmeast:2,degre:0,per:0,content:[],version:2,take:2,rel:2,utmletterdesign:2,east:[0,2],local:[0,2],matlab:0,origin_lat:2,whoi:0,"function":2,sourc:[0,2],"return":[0,2],thi:0,robot_loc:2,python:2,tupl:[0,2],given:[0,2],here:0,util:0,coordin:0,base:0,grid:[0,2],origin_lon:2,utmtol:2,geonav:2,mdeglat:0,along:0,inlin:2,implement:0,valu:0,convert:[0,2],xy2ll:[0,2],orglat:[0,2],provid:0,mdeglon:0,utm:2,project:0,defin:2,lltoutm:2,exampl:0,degress:2,utmzon:2,north:[0,2],meter:[0,2],dec:2,where:[0,2],page:1,longitud:[0,2]},objtypes:{"0":"py:module","1":"py:function"},objnames:{"0":["py","module","Python module"],"1":["py","function","Python function"]},filenames:["alvinxy","index","geonav_transform","modules"],titles:["alvinxy package","Welcome to geonav_transform’s documentation!","geonav_transform package","src"],objects:{"":{alvinxy:[0,0,0,"-"],geonav_transform:[2,0,0,"-"]},alvinxy:{alvinxy:[0,0,0,"-"],alvinxy_example:[0,0,0,"-"]},geonav_transform:{geonav_conversions:[2,0,0,"-"],utmtest:[2,0,0,"-"]},"geonav_transform.geonav_conversions":{UTMtoLL:[2,1,1,""],UTMLetterDesignator:[2,1,1,""],LLtoUTM:[2,1,1,""],xy2ll:[2,1,1,""],ll2xy:[2,1,1,""]},"alvinxy.alvinxy":{mdeglon:[0,1,1,""],xy2ll:[0,1,1,""],ll2xy:[0,1,1,""],mdeglat:[0,1,1,""]}},titleterms:{src:3,geonav_transform:[1,2],welcom:1,alvinxy_exampl:0,modul:[0,2],utmtest:2,submodul:[0,2],indic:1,content:[0,2],packag:[0,2],tabl:1,geonav_convers:2,alvinxi:0,document:1}}) -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | .. geonav_transform documentation master file, created by 2 | sphinx-quickstart on Tue Mar 7 15:42:59 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to geonav_transform's documentation! 7 | ============================================ 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | 15 | 16 | Indices and tables 17 | ================== 18 | 19 | * :ref:`genindex` 20 | * :ref:`modindex` 21 | * :ref:`search` 22 | 23 | -------------------------------------------------------------------------------- /doc/manifest.yaml: -------------------------------------------------------------------------------- 1 | actions: [] 2 | authors: Brian Bingham 3 | brief: '' 4 | bugtracker: '' 5 | depends: 6 | - nav_msgs 7 | - geographic_msgs 8 | - catkin 9 | - eigen 10 | - tf2_geometry_msgs 11 | - std_msgs 12 | - sensor_msgs 13 | - cmake_modules 14 | - python-catkin-pkg 15 | - robot_localization 16 | - rospy 17 | - tf2_ros 18 | - geometry_msgs 19 | - tf2 20 | - roscpp 21 | description: The geonav_transform package 22 | license: GPL 23 | maintainers: Brian Bingham 24 | msgs: [] 25 | package_type: package 26 | repo_url: '' 27 | srvs: [] 28 | url: https://github.com/bsb808/geonav_transform 29 | -------------------------------------------------------------------------------- /doc/modules.rst: -------------------------------------------------------------------------------- 1 | src 2 | === 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | alvinxy 8 | geonav_transform 9 | -------------------------------------------------------------------------------- /examples/compare_alvin_geonav.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Compare AlvinXY with Geonav 3 | 4 | Should be run with `ipython --pylab` 5 | ''' 6 | import numpy as np 7 | 8 | from alvinxy import alvinxy as axy 9 | #import alvinxy.alvinxy as axy 10 | reload(axy) 11 | 12 | from geonav_transform import geonav_conversions as gc 13 | #import geonav_transform.geonav_conversions as gc 14 | reload(gc) 15 | 16 | # Set an origin near NPS. 17 | originlat = 36.59 18 | originlon = -121.89 19 | nx = 10 20 | dx = 100000 21 | ny = 10 22 | dy = 100000 23 | xv, yv = np.meshgrid(np.linspace(0,dx,nx), 24 | np.linspace(0,dy,ny)) 25 | alatv = np.zeros(yv.shape) 26 | alonv = np.zeros(xv.shape) 27 | glatv = np.zeros(yv.shape) 28 | glonv = np.zeros(xv.shape) 29 | 30 | for ii in range(nx): 31 | for jj in range(ny): 32 | x = xv[ii,jj] 33 | y = yv[ii,jj] 34 | alatv[ii,jj], alonv[ii,jj] = axy.xy2ll(x,y,originlat,originlon) 35 | glatv[ii,jj], glonv[ii,jj] = gc.xy2ll(x,y,originlat,originlon) 36 | 37 | figure(1) 38 | clf() 39 | plot(xv.flatten(),yv.flatten(),'.') 40 | grid(True) 41 | 42 | 43 | figure(2) 44 | clf() 45 | plot(alonv.flatten(),alatv.flatten(),'r.',label='AlvinXY') 46 | hold(True) 47 | plot(glonv.flatten(),glatv.flatten(),'g.',label='Geonav') 48 | grid(True) 49 | xlabel('Easting [m]') 50 | ylabel('Northing [m]') 51 | title('Comparison of AlvinXY and Geonav for regular spaced X/Y') 52 | legend() 53 | 54 | # Now start with lat/lon 55 | nx = 10 56 | dx = 1.0 57 | ny = 10 58 | dy = 1.0 59 | lonv, latv = np.meshgrid(np.linspace(0,dx,nx), 60 | np.linspace(0,dy,ny)) 61 | axv = np.zeros(lonv.shape) 62 | ayv = np.zeros(latv.shape) 63 | gxv = np.zeros(lonv.shape) 64 | gyv = np.zeros(latv.shape) 65 | 66 | for ii in range(nx): 67 | for jj in range(ny): 68 | lat = originlat+latv[ii,jj] 69 | lon = originlon+lonv[ii,jj] 70 | axv[ii,jj], ayv[ii,jj] = axy.ll2xy(lat,lon,originlat,originlon) 71 | gxv[ii,jj], gyv[ii,jj] = gc.ll2xy(lat,lon,originlat,originlon) 72 | 73 | figure(3) 74 | clf() 75 | plot(lonv.flatten(),latv.flatten(),'.') 76 | grid(True) 77 | 78 | 79 | figure(4) 80 | clf() 81 | plot(axv.flatten(),ayv.flatten(),'r.',label='AlvinXY') 82 | hold(True) 83 | plot(gxv.flatten(),gyv.flatten(),'g.',label='Geonav') 84 | grid(True) 85 | xlabel('Longitude [deg]') 86 | ylabel('Latitude [m]') 87 | title('Comparison of AlvinXY and Geonav for regular spaced Lat/Lon') 88 | legend() 89 | 90 | 91 | 92 | show() 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /examples/gazebo_elestero_ex.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | In this example we use the geonav coordinate system to move between three 4 | coordinate systems 5 | 1) Lat/lon 6 | 2) Local geonav coordinates 7 | 3) Gazebo local coordinates 8 | 9 | ''' 10 | 11 | # Import geonav tranformation module 12 | import geonav_transform.geonav_conversions as gc 13 | reload(gc) 14 | 15 | # Define a local orgin, latitude and longitude in decimal degrees 16 | # This is the standard geonav origin for working at El Estero Lake 17 | olat = 36.595 18 | olon = -121.89 19 | 20 | # This is somewhat confusing, but Gazebo also has a local coordinate system. 21 | # In the elestero.launch file we define the gazebo origin relative to the 22 | # heightmap used to represent the lake. 23 | gaz_lat = 36.596524 24 | gaz_lon = -121.888169 25 | 26 | # So in geonav coordinates, the Gazebo origin is... 27 | xg, yg = gc.ll2xy(gaz_lat,gaz_lon,olat,olon) 28 | print('Geonav ll2xy, Lat: %.4f, Lon:%.4f >> X: %.1f, Y: %.1f' 29 | %(gaz_lat,gaz_lon,xg,yg)) 30 | # This corresponds to where the vehicle is when we spawn it at 0,0 in Gazebo 31 | 32 | # If we know the target location in the geonav coordinates 33 | x = 137.6 34 | y = 54.0 35 | # Then the location in Gazebo coordinates is... 36 | print('Target Gazebo coordinates, X: %.3f, Y: %.3f [m]'%(x-xg,y-yg)) 37 | 38 | # Similarly for the repulsive field location 39 | x = 138.2 40 | y = 75.9 41 | # Then the location in Gazebo coordinates is... 42 | print('Target Gazebo coordinates, X: %.3f, Y: %.3f [m]'%(x-xg,y-yg)) 43 | 44 | 45 | -------------------------------------------------------------------------------- /examples/geonav_alvin_ex.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | Example illustrating use of local coordinate systems. 4 | ''' 5 | 6 | # Import geonav tranformation module 7 | import geonav_transform.geonav_conversions as gc 8 | reload(gc) 9 | # Import AlvinXY transformation module 10 | import alvinxy.alvinxy as axy 11 | reload(axy) 12 | 13 | # Define a local orgin, latitude and longitude in decimal degrees 14 | olat = 37. 15 | olon = -122. 16 | 17 | # Pick a point not too far from the origin 18 | lat = olat+0.01 19 | lon = olon+0.01 20 | 21 | # Convert to UTM 22 | outmy, outmx, outmzone = gc.LLtoUTM(olat,olon) 23 | utmy, utmx, utmzone = gc.LLtoUTM(lat,lon) 24 | print('UTM Conversion Using geonav_tranform') 25 | print('Convert to UTM, Lat: %.4f, Lon:%.4f >> UTMY: %.1f, UTMX: %.1f, Zone:%s'%(lat,lon,utmy,utmx,utmzone)) 26 | 27 | latt, lonn = gc.UTMtoLL(utmy,utmx,utmzone) 28 | print('Convert back to Lat/Lon, Lat: %.4f, Lon: %.4f'%(latt,lonn)) 29 | print('Delta, Lat: %.12f, Lon: %.12f [deg]'%(lat-latt,lon-lonn)) 30 | print(' ') 31 | 32 | # Convert to X/Y and back 33 | # Convert a lat/lon to a local x/y 34 | print('Convert from lat/lon to x/y') 35 | xg, yg = gc.ll2xy(lat,lon,olat,olon) 36 | xa, ya = axy.ll2xy(lat,lon,olat,olon) 37 | print('Geonav ll2xy, Lat: %.4f, Lon:%.4f >> X: %.1f, Y: %.1f' 38 | %(lat,lon,xg,yg)) 39 | print('AlvinXY ll2xy, Lat: %.4f, Lon:%.4f >> X: %.1f, Y: %.1f' 40 | %(lat,lon,xa,ya)) 41 | # Back to lat/lon 42 | glat, glon = gc.xy2ll(xg,yg,olat,olon) 43 | alat, alon = axy.xy2ll(xg,yg,olat,olon) 44 | print('Geonav xy2xy, X: %.1f, Y: %.1f >> Lat: %.4f, Lon:%.4f ' 45 | %(xg,yg,glat,glon)) 46 | print('\t Delta, Lat: %.12f, Lon: %.12f [deg]' 47 | %(lat-glat,lon-glon)) 48 | print('AlvinXY xy2xy, X: %.1f, Y: %.1f >> Lat: %.4f, Lon:%.4f ' 49 | %(xa,ya,alat,alon)) 50 | print('\t Delta, Lat: %.12f, Lon: %.12f [deg]' 51 | %(lat-alat,lon-alon)) 52 | 53 | -------------------------------------------------------------------------------- /examples/utm_ex.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Illustrative example of UTM and local xy coordinates 3 | 4 | Should be run with ipython --pylab 5 | ''' 6 | 7 | import geonav_transform.geonav_conversions as gc 8 | reload(gc) 9 | import alvinxy.alvinxy as axy 10 | reload(axy) 11 | 12 | # Consider Zone 10S 13 | # Longitude from -120 to -126 14 | # Latitude from 32 to 40 15 | 16 | Lon = linspace(-126.0,-120.0,100) 17 | Lat = linspace(32.0,40.0,100) 18 | 19 | # Define local origin at lower right of UTM zone 20 | olon = -126.0 21 | olat = 32.0 22 | 23 | Xutm = [] 24 | Yutm = [] 25 | Xxy = [] 26 | Yxy = [] 27 | Xa = [] 28 | Ya = [] 29 | for lat in Lat: 30 | lon = -126.0 31 | utmy, utmx, utmzone = gc.LLtoUTM(lat,lon) 32 | x, y = gc.ll2xy(lat,lon,olat,olon) 33 | xa,ya = axy.ll2xy(lat,lon,olat,olon) 34 | print utmzone 35 | Xutm.append(utmx) 36 | Yutm.append(utmy) 37 | Xxy.append(x) 38 | Yxy.append(y) 39 | Xa.append(xa) 40 | Ya.append(ya) 41 | 42 | Xutm = array(Xutm) 43 | Yutm = array(Yutm) 44 | figure(1) 45 | clf() 46 | subplot(211) 47 | plot(Lat,Xutm,'.') 48 | title('Lon = %.2f'%lon) 49 | ylabel('UTM X (Easting) [m]') 50 | grid(True) 51 | subplot(212) 52 | plot(Lat,Yutm,'.') 53 | ylabel('UTM Y (Northing) [m]') 54 | xlabel('Lat [deg]') 55 | grid(True) 56 | 57 | figure(2) 58 | clf() 59 | subplot(211) 60 | plot(Lat,Xutm-Xutm[0],'.') 61 | title('Lon = %.2f'%lon) 62 | ylabel('Delta UTM X (Easting) [m]') 63 | grid(True) 64 | subplot(212) 65 | plot(Lat,Yutm-Yutm[0],'.') 66 | ylabel('Delta UTM Y (Northing) [m]') 67 | xlabel('Lat [deg]') 68 | grid(True) 69 | 70 | figure(3) 71 | clf() 72 | subplot(211) 73 | plot(Lat,Xxy,'.',label='UTM local') 74 | hold(True) 75 | plot(Lat,Xa,'r.',label='Alvin XY') 76 | title('Lon = %.2f, Origin: Lat=%.2f, Lon=%.2f'%(lon,olat,olon)) 77 | legend() 78 | ylabel('Local X (Easting) [m]') 79 | grid(True) 80 | subplot(212) 81 | plot(Lat,Yxy,'.') 82 | hold(True) 83 | plot(Lat,Ya,'r.') 84 | ylabel('Local Y (Northing) [m]') 85 | xlabel('Lat [deg]') 86 | grid(True) 87 | 88 | 89 | Xutm = [] 90 | Yutm = [] 91 | Xxy = [] 92 | Yxy = [] 93 | Xa = [] 94 | Ya = [] 95 | for lon in Lon: 96 | lat = olat 97 | utmy, utmx, utmzone = gc.LLtoUTM(lat,lon) 98 | x, y = gc.ll2xy(lat,lon,olat,olon) 99 | xa,ya = axy.ll2xy(lat,lon,olat,olon) 100 | print utmzone 101 | Xutm.append(utmx) 102 | Yutm.append(utmy) 103 | Xxy.append(x) 104 | Yxy.append(y) 105 | Xa.append(xa) 106 | Ya.append(ya) 107 | 108 | Xutm = array(Xutm) 109 | Yutm = array(Yutm) 110 | figure(4) 111 | clf() 112 | subplot(211) 113 | plot(Lon,Xutm,'.') 114 | title('Lat = %.2f'%lat) 115 | ylabel('UTM X (Easting) [m]') 116 | grid(True) 117 | subplot(212) 118 | plot(Lon,Yutm,'.') 119 | ylabel('UTM Y (Northing) [m]') 120 | xlabel('Lon [deg]') 121 | grid(True) 122 | 123 | figure(5) 124 | clf() 125 | subplot(211) 126 | plot(Lon,Xutm-Xutm[0],'.') 127 | title('Lat = %.2f'%lat) 128 | ylabel('Delta UTM X (Easting) [m]') 129 | grid(True) 130 | subplot(212) 131 | plot(Lon,Yutm-Yutm[0],'.') 132 | ylabel('Delta UTM Y (Northing) [m]') 133 | xlabel('Lon [deg]') 134 | grid(True) 135 | 136 | figure(6) 137 | clf() 138 | subplot(211) 139 | plot(Lat,Xxy,'.',label='UTM local') 140 | hold(True) 141 | plot(Lat,Xa,'r.',label='Alvin XY') 142 | title('Lat = %.2f, Origin: Lat=%.2f, Lon=%.2f'%(lat,olat,olon)) 143 | ylabel('Local X (Easting) [m]') 144 | grid(True) 145 | subplot(212) 146 | plot(Lat,Yxy,'.') 147 | hold(True) 148 | plot(Lat,Ya,'r.') 149 | ylabel('Local Y (Northing) [m]') 150 | xlabel('Lon [deg]') 151 | grid(True) 152 | 153 | show() 154 | -------------------------------------------------------------------------------- /include/geonav_transform/geonav_transform.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2017, Brian Bingham 4 | All rights reserved 5 | 6 | This file is part of the geonav_transform package. 7 | 8 | Geonav_transform is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | Geonav_transform is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with Foobar. If not, see . 20 | 21 | */ 22 | 23 | #ifndef GEONAV_TRANSFORM_GEONAV_TRANSFORM_H 24 | #define GEONAV_TRANSFORM_GEONAV_TRANSFORM_H 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | 40 | #include 41 | 42 | namespace GeonavTransform 43 | { 44 | 45 | class GeonavTransform 46 | { 47 | public: 48 | //! @brief Constructor 49 | //! 50 | GeonavTransform(); 51 | 52 | //! @brief Destructor 53 | //! 54 | ~GeonavTransform(); 55 | 56 | //! @brief Main run loop 57 | //! 58 | void run(); 59 | 60 | private: 61 | //! @brief Computes the transform from the UTM frame to the odom frame 62 | //! 63 | void computeTransformOdom2Utm(); 64 | 65 | //! @brief Sets datum values 66 | //! yaw is ENU 67 | //! 68 | bool setDatum(double lat, double lon, double alt, tf2::Quaternion q); 69 | 70 | //! @brief Given the pose of the navsat sensor in the UTM frame, removes the offset from the vehicle's centroid 71 | //! and returns the UTM-frame pose of said centroid. 72 | //! 73 | void getRobotOriginUtmPose(const tf2::Transform &gps_utm_pose, 74 | tf2::Transform &robot_utm_pose, 75 | const ros::Time &transform_time); 76 | 77 | //! @brief Given the pose of the navsat sensor in the world frame, removes the offset from the vehicle's centroid 78 | //! and returns the world-frame pose of said centroid. 79 | //! 80 | void getRobotOriginWorldPose(const tf2::Transform &gps_odom_pose, 81 | tf2::Transform &robot_odom_pose, 82 | const ros::Time &transform_time); 83 | 84 | 85 | //! @brief Callback for the geo nav odom data 86 | //! @param[in] msg The odometry message to process 87 | //! 88 | void navOdomCallback(const nav_msgs::OdometryConstPtr& msg); 89 | 90 | //! @brief Callback for odom in geo frame 91 | //! @param[in] msg The odometry message to process 92 | //! 93 | void geoOdomCallback(const nav_msgs::OdometryConstPtr& msg); 94 | 95 | //! @brief Sends transform 96 | void broadcastTf(void); 97 | 98 | //! @brief Frame ID of the robot's body frame 99 | //! 100 | std::string base_link_frame_id_; 101 | 102 | //! @brief Frame ID of the "odom" frame 103 | //! 104 | std::string odom_frame_id_; 105 | 106 | //! @brief Frame ID of the "odom" frame 107 | //! 108 | std::string utm_frame_id_; 109 | 110 | //! @brief Whether or not we broadcast the utm->odom transform 111 | //! 112 | bool broadcast_utm2odom_transform_; 113 | 114 | //! @brief Whether or not we broadcast the odom->base_link transform 115 | //! 116 | bool broadcast_odom2base_transform_; 117 | 118 | //! @brief Whether or not convert from NED to ENU 119 | //! 120 | bool orientation_ned_; 121 | 122 | //! @brief The frame_id of the NAV message (specifies mounting location) 123 | //! 124 | std::string nav_frame_id_; 125 | 126 | //! @brief Timestamp of the latest good NAV message 127 | //! 128 | //! We assign this value to the timestamp of the odometry message that we output 129 | //! 130 | ros::Time nav_update_time_; 131 | 132 | //! @brief Latest NAV data, stored as UTM coords 133 | //! 134 | tf2::Transform transform_utm2nav_; 135 | tf2::Transform transform_utm2nav_inverse_; 136 | 137 | tf2::Transform transform_odom2nav_; 138 | tf2::Transform transform_odom2nav_inverse_; 139 | 140 | //! @brief Transform buffer for managing coordinate transforms 141 | //! 142 | tf2_ros::Buffer tf_buffer_; 143 | 144 | //! @brief Transform listener for receiving transforms 145 | //! 146 | tf2_ros::TransformListener tf_listener_; 147 | 148 | //! @brief Whether or not we've computed a good heading 149 | //! 150 | bool transform_good_; 151 | 152 | //! @brief Used for publishing the static utm->odom transform 153 | //! 154 | tf2_ros::StaticTransformBroadcaster utm_broadcaster_; 155 | 156 | //! @brief Used for publishing the static utm->odom transform 157 | //! 158 | tf2_ros::TransformBroadcaster tf_broadcaster_; 159 | 160 | //! @brief Holds the UTM->odom transform 161 | //! 162 | tf2::Transform transform_utm2odom_; 163 | tf2::Transform transform_utm2odom_inverse_; 164 | //! @brief Message 165 | geometry_msgs::TransformStamped transform_msg_utm2odom_; 166 | nav_msgs::Odometry nav_in_odom_; 167 | 168 | //! @brief Holds the odom->base transform 169 | //! 170 | tf2::Transform transform_odom2base_; 171 | tf2::Transform transform_odom2base_inverse_; 172 | //! @brief Messages 173 | geometry_msgs::TransformStamped transform_msg_odom2base_; 174 | nav_msgs::Odometry nav_in_utm_; 175 | nav_msgs::Odometry nav_in_geo_; 176 | 177 | //! @brief UTM zone as determined after transforming GPS message 178 | //! 179 | std::string utm_zone_; 180 | 181 | //! @brief Whether or not to report 0 altitude 182 | //! 183 | //! If this parameter is true, we always report 0 for the altitude of the converted GPS odometry message. 184 | //! 185 | bool zero_altitude_; 186 | 187 | //! @brief Publisher of Nav relative to odom (datum) frame 188 | ros::Publisher odom_pub_; 189 | //! @brief Publisher of Nav Odometry relative to utm frame 190 | ros::Publisher utm_pub_; 191 | //! @brief Publisher of Geo Odometry relative to geo frame 192 | ros::Publisher geo_pub_; 193 | 194 | 195 | }; 196 | 197 | } // namespace GeonavTransform 198 | 199 | #endif // GEONAV_TRANSFORM_NAVSAT_TRANSFORM_H 200 | -------------------------------------------------------------------------------- /include/geonav_transform/geonav_utilities.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2017, Brian Bingham 4 | All rights reserved 5 | 6 | This file is part of the geonav_transform package. 7 | 8 | Geonav_transform is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | Geonav_transform is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with Foobar. If not, see . 20 | 21 | */ 22 | #ifndef GEONAV_TRANSFORM_FILTER_UTILITIES_H 23 | #define GEONAV_TRANSFORM_FILTER_UTILITIES_H 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | 33 | #define FB_DEBUG(msg) if (getDebug()) { *debugStream_ << msg; } 34 | // Handy methods for debug output 35 | std::ostream& operator<<(std::ostream& os, const Eigen::MatrixXd &mat); 36 | std::ostream& operator<<(std::ostream& os, const Eigen::VectorXd &vec); 37 | std::ostream& operator<<(std::ostream& os, const std::vector &vec); 38 | std::ostream& operator<<(std::ostream& os, const std::vector &vec); 39 | 40 | namespace GeonavTransform 41 | { 42 | // from filter_common.h 43 | //! @brief Pose and twist messages each 44 | //! contain six variables 45 | const int POSE_SIZE = 6; 46 | const int TWIST_SIZE = 6; 47 | const int POSITION_SIZE = 3; 48 | const int ORIENTATION_SIZE = 3; 49 | const int ACCELERATION_SIZE = 3; 50 | 51 | //! @brief Common variables 52 | const double PI = 3.141592653589793; 53 | const double TAU = 6.283185307179587; 54 | 55 | 56 | namespace GeonavUtilities 57 | { 58 | 59 | //! @brief Utility method keeping RPY angles in the range [-pi, pi] 60 | //! @param[in] rotation - The rotation to bind 61 | //! @return the bounded value 62 | //! 63 | double clampRotation(double rotation); 64 | 65 | //! @brief Utility method for appending tf2 prefixes cleanly 66 | //! @param[in] tfPrefix - the tf2 prefix to append 67 | //! @param[in, out] frameId - the resulting frame_id value 68 | //! 69 | void appendPrefix(std::string tfPrefix, std::string &frameId); 70 | 71 | } // namespace GeonavUtilities 72 | } // namespace GeonavTranform 73 | 74 | #endif // ROBOT_LOCALIZATION_FILTER_UTILITIES_H 75 | -------------------------------------------------------------------------------- /include/geonav_transform/navsat_conversions.h: -------------------------------------------------------------------------------- 1 | /* This file, with some modification, is from the robot_localization 2 | * package 3 | */ 4 | 5 | /* 6 | * Copyright (C) 2010 Austin Robot Technology, and others 7 | * All rights reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions 11 | * are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 2. Redistributions in binary form must reproduce the above 16 | * copyright notice, this list of conditions and the following 17 | * disclaimer in the documentation and/or other materials provided 18 | * with the distribution. 19 | * 3. Neither the names of the University of Texas at Austin, nor 20 | * Austin Robot Technology, nor the names of other contributors may 21 | * be used to endorse or promote products derived from this 22 | * software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 34 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 | * POSSIBILITY OF SUCH DAMAGE. 36 | * 37 | * This file contains code from multiple files in the original 38 | * source. The originals can be found here: 39 | * 40 | * https://github.com/austin-robot/utexas-art-ros-pkg/blob/afd147a1eb944fc3dbd138574c39699813f797bf/stacks/art_vehicle/art_common/include/art/UTM.h 41 | * https://github.com/austin-robot/utexas-art-ros-pkg/blob/afd147a1eb944fc3dbd138574c39699813f797bf/stacks/art_vehicle/art_common/include/art/conversions.h 42 | */ 43 | 44 | #ifndef GEONAV_TRANSFORM_NAVSAT_CONVERSIONS_H 45 | #define GEONAV_TRANSFORM_NAVSAT_CONVERSIONS_H 46 | 47 | /** @file 48 | 49 | @brief Universal Transverse Mercator transforms. 50 | Functions to convert (spherical) latitude and longitude to and 51 | from (Euclidean) UTM coordinates. 52 | @author Chuck Gantz- chuck.gantz@globalstar.com 53 | */ 54 | 55 | #include 56 | #include 57 | 58 | #include 59 | #include 60 | 61 | namespace GeonavTransform 62 | { 63 | namespace NavsatConversions 64 | { 65 | 66 | const double RADIANS_PER_DEGREE = M_PI/180.0; 67 | const double DEGREES_PER_RADIAN = 180.0/M_PI; 68 | 69 | // Grid granularity for rounding UTM coordinates to generate MapXY. 70 | const double grid_size = 100000.0; // 100 km grid 71 | 72 | // WGS84 Parameters 73 | #define WGS84_A 6378137.0 // major axis 74 | #define WGS84_B 6356752.31424518 // minor axis 75 | #define WGS84_F 0.0033528107 // ellipsoid flattening 76 | #define WGS84_E 0.0818191908 // first eccentricity 77 | #define WGS84_EP 0.0820944379 // second eccentricity 78 | 79 | // UTM Parameters 80 | #define UTM_K0 0.9996 // scale factor 81 | #define UTM_FE 500000.0 // false easting 82 | #define UTM_FN_N 0.0 // false northing, northern hemisphere 83 | #define UTM_FN_S 10000000.0 // false northing, southern hemisphere 84 | #define UTM_E2 (WGS84_E*WGS84_E) // e^2 85 | #define UTM_E4 (UTM_E2*UTM_E2) // e^4 86 | #define UTM_E6 (UTM_E4*UTM_E2) // e^6 87 | #define UTM_EP2 (UTM_E2/(1-UTM_E2)) // e'^2 88 | 89 | /** 90 | * Utility function to convert geodetic to UTM position 91 | * 92 | * Units in are floating point degrees (sign for east/west) 93 | * 94 | * Units out are meters 95 | * 96 | * @todo deprecate this interface in favor of LLtoUTM() 97 | */ 98 | static inline void UTM(double lat, double lon, double *x, double *y) 99 | { 100 | // constants 101 | static const double m0 = (1 - UTM_E2/4 - 3*UTM_E4/64 - 5*UTM_E6/256); 102 | static const double m1 = -(3*UTM_E2/8 + 3*UTM_E4/32 + 45*UTM_E6/1024); 103 | static const double m2 = (15*UTM_E4/256 + 45*UTM_E6/1024); 104 | static const double m3 = -(35*UTM_E6/3072); 105 | 106 | // compute the central meridian 107 | int cm = ((lon >= 0.0) 108 | ? (static_cast(lon) - (static_cast(lon)) % 6 + 3) 109 | : (static_cast(lon) - (static_cast(lon)) % 6 - 3)); 110 | 111 | // convert degrees into radians 112 | double rlat = lat * RADIANS_PER_DEGREE; 113 | double rlon = lon * RADIANS_PER_DEGREE; 114 | double rlon0 = cm * RADIANS_PER_DEGREE; 115 | 116 | // compute trigonometric functions 117 | double slat = sin(rlat); 118 | double clat = cos(rlat); 119 | double tlat = tan(rlat); 120 | 121 | // decide the false northing at origin 122 | double fn = (lat > 0) ? UTM_FN_N : UTM_FN_S; 123 | 124 | double T = tlat * tlat; 125 | double C = UTM_EP2 * clat * clat; 126 | double A = (rlon - rlon0) * clat; 127 | double M = WGS84_A * (m0*rlat + m1*sin(2*rlat) 128 | + m2*sin(4*rlat) + m3*sin(6*rlat)); 129 | double V = WGS84_A / sqrt(1 - UTM_E2*slat*slat); 130 | 131 | // compute the easting-northing coordinates 132 | *x = UTM_FE + UTM_K0 * V * (A + (1-T+C)*pow(A, 3)/6 133 | + (5-18*T+T*T+72*C-58*UTM_EP2)*pow(A, 5)/120); 134 | *y = fn + UTM_K0 * (M + V * tlat * (A*A/2 135 | + (5-T+9*C+4*C*C)*pow(A, 4)/24 136 | + ((61-58*T+T*T+600*C-330*UTM_EP2) 137 | * pow(A, 6)/720))); 138 | 139 | return; 140 | } 141 | 142 | 143 | /** 144 | * Determine the correct UTM letter designator for the 145 | * given latitude 146 | * 147 | * @returns 'Z' if latitude is outside the UTM limits of 84N to 80S 148 | * 149 | * Written by Chuck Gantz- chuck.gantz@globalstar.com 150 | */ 151 | static inline char UTMLetterDesignator(double Lat) 152 | { 153 | char LetterDesignator; 154 | 155 | if ((84 >= Lat) && (Lat >= 72)) LetterDesignator = 'X'; 156 | else if ((72 > Lat) && (Lat >= 64)) LetterDesignator = 'W'; 157 | else if ((64 > Lat) && (Lat >= 56)) LetterDesignator = 'V'; 158 | else if ((56 > Lat) && (Lat >= 48)) LetterDesignator = 'U'; 159 | else if ((48 > Lat) && (Lat >= 40)) LetterDesignator = 'T'; 160 | else if ((40 > Lat) && (Lat >= 32)) LetterDesignator = 'S'; 161 | else if ((32 > Lat) && (Lat >= 24)) LetterDesignator = 'R'; 162 | else if ((24 > Lat) && (Lat >= 16)) LetterDesignator = 'Q'; 163 | else if ((16 > Lat) && (Lat >= 8)) LetterDesignator = 'P'; 164 | else if (( 8 > Lat) && (Lat >= 0)) LetterDesignator = 'N'; 165 | else if (( 0 > Lat) && (Lat >= -8)) LetterDesignator = 'M'; 166 | else if ((-8 > Lat) && (Lat >= -16)) LetterDesignator = 'L'; 167 | else if ((-16 > Lat) && (Lat >= -24)) LetterDesignator = 'K'; 168 | else if ((-24 > Lat) && (Lat >= -32)) LetterDesignator = 'J'; 169 | else if ((-32 > Lat) && (Lat >= -40)) LetterDesignator = 'H'; 170 | else if ((-40 > Lat) && (Lat >= -48)) LetterDesignator = 'G'; 171 | else if ((-48 > Lat) && (Lat >= -56)) LetterDesignator = 'F'; 172 | else if ((-56 > Lat) && (Lat >= -64)) LetterDesignator = 'E'; 173 | else if ((-64 > Lat) && (Lat >= -72)) LetterDesignator = 'D'; 174 | else if ((-72 > Lat) && (Lat >= -80)) LetterDesignator = 'C'; 175 | // 'Z' is an error flag, the Latitude is outside the UTM limits 176 | else LetterDesignator = 'Z'; 177 | return LetterDesignator; 178 | } 179 | 180 | /** 181 | * Convert lat/long to UTM coords. Equations from USGS Bulletin 1532 182 | * 183 | * East Longitudes are positive, West longitudes are negative. 184 | * North latitudes are positive, South latitudes are negative 185 | * Lat and Long are in fractional degrees 186 | * 187 | * Written by Chuck Gantz- chuck.gantz@globalstar.com 188 | */ 189 | static inline void LLtoUTM(const double Lat, const double Long, 190 | double &UTMNorthing, double &UTMEasting, 191 | std::string &UTMZone) 192 | { 193 | double a = WGS84_A; 194 | double eccSquared = UTM_E2; 195 | double k0 = UTM_K0; 196 | 197 | double LongOrigin; 198 | double eccPrimeSquared; 199 | double N, T, C, A, M; 200 | 201 | // Make sure the longitude is between -180.00 .. 179.9 202 | double LongTemp = (Long+180)-static_cast((Long+180)/360)*360-180; 203 | 204 | double LatRad = Lat*RADIANS_PER_DEGREE; 205 | double LongRad = LongTemp*RADIANS_PER_DEGREE; 206 | double LongOriginRad; 207 | int ZoneNumber; 208 | 209 | ZoneNumber = static_cast((LongTemp + 180)/6) + 1; 210 | 211 | if ( Lat >= 56.0 && Lat < 64.0 && LongTemp >= 3.0 && LongTemp < 12.0 ) 212 | ZoneNumber = 32; 213 | 214 | // Special zones for Svalbard 215 | if ( Lat >= 72.0 && Lat < 84.0 ) 216 | { 217 | if ( LongTemp >= 0.0 && LongTemp < 9.0 ) ZoneNumber = 31; 218 | else if ( LongTemp >= 9.0 && LongTemp < 21.0 ) ZoneNumber = 33; 219 | else if ( LongTemp >= 21.0 && LongTemp < 33.0 ) ZoneNumber = 35; 220 | else if ( LongTemp >= 33.0 && LongTemp < 42.0 ) ZoneNumber = 37; 221 | } 222 | // +3 puts origin in middle of zone 223 | LongOrigin = (ZoneNumber - 1)*6 - 180 + 3; 224 | LongOriginRad = LongOrigin * RADIANS_PER_DEGREE; 225 | 226 | // Compute the UTM Zone from the latitude and longitude 227 | char zone_buf[] = {0, 0, 0, 0}; 228 | snprintf(zone_buf, sizeof(zone_buf), "%d%c", ZoneNumber, UTMLetterDesignator(Lat)); 229 | UTMZone = std::string(zone_buf); 230 | 231 | eccPrimeSquared = (eccSquared)/(1-eccSquared); 232 | 233 | N = a/sqrt(1-eccSquared*sin(LatRad)*sin(LatRad)); 234 | T = tan(LatRad)*tan(LatRad); 235 | C = eccPrimeSquared*cos(LatRad)*cos(LatRad); 236 | A = cos(LatRad)*(LongRad-LongOriginRad); 237 | 238 | M = a*((1 - eccSquared/4 - 3*eccSquared*eccSquared/64 239 | - 5*eccSquared*eccSquared*eccSquared/256) * LatRad 240 | - (3*eccSquared/8 + 3*eccSquared*eccSquared/32 241 | + 45*eccSquared*eccSquared*eccSquared/1024)*sin(2*LatRad) 242 | + (15*eccSquared*eccSquared/256 243 | + 45*eccSquared*eccSquared*eccSquared/1024)*sin(4*LatRad) 244 | - (35*eccSquared*eccSquared*eccSquared/3072)*sin(6*LatRad)); 245 | 246 | UTMEasting = static_cast 247 | (k0*N*(A+(1-T+C)*A*A*A/6 248 | + (5-18*T+T*T+72*C-58*eccPrimeSquared)*A*A*A*A*A/120) 249 | + 500000.0); 250 | 251 | UTMNorthing = static_cast 252 | (k0*(M+N*tan(LatRad) 253 | *(A*A/2+(5-T+9*C+4*C*C)*A*A*A*A/24 254 | + (61-58*T+T*T+600*C-330*eccPrimeSquared)*A*A*A*A*A*A/720))); 255 | 256 | if (Lat < 0) 257 | { 258 | // 10000000 meter offset for southern hemisphere 259 | UTMNorthing += 10000000.0; 260 | } 261 | } 262 | 263 | /** 264 | * Converts UTM coords to lat/long. Equations from USGS Bulletin 1532 265 | * 266 | * East Longitudes are positive, West longitudes are negative. 267 | * North latitudes are positive, South latitudes are negative 268 | * Lat and Long are in fractional degrees. 269 | * 270 | * Written by Chuck Gantz- chuck.gantz@globalstar.com 271 | */ 272 | static inline void UTMtoLL(const double UTMNorthing, const double UTMEasting, 273 | const std::string &UTMZone, double& Lat, double& Long ) 274 | { 275 | double k0 = UTM_K0; 276 | double a = WGS84_A; 277 | double eccSquared = UTM_E2; 278 | double eccPrimeSquared; 279 | double e1 = (1-sqrt(1-eccSquared))/(1+sqrt(1-eccSquared)); 280 | double N1, T1, C1, R1, D, M; 281 | double LongOrigin; 282 | double mu, phi1Rad; 283 | double x, y; 284 | int ZoneNumber; 285 | char* ZoneLetter; 286 | 287 | x = UTMEasting - 500000.0; // remove 500,000 meter offset for longitude 288 | y = UTMNorthing; 289 | 290 | ZoneNumber = strtoul(UTMZone.c_str(), &ZoneLetter, 10); 291 | if ((*ZoneLetter - 'N') < 0) 292 | { 293 | // remove 10,000,000 meter offset used for southern hemisphere 294 | y -= 10000000.0; 295 | } 296 | 297 | // +3 puts origin in middle of zone 298 | LongOrigin = (ZoneNumber - 1)*6 - 180 + 3; 299 | eccPrimeSquared = (eccSquared)/(1-eccSquared); 300 | 301 | M = y / k0; 302 | mu = M/(a*(1-eccSquared/4-3*eccSquared*eccSquared/64 303 | -5*eccSquared*eccSquared*eccSquared/256)); 304 | 305 | phi1Rad = mu + ((3*e1/2-27*e1*e1*e1/32)*sin(2*mu) 306 | + (21*e1*e1/16-55*e1*e1*e1*e1/32)*sin(4*mu) 307 | + (151*e1*e1*e1/96)*sin(6*mu)); 308 | 309 | N1 = a/sqrt(1-eccSquared*sin(phi1Rad)*sin(phi1Rad)); 310 | T1 = tan(phi1Rad)*tan(phi1Rad); 311 | C1 = eccPrimeSquared*cos(phi1Rad)*cos(phi1Rad); 312 | R1 = a*(1-eccSquared)/pow(1-eccSquared*sin(phi1Rad)*sin(phi1Rad), 1.5); 313 | D = x/(N1*k0); 314 | 315 | Lat = phi1Rad - ((N1*tan(phi1Rad)/R1) 316 | *(D*D/2 317 | -(5+3*T1+10*C1-4*C1*C1-9*eccPrimeSquared)*D*D*D*D/24 318 | +(61+90*T1+298*C1+45*T1*T1-252*eccPrimeSquared 319 | -3*C1*C1)*D*D*D*D*D*D/720)); 320 | 321 | Lat = Lat * DEGREES_PER_RADIAN; 322 | 323 | Long = ((D-(1+2*T1+C1)*D*D*D/6 324 | +(5-2*C1+28*T1-3*C1*C1+8*eccPrimeSquared+24*T1*T1) 325 | *D*D*D*D*D/120) 326 | / cos(phi1Rad)); 327 | Long = LongOrigin + Long * DEGREES_PER_RADIAN; 328 | } 329 | 330 | } // namespace NavsatConversions 331 | } // namespace GeonavTransform 332 | 333 | #endif // GEONAV_TRANSFORM_NAVSAT_CONVERSIONS_H 334 | -------------------------------------------------------------------------------- /launch/cavr.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | [36.596, -121.878, 0.0] 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /launch/elestero.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | [36.595, -121.89, 0.0] 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /launch/geonav_transform.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | [36.6137, -121.912, 0.0] 11 | 12 | 13 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /matlab/alvinxy/deg2degmin.m: -------------------------------------------------------------------------------- 1 | function [degInt,minFloat] = deg2degmin(degFloat) 2 | 3 | % convert decimal degrees to integer degrees and decimal minutes 4 | % i.e., ddd.dddd -> dd mm.mmmm 5 | % 6 | % bbing 17.06.04 7 | % 8 | 9 | degFloat = abs(degFloat); 10 | degInt = floor(degFloat); 11 | minFloat = (degFloat-degInt)*60; 12 | -------------------------------------------------------------------------------- /matlab/alvinxy/degmin2deg.m: -------------------------------------------------------------------------------- 1 | function [degFloat] = degmin2deg(degInt,minFloat) 2 | 3 | % convert decimal degrees to integer degrees and decimal minutes 4 | % i.e., ddd.dddd -> dd mm.mmmm 5 | % 6 | % bbing 17.06.04 7 | % 8 | 9 | % check that degress are integer 10 | if round(degInt)~=degInt 11 | fprintf('Input degrees need to be integer \n'); 12 | degFloat = -1; 13 | return 14 | end 15 | 16 | degFloat = degInt+minFloat/60; 17 | -------------------------------------------------------------------------------- /matlab/alvinxy/ll2xy.m: -------------------------------------------------------------------------------- 1 | function [x,y] = ll2xy(lat, lon, orglat, orglon) 2 | 3 | % [x,y] = ll2xy(lat, lon, orglat, orglon) 4 | % 5 | % LL2XY: AlvinXY 6 | % Converts Lat/Lon (WGS84) to Alvin XY's using a Mercator projection. 7 | % 8 | % INPUT 9 | % x - Easting in m (Alvin local grid) 10 | % y - Northing in m (Alvin local grid) 11 | % orglat - origin location latitude in decimal degrees 12 | % orglon - origin location longitude in decimal degrees 13 | % 14 | % OUTPUT 15 | % x - Easting in m (Alvin local grid) 16 | % y - Northing in m (Alvin local grid) 17 | % 18 | % HISTORY 19 | % 20 | % EXAMPLE 21 | % [x,y] = 22 | % ll2xy(degmin2deg(latdeg,latmin),degmin2deg(londeg,lonmin),... 23 | % originlat,originlon); 24 | % 25 | % SEE ALSO 26 | % xy2ll.m 27 | % mdeglon.m and mdeglat.m 28 | 29 | x = (lon - orglon) * mdeglon(orglat); 30 | y = (lat - orglat) * mdeglat(orglat); 31 | -------------------------------------------------------------------------------- /matlab/alvinxy/ll2xy2ll_example_at340.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/matlab/alvinxy/ll2xy2ll_example_at340.xls -------------------------------------------------------------------------------- /matlab/alvinxy/ll2xyv.m: -------------------------------------------------------------------------------- 1 | function [x,y] = (ll,originll) 2 | % given the lat and lon in decimal degrees along with the orgin (also 3 | % in decimal degrees, returns the x,y using a mercator 4 | % projection 5 | % History 6 | % 02.07.2006 bbing using DSL's version as a base and creating a similar 7 | % script 8 | % 9 | % Ex 10 | % [xy] = ll2xy 11 | 12 | % ll2xy(degmin2deg(latdeg,latmin),degmin2deg(londeg,lonmin),... 13 | % originlat,originlon); 14 | 15 | lat = ll(:,1)); 16 | lon = ll(:,2); 17 | orglat = originll(1); 18 | orglon = originll(2); 19 | 20 | x = (lon - orglon) * mdeglon(orglat); 21 | y = (lat - orglat) * mdeglat(orglat); 22 | -------------------------------------------------------------------------------- /matlab/alvinxy/mdeglat.m: -------------------------------------------------------------------------------- 1 | function dy = mdeglat(lat) % dy = mdeglat(lat) % % MDEGLAT- used in converting lat/lon <-> xy % Meters per degree Latitude at the given Latitude % % INPUT % lon - longitude in decimal degrees % % OUTPUT % dx - meters per degree latitude % % SEE ALSO % xy2ll.m and ll2xy.m % mdeglon.m and mdeglat.m latrad = lat*2*pi/360 ; dy = 111132.09 - 566.05 * cos(2.0*latrad) ... + 1.20 * cos(4.0*latrad) ... - 0.002 * cos(6.0*latrad) ; -------------------------------------------------------------------------------- /matlab/alvinxy/mdeglon.m: -------------------------------------------------------------------------------- 1 | function dx = mdeglon(lat) % dx = mdeglon(lat) % % MDEGLON - used in converting lat/lon <-> xy % Meters per degree Longitude at the given Latitude % % INPUT % lat - latitude in decimal degrees % % OUTPUT % dx - meters per degree longitude % % SEE ALSO % xy2ll.m and ll2xy.m % mdeglon.m and mdeglat.m latrad = lat*2*pi/360 ; dx = 111415.13 * cos(latrad) ... - 94.55 * cos(3.0*latrad) ... + 0.12 * cos(5.0*latrad); -------------------------------------------------------------------------------- /matlab/alvinxy/xy2ll.m: -------------------------------------------------------------------------------- 1 | function [lat,lon] = xy2ll(x, y, orglat, orglon) % [lat,lon] = xy2ll(x, y, orglat, orglon) % % XY2LL % Converts Alvin XY's to Lat/Lon (WGS84) using a Mercator projection. % % INPUT % x - Easting in m (Alvin local grid) % y - Northing in m (Alvin local grid) % orglat - origin location latitude in decimal degrees % orglon - origin location longitude in decimal degrees % % OUTPUT % lat - Latitude in decimal degrees % lon - Longitude in decimal degrees % % HISTORY % % % SEE ALSO % ll2xy.m % mdeglon.m and mdeglat.m % lon = x/mdeglon(orglat) + orglon; lat = y/mdeglat(orglat) + orglat; -------------------------------------------------------------------------------- /matlab/alvinxy/xy2lldegmin.m: -------------------------------------------------------------------------------- 1 | function [ll] = xy2lldegmin(xy,originll,showit) 2 | 3 | 4 | 5 | if nargin < 3 6 | showit = 0; 7 | end 8 | 9 | [Lat,Lon] = xy2ll(xy(1),xy(2),originll(1),originll(2)); 10 | [ll.latdeg,ll.latmin] = deg2degmin(Lat); 11 | [ll.londeg,ll.lonmin] = deg2degmin(Lon); 12 | 13 | if showit 14 | fprintf('X = %6.1f, Y = %6.1f\n',xy(1),xy(2)); 15 | fprintf('Lat = %d , %7.4f N ; %d , %7.4f E \n',... 16 | ll.latdeg,ll.latmin,ll.londeg,ll.lonmin); 17 | end 18 | 19 | 20 | -------------------------------------------------------------------------------- /matlab/geonav/ll2xy.cpp: -------------------------------------------------------------------------------- 1 | /*========================================================== 2 | * ll2xy.cpp for conversion to MEX file for MATLAB 3 | *========================================================*/ 4 | 5 | #include "mex.h" 6 | #include "../../include/geonav_transform/navsat_conversions.h" 7 | /* Computational routine */ 8 | void ll2xy(double lat, double lon, double origin_lat, double origin_lon, 9 | double *x, double *y) 10 | { 11 | std::string outmzone, utmzone; 12 | double outmy, outmx, utmx, utmy; 13 | GeonavTransform::NavsatConversions::LLtoUTM(origin_lat,origin_lon,outmy,outmx,outmzone); 14 | GeonavTransform::NavsatConversions::LLtoUTM(lat,lon,utmy,utmx,utmzone); 15 | *x = utmx-outmx; 16 | *y = utmy-outmy; 17 | 18 | } 19 | 20 | 21 | /* The gateway function */ 22 | void mexFunction( int nlhs, mxArray *plhs[], 23 | int nrhs, const mxArray *prhs[]) 24 | { 25 | /* Inputs */ 26 | double lat; 27 | double lon; 28 | double origin_lat; 29 | double origin_lon; 30 | /* Outputs */ 31 | double *x; 32 | double *y; 33 | 34 | /* check for proper number of arguments */ 35 | if(nrhs!=4) { 36 | mexErrMsgIdAndTxt("geonav_conversions:ll2xy:nrhs","Four inputs required."); 37 | } 38 | if(nlhs!=2) { 39 | mexErrMsgIdAndTxt("geonav_conversions:ll2xy:nlhs","Two outputs required."); 40 | } 41 | /* make sure the all inputs argument are scalar */ 42 | int ii = 0; 43 | for (ii=0; ii <= 3; ii++) 44 | { 45 | if( !mxIsDouble(prhs[ii]) || 46 | mxIsComplex(prhs[ii]) || 47 | mxGetNumberOfElements(prhs[ii])!=1 ) 48 | { 49 | mexErrMsgIdAndTxt("MyToolbox:arrayProduct:notScalar","Input multiplier must be a scalar."); 50 | } 51 | } 52 | /* get the value of the scalar inputs */ 53 | lat = mxGetScalar(prhs[0]); 54 | lon = mxGetScalar(prhs[1]); 55 | origin_lat = mxGetScalar(prhs[2]); 56 | origin_lon = mxGetScalar(prhs[3]); 57 | 58 | 59 | /* create the output */ 60 | plhs[0] = mxCreateDoubleScalar(0); 61 | plhs[1] = mxCreateDoubleScalar(0); 62 | 63 | x = mxGetPr(plhs[0]); 64 | y = mxGetPr(plhs[1]); 65 | 66 | /* call the computational routine */ 67 | ll2xy(lat,lon,origin_lat,origin_lon,x,y); 68 | } 69 | -------------------------------------------------------------------------------- /matlab/geonav/ll2xy.m: -------------------------------------------------------------------------------- 1 | % [x,y] = ll2xy(lat, lon, orglat, orglon) 2 | % 3 | % LL2XY: Geonav 4 | % Converts Lat/Lon to a local x/y using UTM coordinates 5 | % The actual function is a MEX function based on the .cpp file 6 | % for compatibility with ROS geonav_transform package. This 7 | % file is just the help documentation 8 | % 9 | % INPUT 10 | % x - Easting in m (Geonav local grid) 11 | % y - Northing in m (Geonav local grid) 12 | % orglat - origin location latitude in decimal degrees 13 | % orglon - origin location longitude in decimal degrees 14 | % 15 | % OUTPUT 16 | % x - Easting in m (Geonav local grid)XS 17 | % y - Northing in m (Geonav local grid) 18 | % 19 | % HISTORY 20 | % 21 | % EXAMPLE 22 | % [x,y] = ll2xy(37.01,-121.99,36,-122); 23 | % 24 | -------------------------------------------------------------------------------- /matlab/geonav/make_mex.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mex -v CFLAGS='$CFLAGS -Wall' ll2xy.cpp 3 | mex -v CFLAGS='$CFLAGS -Wall' xy2ll.cpp 4 | -------------------------------------------------------------------------------- /matlab/geonav/xy2ll.cpp: -------------------------------------------------------------------------------- 1 | /*========================================================== 2 | * xy2ll.cpp for conversion to MEX file for MATLAB 3 | *========================================================*/ 4 | 5 | #include "mex.h" 6 | #include "../../include/geonav_transform/navsat_conversions.h" 7 | /* Computational routine */ 8 | void xy2ll(double x, double y, double origin_lat, double origin_lon, 9 | double *lat, double *lon) 10 | { 11 | std::string outmzone, utmzone; 12 | double outmy, outmx, utmx, utmy; 13 | 14 | GeonavTransform::NavsatConversions::LLtoUTM(origin_lat,origin_lon,outmy,outmx,outmzone); 15 | utmy = outmy+y; 16 | utmx = outmx+x; 17 | double tlat, tlon; 18 | GeonavTransform::NavsatConversions::UTMtoLL(utmy,utmx,outmzone,tlat,tlon); 19 | *lat=tlat; 20 | *lon=tlon; 21 | 22 | 23 | } 24 | 25 | 26 | /* The gateway function */ 27 | void mexFunction( int nlhs, mxArray *plhs[], 28 | int nrhs, const mxArray *prhs[]) 29 | { 30 | /* Inputs */ 31 | double x; 32 | double y; 33 | double origin_lat; 34 | double origin_lon; 35 | /* Outputs */ 36 | double *lat; 37 | double *lon; 38 | 39 | /* check for proper number of arguments */ 40 | if(nrhs!=4) { 41 | mexErrMsgIdAndTxt("geonav_conversions:xy2ll:nrhs","Four inputs required."); 42 | } 43 | if(nlhs!=2) { 44 | mexErrMsgIdAndTxt("geonav_conversions:xy2ll:nlhs","Two outputs required."); 45 | } 46 | /* make sure the all inputs argument are scalar */ 47 | int ii = 0; 48 | for (ii=0; ii <= 3; ii++) 49 | { 50 | if( !mxIsDouble(prhs[ii]) || 51 | mxIsComplex(prhs[ii]) || 52 | mxGetNumberOfElements(prhs[ii])!=1 ) 53 | { 54 | mexErrMsgIdAndTxt("MyToolbox:arrayProduct:notScalar","Input multiplier must be a scalar."); 55 | } 56 | } 57 | /* get the value of the scalar inputs */ 58 | x = mxGetScalar(prhs[0]); 59 | y = mxGetScalar(prhs[1]); 60 | origin_lat = mxGetScalar(prhs[2]); 61 | origin_lon = mxGetScalar(prhs[3]); 62 | 63 | 64 | /* create the output */ 65 | plhs[0] = mxCreateDoubleScalar(0); 66 | plhs[1] = mxCreateDoubleScalar(0); 67 | 68 | lat = mxGetPr(plhs[0]); 69 | lon = mxGetPr(plhs[1]); 70 | 71 | /* call the computational routine */ 72 | xy2ll(x,y,origin_lat,origin_lon,lat,lon); 73 | } 74 | -------------------------------------------------------------------------------- /matlab/geonav/xy2ll.m: -------------------------------------------------------------------------------- 1 | % [lat,lon] = xy2ll(x, y, orglat, orglon) 2 | % 3 | % XY2LL: Geonav 4 | % Converts local x/y to Lat/Lon using UTM coordinates 5 | % The actual function is a MEX function based on the .cpp file 6 | % for compatibility with ROS geonav_transform package. This 7 | % file is just the help documentation 8 | % 9 | % INPUT 10 | % x - Easting in m (Geonav local grid) 11 | % y - Northing in m (Geonav local grid) 12 | % orglat - origin location latitude in decimal degrees 13 | % orglon - origin location longitude in decimal degrees 14 | % 15 | 16 | % OUTPUT 17 | % lat - Latitude in decimal degrees 18 | % lon - Longitude in decimal degrees 19 | % 20 | % EXAMPLE 21 | % [lat,lon] = ll2xy(878.0, 1118.8, 37, -122); 22 | % -------------------------------------------------------------------------------- /matlab/matlab_ex.m: -------------------------------------------------------------------------------- 1 | % Example of Global <-> Local coordinate transforms using 2 | % either geonav or alvinxy 3 | % 4 | % Note - since the geonav functions are MEX functions, you 5 | % will need to run the make_mex.sh script to create the MEX 6 | % files for your particular MATLAB installation. 7 | 8 | % Specify paths to the two toolboxes 9 | alvinpath = './alvinxy'; 10 | geonavpath = './geonav'; 11 | 12 | % Make sure to remove both paths so that only one is active at a time 13 | rmpath(alvinpath); 14 | rmpath(geonavpath); 15 | 16 | % Select which toolbox to use by commenting out one of the following 17 | addpath(geonavpath); disp('**Using Geonav**'); 18 | %addpath(alvinpath); disp('**Using AlvinXY**'); 19 | 20 | % Define a local orgin, latitude and longitude in decimal degrees 21 | olat = 37.0; 22 | olon = -122.0; 23 | 24 | % Pick a point not too far from the origin 25 | lat = olat+0.01; 26 | lon = olon+0.01; 27 | 28 | 29 | % Convert to X/Y and back 30 | % Convert a lat/lon to a local x/y 31 | fprintf('Convert from lat/lon to x/y\n') 32 | [x,y] = ll2xy(lat,lon,olat,olon); 33 | fprintf('ll2xy, Lat: %.4f, Lon:%.4f >> X: %.1f, Y: %.1f\n',lat,lon,x,y); 34 | % Back to lat/lon 35 | [nlat, nlon] = xy2ll(x,y,olat,olon); 36 | fprintf('xy2ll, X: %.1f, Y: %.1f >> Lat: %.4f, Lon:%.4f\n',x,y,nlat,nlon); 37 | fprintf('\t Delta, Lat: %.12f, Lon: %.12f [deg]\n',lat-nlat,lon-nlon); 38 | 39 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | geonav_transform 4 | 0.0.1 5 | The geonav_transform package 6 | 7 | Brian Bingham 8 | Brian Bingham 9 | GPL 10 | https://github.com/bsb808/geonav_transform --> 11 | 12 | catkin 13 | 14 | cmake_modules 15 | roscpp 16 | rospy 17 | python-catkin-pkg 18 | eigen 19 | geographic_msgs 20 | geometry_msgs 21 | nav_msgs 22 | sensor_msgs 23 | std_msgs 24 | tf2 25 | tf2_geometry_msgs 26 | tf2_ros 27 | 28 | robot_localization 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /rosdoc.yaml: -------------------------------------------------------------------------------- 1 | - builder: sphinx 2 | sphinx_root_dir: doc 3 | -------------------------------------------------------------------------------- /rviz/frames.rviz: -------------------------------------------------------------------------------- 1 | Panels: 2 | - Class: rviz/Displays 3 | Help Height: 78 4 | Name: Displays 5 | Property Tree Widget: 6 | Expanded: 7 | - /Global Options1 8 | - /Status1 9 | - /Axes1 10 | - /Axes2 11 | - /Axes3 12 | Splitter Ratio: 0.5 13 | Tree Height: 521 14 | - Class: rviz/Selection 15 | Name: Selection 16 | - Class: rviz/Tool Properties 17 | Expanded: 18 | - /2D Pose Estimate1 19 | - /2D Nav Goal1 20 | - /Publish Point1 21 | Name: Tool Properties 22 | Splitter Ratio: 0.588679 23 | - Class: rviz/Views 24 | Expanded: 25 | - /Current View1 26 | Name: Views 27 | Splitter Ratio: 0.5 28 | - Class: rviz/Time 29 | Experimental: false 30 | Name: Time 31 | SyncMode: 0 32 | SyncSource: "" 33 | Visualization Manager: 34 | Class: "" 35 | Displays: 36 | - Alpha: 0.5 37 | Cell Size: 1 38 | Class: rviz/Grid 39 | Color: 160; 160; 164 40 | Enabled: true 41 | Line Style: 42 | Line Width: 0.03 43 | Value: Lines 44 | Name: Grid 45 | Normal Cell Count: 0 46 | Offset: 47 | X: 0 48 | Y: 0 49 | Z: 0 50 | Plane: XY 51 | Plane Cell Count: 100 52 | Reference Frame: 53 | Value: true 54 | - Class: rviz/Axes 55 | Enabled: true 56 | Length: 1 57 | Name: Axes 58 | Radius: 0.1 59 | Reference Frame: base_link 60 | Value: true 61 | - Class: rviz/Axes 62 | Enabled: true 63 | Length: 1 64 | Name: Axes 65 | Radius: 0.1 66 | Reference Frame: odom 67 | Value: true 68 | - Class: rviz/Axes 69 | Enabled: true 70 | Length: 1 71 | Name: Axes 72 | Radius: 0.1 73 | Reference Frame: utm 74 | Value: true 75 | Enabled: true 76 | Global Options: 77 | Background Color: 48; 48; 48 78 | Fixed Frame: odom 79 | Frame Rate: 30 80 | Name: root 81 | Tools: 82 | - Class: rviz/Interact 83 | Hide Inactive Objects: true 84 | - Class: rviz/MoveCamera 85 | - Class: rviz/Select 86 | - Class: rviz/FocusCamera 87 | - Class: rviz/Measure 88 | - Class: rviz/SetInitialPose 89 | Topic: /initialpose 90 | - Class: rviz/SetGoal 91 | Topic: /move_base_simple/goal 92 | - Class: rviz/PublishPoint 93 | Single click: true 94 | Topic: /clicked_point 95 | Value: true 96 | Views: 97 | Current: 98 | Class: rviz/Orbit 99 | Distance: 20.4094 100 | Enable Stereo Rendering: 101 | Stereo Eye Separation: 0.06 102 | Stereo Focal Distance: 1 103 | Swap Stereo Eyes: false 104 | Value: false 105 | Focal Point: 106 | X: 0 107 | Y: 0 108 | Z: 0 109 | Name: Current View 110 | Near Clip Distance: 0.01 111 | Pitch: 0.645398 112 | Target Frame: 113 | Value: Orbit (rviz) 114 | Yaw: 4.99857 115 | Saved: ~ 116 | Window Geometry: 117 | Displays: 118 | collapsed: false 119 | Height: 846 120 | Hide Left Dock: false 121 | Hide Right Dock: false 122 | QMainWindow State: 000000ff00000000fd00000004000000000000016a0000029ffc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000006400fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c00610079007301000000430000029f000000de00fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f0000029ffc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a0056006900650077007301000000430000029f000000b800fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004b000000044fc0100000002fb0000000800540069006d00650100000000000004b00000025800fffffffb0000000800540069006d006501000000000000045000000000000000000000022b0000029f00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 123 | Selection: 124 | collapsed: false 125 | Time: 126 | collapsed: false 127 | Tool Properties: 128 | collapsed: false 129 | Views: 130 | collapsed: false 131 | Width: 1200 132 | X: 490 133 | Y: 44 134 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | ## ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD 2 | 3 | from distutils.core import setup 4 | from catkin_pkg.python_setup import generate_distutils_setup 5 | 6 | # fetch values from package.xml 7 | setup_args = generate_distutils_setup( 8 | packages=['geonav_transform','alvinxy'], 9 | package_dir={'': 'src'}, 10 | ) 11 | 12 | setup(**setup_args) 13 | -------------------------------------------------------------------------------- /src/alvinxy/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/src/alvinxy/__init__.py -------------------------------------------------------------------------------- /src/alvinxy/alvinxy.py: -------------------------------------------------------------------------------- 1 | ''' 2 | AlvinXY Utility Module 3 | 4 | All of this is based on WHOI's original implementations in C and MATLAB. 5 | 6 | ''' 7 | 8 | from math import * 9 | import numpy as np 10 | 11 | def mdeglat(lat): 12 | ''' 13 | Provides meters-per-degree latitude at a given latitude 14 | 15 | Args: 16 | lat (float): latitude 17 | 18 | Returns: 19 | float: meters-per-degree value 20 | ''' 21 | latrad = lat*2.0*pi/360.0 ; 22 | 23 | dy = 111132.09 - 566.05 * cos(2.0*latrad) \ 24 | + 1.20 * cos(4.0*latrad) \ 25 | - 0.002 * cos(6.0*latrad) 26 | return dy 27 | 28 | def mdeglon(lat): 29 | ''' 30 | Provides meters-per-degree longitude at a given latitude 31 | 32 | Args: 33 | lat (float): latitude in decimal degrees 34 | 35 | Returns: 36 | float: meters per degree longitude 37 | ''' 38 | latrad = lat*2.0*pi/360.0 39 | dx = 111415.13 * cos(latrad) \ 40 | - 94.55 * cos(3.0*latrad) \ 41 | + 0.12 * cos(5.0*latrad) 42 | return dx 43 | 44 | def ll2xy(lat, lon, orglat, orglon): 45 | ''' 46 | AlvinXY: Lat/Long to X/Y 47 | Converts Lat/Lon (WGS84) to Alvin XYs using a Mercator projection. 48 | 49 | Args: 50 | lat (float): Latitude of location 51 | lon (float): Longitude of location 52 | orglat (float): Latitude of origin location 53 | orglon (float): Longitude of origin location 54 | 55 | Returns: 56 | tuple: (x,y) where... 57 | x is Easting in m (Alvin local grid) 58 | y is Northing in m (Alvin local grid) 59 | ''' 60 | x = (lon - orglon) * mdeglon(orglat); 61 | y = (lat - orglat) * mdeglat(orglat); 62 | return (x,y) 63 | 64 | 65 | 66 | def xy2ll(x, y, orglat, orglon): 67 | 68 | ''' 69 | X/Y to Lat/Lon 70 | Converts Alvin XYs to Lat/Lon (WGS84) using a Mercator projection. 71 | 72 | Args: 73 | x (float): Easting in m (Alvin local grid) 74 | x (float): Northing in m (Alvin local grid) 75 | orglat (float): Latitude of origin location 76 | orglon (float): Longitude of origin location 77 | 78 | Returns: 79 | tuple: (lat,lon) 80 | ''' 81 | lon = x/mdeglon(orglat) + orglon 82 | lat = y/mdeglat(orglat) + orglat 83 | 84 | return (lat, lon) 85 | 86 | 87 | # Vectorize 88 | vxy2ll = np.vectorize(xy2ll) 89 | vll2xy = np.vectorize(ll2xy) 90 | -------------------------------------------------------------------------------- /src/alvinxy/alvinxy_example.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Here is an example of using lat/lon along with AlvinXY coordinates. 3 | ''' 4 | 5 | import alvinxy 6 | reload(alvinxy) 7 | import numpy as np 8 | 9 | # Specify an origin 10 | origin = [21.3190429, -157.6689890] 11 | 12 | x = 10 13 | y = 10 14 | lat, lon = alvinxy.xy2ll(x,y,origin[0],origin[1]) 15 | print ('lat: %.10f, lon:%.10f'%(lat,lon)) 16 | xx,yy = alvinxy.ll2xy(lat,lon,origin[0],origin[1]) 17 | print ('x: %.10f, y:%.10f'%(xx,yy)) 18 | 19 | # Use vectorized version 20 | x = np.arange(1,100) 21 | y = np.arange(1,100) 22 | lat,lon = alvinxy.vxy2ll(x,y,origin[0],origin[1]) 23 | xx,yy = alvinxy.vll2xy(lat,lon,origin[0],origin[1]) 24 | print xx 25 | print yy 26 | -------------------------------------------------------------------------------- /src/geonav_transform.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2017, Brian Bingham 4 | All rights reserved 5 | 6 | This file is part of the geonav_transform package. 7 | 8 | Geonav_transform is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | Geonav_transform is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this package. If not, see . 20 | 21 | */ 22 | 23 | #include "geonav_transform/geonav_transform.h" 24 | #include "geonav_transform/navsat_conversions.h" 25 | #include "geonav_transform/geonav_utilities.h" 26 | 27 | #include 28 | 29 | #include 30 | 31 | #include 32 | 33 | namespace GeonavTransform 34 | { 35 | GeonavTransform::GeonavTransform() : 36 | // Initialize attributes 37 | broadcast_utm2odom_transform_(true), 38 | broadcast_odom2base_transform_(true), 39 | nav_frame_id_(""), 40 | zero_altitude_(true), 41 | utm_frame_id_("utm"), 42 | odom_frame_id_("odom"), 43 | base_link_frame_id_("base_link"), 44 | utm_zone_(""), 45 | tf_listener_(tf_buffer_) 46 | { 47 | // Initialize transforms 48 | transform_odom2base_=tf2::Transform(tf2::Transform::getIdentity()); 49 | transform_odom2base_inverse_=tf2::Transform(tf2::Transform::getIdentity()); 50 | transform_utm2odom_=tf2::Transform(tf2::Transform::getIdentity()); 51 | transform_utm2odom_inverse_=tf2::Transform(tf2::Transform::getIdentity()); 52 | } 53 | 54 | GeonavTransform::~GeonavTransform() 55 | { 56 | } 57 | 58 | void GeonavTransform::run() 59 | { 60 | 61 | double frequency = 10.0; 62 | double delay = 0.0; 63 | 64 | ros::NodeHandle nh; 65 | ros::NodeHandle nh_priv("~"); 66 | 67 | nav_update_time_ = ros::Time::now(); 68 | 69 | // Load ROS parameters 70 | nh_priv.param("frequency", frequency, 10.0); 71 | nh_priv.param("orientation_ned", orientation_ned_, true); 72 | 73 | nh_priv.param("broadcast_utm2odom_transform", broadcast_utm2odom_transform_, true); 74 | nh_priv.param("broadcast_odom2base_transform", broadcast_odom2base_transform_, true); 75 | nh_priv.param("zero_altitude", zero_altitude_, false); 76 | nh_priv.param("base_link_frame_id", base_link_frame_id_, "base_link"); 77 | nh_priv.param("odom_frame_id", odom_frame_id_, "odom"); 78 | nh_priv.param("utm_frame_id", utm_frame_id_, "utm"); 79 | 80 | // Datum parameter - required 81 | double datum_lat; 82 | double datum_lon; 83 | double datum_yaw; 84 | tf2::Quaternion quat = tf2::Quaternion::getIdentity(); 85 | 86 | if ( (! nh_priv.hasParam("datum")) && (! nh.hasParam("/geonav_datum")) ) 87 | { 88 | ROS_FATAL("Neither global " 89 | "nor private parameter is not supplied in " 90 | "geonav_transform configuration"); 91 | exit(1); 92 | } 93 | else 94 | { 95 | XmlRpc::XmlRpcValue datum_config; 96 | if ( nh.hasParam("/geonav_datum") ) 97 | { 98 | if ( nh_priv.hasParam("datum") ) 99 | { 100 | ROS_ERROR("Both global and private parameter" 101 | "are supplied. Using the global parameter."); 102 | } 103 | try 104 | { 105 | nh.getParam("/geonav_datum", datum_config); 106 | } 107 | catch (XmlRpc::XmlRpcException &e) 108 | { 109 | ROS_FATAL_STREAM("ERROR geonav_datum config: " << e.getMessage() << 110 | " for geonav_transform (type: " 111 | << datum_config.getType() << ")"); 112 | exit(1); 113 | } 114 | } 115 | else 116 | { 117 | try 118 | { 119 | nh_priv.getParam("datum", datum_config); 120 | } 121 | catch (XmlRpc::XmlRpcException &e) 122 | { 123 | ROS_FATAL_STREAM("ERROR datum config: " << e.getMessage() << 124 | " for geonav_transform (type: " 125 | << datum_config.getType() << ")"); 126 | exit(1); 127 | } 128 | } 129 | try 130 | { 131 | /* Handle datum specification. 132 | Users should always specify a baseLinkFrameId_ in the 133 | datum config, but we had a release where it wasn't used, 134 | so we'll maintain compatibility.*/ 135 | ROS_ASSERT(datum_config.getType() == XmlRpc::XmlRpcValue::TypeArray); 136 | ROS_ASSERT(datum_config.size() >= 3); 137 | 138 | if (datum_config.size() > 3) 139 | { 140 | ROS_WARN_STREAM("Deprecated datum parameter configuration detected. " 141 | "Only the first three parameters " 142 | "(latitude, longitude, yaw) will be used. frame_ids " 143 | "will be derived from odometry and navsat inputs."); 144 | } 145 | // Parse the data spec. 146 | std::ostringstream ostr; 147 | ostr << datum_config[0] << " " << datum_config[1] << " " << datum_config[2]; 148 | std::istringstream istr(ostr.str()); 149 | istr >> datum_lat >> datum_lon >> datum_yaw; 150 | } 151 | catch (XmlRpc::XmlRpcException &e) 152 | { 153 | ROS_FATAL_STREAM("ERROR datum config: " << e.getMessage() << 154 | " for geonav_transform (type: " 155 | << datum_config.getType() << ")"); 156 | exit(1); 157 | } 158 | 159 | // Tell everyone we are ignoring yaw in the datum 160 | if (fabs(datum_yaw) > 0.01) 161 | { 162 | ROS_WARN("Yaw of the datum is ignored!"); 163 | } 164 | datum_yaw = 0.0; 165 | 166 | // Try to resolve tf_prefix 167 | std::string tf_prefix = ""; 168 | std::string tf_prefix_path = ""; 169 | if (nh_priv.searchParam("tf_prefix", tf_prefix_path)) 170 | { 171 | nh_priv.getParam(tf_prefix_path, tf_prefix); 172 | } 173 | 174 | // Append the tf prefix in a tf2-friendly manner 175 | GeonavUtilities::appendPrefix(tf_prefix, utm_frame_id_); 176 | GeonavUtilities::appendPrefix(tf_prefix, odom_frame_id_); 177 | GeonavUtilities::appendPrefix(tf_prefix, base_link_frame_id_); 178 | 179 | // Convert specified yaw to quaternion 180 | // Not currently effective since we ignore the yaw! 181 | //quat.setRPY(0.0, 0.0, datum_yaw); 182 | } // end of datum config. 183 | 184 | // Setup transforms and messages 185 | nav_in_odom_.header.frame_id = odom_frame_id_; 186 | nav_in_odom_.child_frame_id = base_link_frame_id_; 187 | nav_in_odom_.header.seq = 0; 188 | nav_in_utm_.header.frame_id = utm_frame_id_; 189 | nav_in_utm_.child_frame_id = base_link_frame_id_; 190 | nav_in_utm_.header.seq = 0; 191 | transform_msg_utm2odom_.header.frame_id = utm_frame_id_; 192 | transform_msg_utm2odom_.child_frame_id = odom_frame_id_; 193 | transform_msg_utm2odom_.header.seq = 0; 194 | transform_msg_odom2base_.header.frame_id = odom_frame_id_; 195 | transform_msg_odom2base_.child_frame_id = base_link_frame_id_; 196 | transform_msg_odom2base_.header.seq = 0; 197 | 198 | // Set datum - published static transform 199 | setDatum(datum_lat, datum_lon, 0.0, quat); // alt is 0.0 for now 200 | 201 | // Publisher - Odometry relative to the odom frame 202 | odom_pub_ = nh.advertise("geonav_odom", 10); 203 | utm_pub_ = nh.advertise("geonav_utm", 10); 204 | 205 | // Publisher - Odometry in Geo frame 206 | geo_pub_ = nh.advertise("geonav_geo", 10); 207 | 208 | // Subscriber - Odometry in GPS frame. 209 | // for converstion from geo. coord. to local nav. coord. 210 | ros::Subscriber geo_odom_sub = nh.subscribe("nav_odom", 1, 211 | &GeonavTransform::navOdomCallback, 212 | this); 213 | // Subscriber - Odometry in Nav. frame. 214 | // for conversion from local nav. coord. to geo. coord 215 | ros::Subscriber nav_odom_sub = nh.subscribe("geo_odom", 1, 216 | &GeonavTransform::geoOdomCallback, 217 | this); 218 | 219 | 220 | // Loop 221 | ros::Rate rate(frequency); 222 | while (ros::ok()) 223 | { 224 | ros::spinOnce(); 225 | 226 | // Check for odometry 227 | if ( (ros::Time::now().toSec()-nav_update_time_.toSec()) > 1.0 ){ 228 | ROS_WARN_STREAM("Haven't received Odometry on <" 229 | << geo_odom_sub.getTopic() << "> for 1.0 seconds!" 230 | << " Will not broadcast transform!"); 231 | } 232 | else{ 233 | // send transforms - particularly odom->base (utm->odom is static) 234 | broadcastTf(); 235 | } 236 | rate.sleep(); 237 | } // end of Loop 238 | } // end of ::run() 239 | 240 | void GeonavTransform::broadcastTf(void) 241 | { 242 | transform_msg_odom2base_.header.stamp = ros::Time::now(); 243 | transform_msg_odom2base_.header.seq++; 244 | transform_msg_odom2base_.transform = tf2::toMsg(transform_odom2base_); 245 | tf_broadcaster_.sendTransform(transform_msg_odom2base_); 246 | } 247 | bool GeonavTransform::setDatum(double lat, double lon, double alt, 248 | tf2::Quaternion q) 249 | { 250 | double utm_x = 0; 251 | double utm_y = 0; 252 | NavsatConversions::LLtoUTM(lat, lon, utm_y, utm_x, utm_zone_); 253 | 254 | ROS_INFO_STREAM("Datum (latitude, longitude, altitude) is (" 255 | << std::fixed << lat << ", " 256 | << lon << ", " << alt << ")"); 257 | ROS_INFO_STREAM("Datum UTM Zone is: " << utm_zone_); 258 | ROS_INFO_STREAM("Datum UTM coordinate is (" 259 | << std::fixed << utm_x << ", " << utm_y << ")"); 260 | 261 | 262 | // Set the transform utm->odom 263 | transform_utm2odom_.setOrigin(tf2::Vector3(utm_x, utm_y, alt)); 264 | transform_utm2odom_.setRotation(q); 265 | transform_utm2odom_inverse_ = transform_utm2odom_.inverse(); 266 | // Convert quaternion to RPY - to double check and diplay 267 | tf2::Matrix3x3 mat(q); 268 | double roll, pitch, yaw; 269 | mat.getRPY(roll, pitch, yaw); 270 | ROS_INFO_STREAM("Datum orientation roll, pitch, yaw is (" 271 | << roll << ", " << pitch << ", " << yaw << ")"); 272 | 273 | 274 | //ROS_INFO_STREAM("Transform utm -> odom is: " << transform_utm2odom_); 275 | 276 | // Send out static UTM transform - frames are specified in ::run() 277 | transform_msg_utm2odom_.header.stamp = ros::Time::now(); 278 | transform_msg_utm2odom_.header.seq++; 279 | transform_msg_utm2odom_.transform = tf2::toMsg(transform_utm2odom_); 280 | transform_msg_utm2odom_.transform.translation.z = (zero_altitude_ ? 0.0 : transform_msg_utm2odom_.transform.translation.z); 281 | utm_broadcaster_.sendTransform(transform_msg_utm2odom_); 282 | 283 | return true; 284 | } // end setDatum 285 | 286 | void GeonavTransform::navOdomCallback(const nav_msgs::OdometryConstPtr& msg) 287 | { 288 | nav_frame_id_ = msg->header.frame_id; 289 | if (nav_frame_id_.empty()) 290 | { 291 | ROS_WARN_STREAM_ONCE("Odometry message has empty frame_id. " 292 | "Will assume navsat device is mounted at " 293 | "robot's origin."); 294 | } 295 | // Make sure the GPS data is usable - can't use NavSatStatus since we 296 | // are making due with an Odometry message 297 | bool good_gps = (!std::isnan(msg->pose.pose.position.x) && 298 | !std::isnan(msg->pose.pose.position.y) && 299 | !std::isnan(msg->pose.pose.position.z)); 300 | if (!good_gps) 301 | { 302 | ROS_WARN_STREAM("Bad GPS! Won't transfrom"); 303 | return; 304 | } 305 | 306 | double utmX = 0; 307 | double utmY = 0; 308 | std::string utm_zone_tmp; 309 | nav_update_time_ = ros::Time::now(); 310 | NavsatConversions::LLtoUTM(msg->pose.pose.position.y, 311 | msg->pose.pose.position.x, 312 | utmY, utmX, utm_zone_tmp); 313 | ROS_DEBUG_STREAM_THROTTLE(2.0,"Latest GPS (lat, lon, alt): " 314 | << msg->pose.pose.position.y << " , " 315 | << msg->pose.pose.position.x << " , " 316 | << msg->pose.pose.position.z ); 317 | ROS_DEBUG_STREAM_THROTTLE(2.0,"UTM of latest GPS is (X,Y):" 318 | << utmX << " , " << utmY); 319 | 320 | // For now the 'nav' frame is that same as the 'base_link' frame 321 | transform_utm2nav_.setOrigin(tf2::Vector3(utmX, utmY, 322 | msg->pose.pose.position.z)); 323 | transform_utm2nav_.setRotation(tf2::Quaternion(msg->pose.pose.orientation.x, 324 | msg->pose.pose.orientation.y, 325 | msg->pose.pose.orientation.z, 326 | msg->pose.pose.orientation.w)); 327 | transform_utm2nav_inverse_=transform_utm2nav_.inverse(); 328 | 329 | // Publish Nav/Base Odometry in UTM frame - note frames are set in ::run() 330 | nav_in_utm_.header.stamp = nav_update_time_; 331 | nav_in_utm_.header.seq++; 332 | // Create position information using transform. 333 | // Convert from transform to pose message 334 | //tf2::toMsg(transform_utm2nav_, nav_in_utm_.pose.pose); 335 | tf2::Vector3 tmp; 336 | tmp = transform_utm2nav_.getOrigin(); 337 | nav_in_utm_.pose.pose.position.x = tmp[0]; 338 | nav_in_utm_.pose.pose.position.y = tmp[1]; 339 | nav_in_utm_.pose.pose.position.z = tmp[2]; 340 | 341 | nav_in_utm_.pose.pose.position.z = (zero_altitude_ ? 0.0 : nav_in_utm_.pose.pose.position.z); 342 | // Create orientation information directy from incoming orientation 343 | nav_in_utm_.pose.pose.orientation = msg->pose.pose.orientation; 344 | nav_in_utm_.pose.covariance = msg->pose.covariance; 345 | // For twist - ignore the rotation since both are in the base_link/nav frame 346 | nav_in_utm_.twist.twist.linear = msg->twist.twist.linear; 347 | nav_in_utm_.twist.twist.angular = msg->twist.twist.angular; 348 | nav_in_utm_.twist.covariance = msg->twist.covariance; 349 | // Publish 350 | utm_pub_.publish(nav_in_utm_); 351 | 352 | // Calculate Nav in odom frame 353 | // Note the 'base' and 'nav' frames are the same for now 354 | // odom2base = odom2nav = odom2utm * utm2nav 355 | transform_odom2base_.mult(transform_utm2odom_inverse_,transform_utm2nav_); 356 | 357 | ROS_DEBUG_STREAM_THROTTLE(2.0,"utm2nav X:" 358 | << transform_utm2nav_.getOrigin()[0] 359 | << "Y:" << transform_utm2nav_.getOrigin()[1] ); 360 | ROS_DEBUG_STREAM_THROTTLE(2.0,"utm2odom X:" 361 | << transform_utm2odom_.getOrigin()[0] 362 | << "Y:" << transform_utm2odom_.getOrigin()[1] ); 363 | ROS_DEBUG_STREAM_THROTTLE(2.0,"utm2odom_inverse X:" 364 | << transform_utm2odom_inverse_.getOrigin()[0] 365 | << "Y:" 366 | << transform_utm2odom_inverse_.getOrigin()[1] ); 367 | ROS_DEBUG_STREAM_THROTTLE(2.0,"odom2base X:" 368 | << transform_odom2base_.getOrigin()[0] 369 | << "Y:" << transform_odom2base_.getOrigin()[1] ); 370 | 371 | 372 | // Publish Nav odometry in odom frame - note frames are set in ::run() 373 | nav_in_odom_.header.stamp = nav_update_time_; 374 | nav_in_odom_.header.seq++; 375 | // Position from transform 376 | tf2::toMsg(transform_odom2base_, nav_in_odom_.pose.pose); 377 | nav_in_odom_.pose.pose.position.z = (zero_altitude_ ? 0.0 : nav_in_odom_.pose.pose.position.z); 378 | // Orientation and twist are uneffected 379 | nav_in_odom_.pose.pose.orientation = msg->pose.pose.orientation; 380 | nav_in_odom_.pose.covariance = msg->pose.covariance; 381 | nav_in_odom_.twist.twist.linear = msg->twist.twist.linear; 382 | nav_in_odom_.twist.twist.angular = msg->twist.twist.angular; 383 | nav_in_odom_.twist.covariance = msg->twist.covariance; 384 | odom_pub_.publish(nav_in_odom_); 385 | } // navOdomCallback 386 | 387 | void GeonavTransform::geoOdomCallback(const nav_msgs::OdometryConstPtr& msg) 388 | { 389 | // Convert position from odometry frame to UTM 390 | // nav and base are same for now 391 | // utm2base = utm2nav = utm2odom * odom2nav 392 | transform_odom2nav_.setOrigin(tf2::Vector3(msg->pose.pose.position.x, 393 | msg->pose.pose.position.y, 394 | msg->pose.pose.position.z)); 395 | transform_odom2nav_.setRotation(tf2::Quaternion(msg->pose.pose.orientation.x, 396 | msg->pose.pose.orientation.y, 397 | msg->pose.pose.orientation.z, 398 | msg->pose.pose.orientation.w)); 399 | transform_odom2nav_inverse_ = transform_odom2nav_.inverse(); 400 | transform_utm2nav_.mult(transform_utm2odom_, transform_odom2nav_); 401 | 402 | // Convert from UTM to LL 403 | double lat; 404 | double lon; 405 | tf2::Vector3 tmp; 406 | tmp = transform_utm2nav_.getOrigin(); 407 | NavsatConversions::UTMtoLL(tmp[1], tmp[0], utm_zone_, 408 | lat, lon); 409 | 410 | nav_in_geo_.header.stamp = ros::Time::now(); 411 | nav_in_geo_.pose.pose.position.x = lon; 412 | nav_in_geo_.pose.pose.position.y = lat; 413 | nav_in_geo_.pose.pose.position.z = 0.0; 414 | // Create orientation information directy from incoming orientation 415 | nav_in_geo_.pose.pose.orientation = msg->pose.pose.orientation; 416 | nav_in_geo_.pose.covariance = msg->pose.covariance; 417 | // For twist - ignore the rotation since both are in the base_link/nav frame 418 | nav_in_geo_.twist.twist.linear = msg->twist.twist.linear; 419 | nav_in_geo_.twist.twist.angular = msg->twist.twist.angular; 420 | nav_in_geo_.twist.covariance = msg->twist.covariance; 421 | // Publish 422 | geo_pub_.publish(nav_in_geo_); 423 | } // geoOdomCallback 424 | 425 | } // namespace GeonavTransform 426 | 427 | -------------------------------------------------------------------------------- /src/geonav_transform/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsb808/geonav_transform/8894342ca6821514f5401ccf63516041d07e44b5/src/geonav_transform/__init__.py -------------------------------------------------------------------------------- /src/geonav_transform/geonav_conversions.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Python version of the inline functions defined in the robot_localization, 3 | navsat_conversions.h 4 | ''' 5 | 6 | from math import * 7 | import re 8 | 9 | RADIANS_PER_DEGREE = pi/180.0; 10 | DEGREES_PER_RADIAN = 180.0/pi; 11 | 12 | # Grid granularity for rounding UTM coordinates to generate MapXY. 13 | grid_size = 100000.0; # 100 km grid 14 | 15 | # WGS84 Parameters 16 | WGS84_A =6378137.0 # major axis 17 | WGS84_B =6356752.31424518 # minor axis 18 | WGS84_F =0.0033528107 # ellipsoid flattening 19 | WGS84_E =0.0818191908 # first eccentricity 20 | WGS84_EP =0.0820944379 # second eccentricity 21 | 22 | # UTM Parameters 23 | UTM_K0 = 0.9996 # scale factor 24 | UTM_FE = 500000.0 # false easting 25 | UTM_FN_N = 0.0 # false northing, northern hemisphere 26 | UTM_FN_S = 10000000.0 # false northing, southern hemisphere 27 | UTM_E2 = (WGS84_E*WGS84_E) # e^2 28 | UTM_E4 = (UTM_E2*UTM_E2) # e^4 29 | UTM_E6 = (UTM_E4*UTM_E2) # e^6 30 | UTM_EP2 = (UTM_E2/(1-UTM_E2)) # e'^2 31 | 32 | 33 | def ll2xy(lat,lon,origin_lat,origin_lon): 34 | ''' 35 | Geonav: Lat/Long to X/Y 36 | Convert latitude and longitude in dec. degress to x and y in meters 37 | relative to the given origin location. Converts lat/lon and orgin to UTM and then takes the difference 38 | 39 | Args: 40 | lat (float): Latitude of location 41 | lon (float): Longitude of location 42 | orglat (float): Latitude of origin location 43 | orglon (float): Longitude of origin location 44 | 45 | Returns: 46 | tuple: (x,y) where... 47 | x is Easting in m (local grid) 48 | y is Northing in m (local grid) 49 | ''' 50 | 51 | outmy, outmx, outmzone = LLtoUTM(origin_lat,origin_lon) 52 | utmy, utmx, utmzone = LLtoUTM(lat,lon) 53 | if (not (outmzone==utmzone)): 54 | print('WARNING: geonav_conversion: origin and location are in different UTM zones!') 55 | y = utmy-outmy 56 | x = utmx-outmx 57 | return (x,y) 58 | 59 | def xy2ll(x, y, orglat, orglon): 60 | ''' 61 | ''' 62 | outmy, outmx, outmzone = LLtoUTM(orglat,orglon) 63 | utmy = outmy+y 64 | utmx = outmx+x 65 | return UTMtoLL(utmy,utmx,outmzone) 66 | 67 | '''* 68 | * Determine the correct UTM letter designator for the 69 | * given latitude 70 | * 71 | * @returns 'Z' if latitude is outside the UTM limits of 84N to 80S 72 | * 73 | * Written by Chuck Gantz- chuck.gantz@globalstar.com 74 | ''' 75 | def UTMLetterDesignator(Lat): 76 | 77 | LetterDesignator ="" 78 | 79 | if ((84 >= Lat) and (Lat >= 72)): LetterDesignator = 'X' 80 | 81 | elif ((72 > Lat) and (Lat >= 64)): LetterDesignator = 'W'; 82 | elif ((64 > Lat) and (Lat >= 56)): LetterDesignator = 'V'; 83 | elif ((56 > Lat) and (Lat >= 48)): LetterDesignator = 'U'; 84 | elif ((48 > Lat) and (Lat >= 40)): LetterDesignator = 'T'; 85 | elif ((40 > Lat) and (Lat >= 32)): LetterDesignator = 'S'; 86 | elif ((32 > Lat) and (Lat >= 24)): LetterDesignator = 'R'; 87 | elif ((24 > Lat) and (Lat >= 16)): LetterDesignator = 'Q'; 88 | elif ((16 > Lat) and (Lat >= 8)) : LetterDesignator = 'P'; 89 | elif (( 8 > Lat) and (Lat >= 0)) : LetterDesignator = 'N'; 90 | elif (( 0 > Lat) and (Lat >= -8)): LetterDesignator = 'M'; 91 | elif ((-8 > Lat) and (Lat >= -16)): LetterDesignator = 'L'; 92 | elif ((-16 > Lat) and (Lat >= -24)): LetterDesignator = 'K'; 93 | elif ((-24 > Lat) and (Lat >= -32)): LetterDesignator = 'J'; 94 | elif ((-32 > Lat) and (Lat >= -40)): LetterDesignator = 'H'; 95 | elif ((-40 > Lat) and (Lat >= -48)): LetterDesignator = 'G'; 96 | elif ((-48 > Lat) and (Lat >= -56)): LetterDesignator = 'F'; 97 | elif ((-56 > Lat) and (Lat >= -64)): LetterDesignator = 'E'; 98 | elif ((-64 > Lat) and (Lat >= -72)): LetterDesignator = 'D'; 99 | elif ((-72 > Lat) and (Lat >= -80)): LetterDesignator = 'C'; 100 | # 'Z' is an error flag, the Latitude is outside the UTM limits 101 | else: LetterDesignator = 'Z'; 102 | return LetterDesignator 103 | 104 | 105 | '''* 106 | * Convert lat/long to UTM coords. Equations from USGS Bulletin 1532 107 | * 108 | * East Longitudes are positive, West longitudes are negative. 109 | * North latitudes are positive, South latitudes are negative 110 | * Lat and Long are in fractional degrees 111 | * 112 | * Written by Chuck Gantz- chuck.gantz@globalstar.com 113 | Retuns a tuple of (UTMNorthing, UTMEasting, UTMZone) 114 | ''' 115 | def LLtoUTM(Lat,Long): 116 | 117 | a = WGS84_A; 118 | eccSquared = UTM_E2; 119 | k0 = UTM_K0; 120 | 121 | # Make sure the longitude is between -180.00 .. 179.9 122 | LongTemp = (Long+180.0)-int((Long+180.)/360.)*360.-180.; 123 | 124 | LatRad = Lat*RADIANS_PER_DEGREE; 125 | LongRad = LongTemp*RADIANS_PER_DEGREE; 126 | ZoneNumber = int((LongTemp + 180.0)/6.0) + 1; 127 | 128 | if ( Lat >= 56.0 and Lat < 64.0 and LongTemp >= 3.0 and LongTemp < 12.0 ): 129 | ZoneNumber = 32; 130 | # Special zones for Svalbard 131 | if ( Lat >= 72.0 and Lat < 84.0 ): 132 | if ( LongTemp >= 0.0 and LongTemp < 9.0 ): ZoneNumber = 31; 133 | elif ( LongTemp >= 9.0 and LongTemp < 21.0 ): ZoneNumber = 33; 134 | elif ( LongTemp >= 21.0 and LongTemp < 33.0 ): ZoneNumber = 35; 135 | elif ( LongTemp >= 33.0 and LongTemp < 42.0 ): ZoneNumber = 37; 136 | # +3 puts origin in middle of zone 137 | LongOrigin = (ZoneNumber - 1.0)*6.0 - 180.0 + 3.0; 138 | LongOriginRad = LongOrigin * RADIANS_PER_DEGREE; 139 | 140 | # Compute the UTM Zone from the latitude and longitude 141 | UTMZone = "%d%s"%(ZoneNumber,UTMLetterDesignator(Lat)) 142 | #print("UTM Zone: %s"%(UTMZone)) 143 | eccPrimeSquared = (eccSquared)/(1.0-eccSquared); 144 | N = a/sqrt(1-eccSquared*sin(LatRad)*sin(LatRad)); 145 | T = tan(LatRad)*tan(LatRad); 146 | C = eccPrimeSquared*cos(LatRad)*cos(LatRad); 147 | A = cos(LatRad)*(LongRad-LongOriginRad); 148 | 149 | M = a*((1 - eccSquared/4.0 - 3.0*eccSquared*eccSquared/64.0 150 | - 5.0*eccSquared*eccSquared*eccSquared/256.0) * LatRad 151 | - (3.0*eccSquared/8.0 + 3.0*eccSquared*eccSquared/32.0 152 | + 45.0*eccSquared*eccSquared*eccSquared/1024.0)*sin(2.0*LatRad) 153 | + (15.0*eccSquared*eccSquared/256.0 154 | + 45.0*eccSquared*eccSquared*eccSquared/1024.0)*sin(4.0*LatRad) 155 | - (35.0*eccSquared*eccSquared*eccSquared/3072.0)*sin(6.0*LatRad)); 156 | 157 | UTMEasting = (k0*N*(A+(1.0-T+C)*A*A*A/6.0 158 | + (5.0-18.0*T+T*T+72*C 159 | - 58.0*eccPrimeSquared)*A*A*A*A*A/120.0) 160 | + 500000.0) 161 | 162 | UTMNorthing = (k0*(M+N*tan(LatRad) 163 | *(A*A/2.0+(5.0-T+9.0*C+4.0*C*C)*A*A*A*A/24.0 164 | + (61.0-58.0*T+T*T+600.0*C 165 | - 330.0*eccPrimeSquared)*A*A*A*A*A*A/720.0))); 166 | if (Lat < 0): 167 | # 10000000 meter offset for southern hemisphere 168 | UTMNorthing += 10000000.0; 169 | 170 | return (UTMNorthing, UTMEasting, UTMZone) 171 | 172 | '''* 173 | * Converts UTM coords to lat/long. Equations from USGS Bulletin 1532 174 | * 175 | * East Longitudes are positive, West longitudes are negative. 176 | * North latitudes are positive, South latitudes are negative 177 | * Lat and Long are in fractional degrees. 178 | * 179 | * Written by Chuck Gantz- chuck.gantz@globalstar.com 180 | Returns (Lat, Lon, UTMZone) 181 | ''' 182 | def UTMtoLL(UTMNorthing,UTMEasting,UTMZone): 183 | k0 = UTM_K0; 184 | a = WGS84_A; 185 | eccSquared = UTM_E2; 186 | e1 = (1-sqrt(1-eccSquared))/(1+sqrt(1-eccSquared)); 187 | 188 | x = UTMEasting - 500000.0; # remove 500,000 meter offset for longitude 189 | y = UTMNorthing; 190 | 191 | ZoneLetter = re.findall('([a-zA-Z])',UTMZone)[0] 192 | ZoneNumber = float( UTMZone.split(ZoneLetter)[0] ) 193 | 194 | if (ZoneLetter <'N'): 195 | # remove 10,000,000 meter offset used for southern hemisphere 196 | y -= 10000000.0; 197 | 198 | # +3 puts origin in middle of zone 199 | LongOrigin = (ZoneNumber - 1)*6.0 - 180.0 + 3.0; 200 | eccPrimeSquared = (eccSquared)/(1.0-eccSquared); 201 | M = y / k0; 202 | mu = M/(a*(1.0-eccSquared/4.0-3.0*eccSquared*eccSquared/64.0 203 | -5.0*eccSquared*eccSquared*eccSquared/256.0)); 204 | phi1Rad = mu + ((3.0*e1/2.0-27.0*e1*e1*e1/32.0)*sin(2.0*mu) 205 | + (21.0*e1*e1/16.0-55.0*e1*e1*e1*e1/32.0)*sin(4.0*mu) 206 | + (151.0*e1*e1*e1/96.0)*sin(6.0*mu)); 207 | 208 | N1 = a/sqrt(1.0-eccSquared*sin(phi1Rad)*sin(phi1Rad)); 209 | T1 = tan(phi1Rad)*tan(phi1Rad); 210 | C1 = eccPrimeSquared*cos(phi1Rad)*cos(phi1Rad); 211 | R1 = a*(1.0-eccSquared)/pow(1-eccSquared*sin(phi1Rad)*sin(phi1Rad), 1.5); 212 | D = x/(N1*k0); 213 | Lat = phi1Rad - ((N1*tan(phi1Rad)/R1) 214 | *(D*D/2.0 215 | -(5.0+3.0*T1+10.0*C1-4.0*C1*C1 216 | -9.0*eccPrimeSquared)*D*D*D*D/24.0 217 | +(61.0+90.0*T1+298.0*C1+45.0*T1*T1-252.0*eccPrimeSquared 218 | -3.0*C1*C1)*D*D*D*D*D*D/720.0)); 219 | 220 | Lat = Lat * DEGREES_PER_RADIAN; 221 | 222 | Long = ((D-(1.0+2.0*T1+C1)*D*D*D/6.0 223 | +(5.0-2.0*C1+28.0*T1-3.0*C1*C1+8.0*eccPrimeSquared+24.0*T1*T1) 224 | *D*D*D*D*D/120.0) 225 | / cos(phi1Rad)); 226 | Long = LongOrigin + Long * DEGREES_PER_RADIAN; 227 | 228 | return (Lat, Long) 229 | -------------------------------------------------------------------------------- /src/geonav_transform/utmtest.py: -------------------------------------------------------------------------------- 1 | import random 2 | from math import * 3 | import geonav_conversions as nc 4 | reload(nc) 5 | 6 | lat = 36.6 7 | lon = -121.9 8 | 9 | utmx, utmy, utmzone = nc.LLtoUTM(lat,lon) 10 | print ("UTM X: %.10f, Y: %.10f, Zone: %s"%(utmx,utmy,utmzone)) 11 | 12 | lat, lon = nc.UTMtoLL(utmx,utmy,utmzone) 13 | print("Lat: %.10f, Long: %.10f"%(lat,lon)) 14 | 15 | cnt = 1 16 | err = [] 17 | for ii in range(10000000): 18 | lat = random.uniform(-80.0,80.0) 19 | lon = random.uniform(-180.0,179.9999) 20 | 21 | utmx, utmy, utmzone = nc.LLtoUTM(lat,lon) 22 | lat2, lon2 = nc.UTMtoLL(utmx,utmy,utmzone) 23 | 24 | utmx2, utmy2, utmzone2 = nc.LLtoUTM(lat2,lon2) 25 | dlat = lat-lat2 26 | dlon = lon-lon2 27 | tol = 1e-6 28 | if (abs(dlat)>tol) or (abs(dlon)>tol): 29 | print("-------%d-------"%cnt) 30 | print("Lat: %.10f, Long: %.10f"%(lat,lon)) 31 | print ("UTM X: %.10f, Y: %.10f, Zone: %s"%(utmx,utmy,utmzone)) 32 | print("dLat: %.10f, dLong: %.10f"%(dlat,dlon)) 33 | dutmx = utmx-utmx2 34 | dutmy = utmy-utmy2 35 | print("dUtmx: %.10f, dUtmy: %.10f"%(dutmx,dutmy)) 36 | cnt +=1 37 | err.append(sqrt(dutmx**2+dutmy**2)) 38 | 39 | print err 40 | print("Max Error [m]: %.10f"%max(err)) 41 | #if (ii%1000)==0: 42 | # print("ii: %d"%ii) 43 | 44 | -------------------------------------------------------------------------------- /src/geonav_transform_node.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2017, Brian Bingham 4 | All rights reserved 5 | 6 | This file is part of the geonav_transform package. 7 | 8 | Geonav_transform is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | Geonav_transform is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with Foobar. If not, see . 20 | 21 | */ 22 | 23 | #include "geonav_transform/geonav_transform.h" 24 | 25 | #include 26 | 27 | int main(int argc, char **argv) 28 | { 29 | ros::init(argc, argv, "geonav_transform_node"); 30 | GeonavTransform::GeonavTransform trans; 31 | trans.run(); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /src/geonav_utilities.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2017, Brian Bingham 4 | All rights reserved 5 | 6 | This file is part of the geonav_transform package. 7 | 8 | Geonav_transform is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | Geonav_transform is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with Foobar. If not, see . 20 | 21 | */ 22 | 23 | #include "geonav_transform/geonav_utilities.h" 24 | 25 | //#include 26 | //#include 27 | 28 | namespace GeonavTransform 29 | { 30 | 31 | namespace GeonavUtilities 32 | { 33 | void appendPrefix(std::string tfPrefix, std::string &frameId) 34 | { 35 | // Strip all leading slashes for tf2 compliance 36 | if (!frameId.empty() && frameId.at(0) == '/') 37 | { 38 | frameId = frameId.substr(1); 39 | } 40 | 41 | if (!tfPrefix.empty() && tfPrefix.at(0) == '/') 42 | { 43 | tfPrefix = tfPrefix.substr(1); 44 | } 45 | 46 | // If we do have a tf prefix, then put a slash in between 47 | if (!tfPrefix.empty()) 48 | { 49 | frameId = tfPrefix + "/" + frameId; 50 | } 51 | } 52 | 53 | double clampRotation(double rotation) 54 | { 55 | while (rotation > PI) 56 | { 57 | rotation -= TAU; 58 | } 59 | 60 | while (rotation < -PI) 61 | { 62 | rotation += TAU; 63 | } 64 | 65 | return rotation; 66 | } 67 | 68 | } // namespace GeonavUtilities 69 | 70 | } // namespace GeonavTransform 71 | -------------------------------------------------------------------------------- /test/pubodom.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | hack to publish some 0 odometry 4 | ''' 5 | 6 | # Python 7 | import sys 8 | from math import * 9 | import tf 10 | 11 | # ROS 12 | import rospy 13 | from nav_msgs.msg import Odometry 14 | 15 | def clamp(val): 16 | while val < -pi/2.0: 17 | val += 2.0*pi 18 | while val > pi/2.0: 19 | val -= 2.0*pi 20 | return val 21 | 22 | def talker(): 23 | pub = rospy.Publisher('/odometry/nav', Odometry, queue_size=10) 24 | rospy.init_node('odomtester', anonymous=True) 25 | rate = rospy.Rate(10) # 10hz 26 | lat = 36.6137 + 0.00001 27 | dlat = 0.0 #0.000001 28 | lon = -121.912 + 0.00001 29 | dlon = 0.0 #0.000001 30 | 31 | roll = 0.0 32 | droll = 0.0 #0.01 33 | pitch = 0.0 34 | dpitch = 0.0 #0.01 35 | yaw = pi/4.0 36 | dyaw = 0.0 #0.03 37 | 38 | N = 0.0 39 | while not rospy.is_shutdown(): 40 | omsg = Odometry() 41 | omsg.header.seq += 1 42 | omsg.header.stamp = rospy.get_rostime() 43 | omsg.header.frame_id = 'frame' 44 | omsg.pose.pose.position.x = lon+N*dlon 45 | omsg.pose.pose.position.y = lat+N*dlat 46 | omsg.pose.pose.position.z = 0.0 #1.0 47 | 48 | r = clamp(roll+N*droll) 49 | p = clamp(pitch+N*dpitch) 50 | y = clamp(yaw+N*dyaw) 51 | q = tf.transformations.quaternion_from_euler(r,p,y) 52 | omsg.pose.pose.orientation.x = q[0] 53 | omsg.pose.pose.orientation.y = q[1] 54 | omsg.pose.pose.orientation.z = q[2] 55 | omsg.pose.pose.orientation.w = q[3] 56 | 57 | omsg.pose.covariance = range(36) 58 | 59 | omsg.twist.twist.linear.x=1.0 60 | omsg.twist.twist.linear.y=2.0 61 | omsg.twist.twist.linear.z=3.0 62 | omsg.twist.twist.angular.x=4.0 63 | omsg.twist.twist.angular.y=5.0 64 | omsg.twist.twist.angular.z=6.0 65 | 66 | omsg.twist.covariance = range(36,36+36) 67 | 68 | rospy.loginfo("publishing odom (%.10f, %.10f, %.2f)"%(omsg.pose.pose.position.y,omsg.pose.pose.position.x,omsg.pose.pose.position.z)) 69 | pub.publish(omsg) 70 | N += 1.0 71 | rate.sleep() 72 | 73 | 74 | if __name__ == '__main__': 75 | try: 76 | talker() 77 | except rospy.ROSInterruptException: 78 | pass 79 | --------------------------------------------------------------------------------