Skip to content

Commit dda2905

Browse files
mdrintoatwilso
authored andcommitted
normalize and cross_product add to point arithmetic
Add Point Arithmetic test
1 parent 714bf41 commit dda2905

13 files changed

+1784
-777
lines changed

.clang-format

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ IndentCaseLabels: true
9292
IndentCaseBlocks: false
9393
IndentGotoLabels: true
9494
IndentPPDirectives: None
95-
IndentWidth: 4
95+
IndentWidth: 2
9696
IndentWrappedFunctionNames: false
9797
InsertTrailingCommas: None
9898
JavaScriptQuotes: Leave

.clang-tidy

Lines changed: 216 additions & 216 deletions
Large diffs are not rendered by default.

tracktable/Analysis/CMakeLists.txt

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,18 @@
3131

3232
# Tracktable C++ Analysis Library
3333

34-
# So far this is a header-only implementation. The lack of libraries
35-
# being built is not an error.
34+
include(GenerateExportHeader)
3635

3736
include_directories(
3837
${Tracktable_SOURCE_DIR}
38+
${PROJECT_BINARY_DIR}
3939
${Boost_INCLUDE_DIR}
4040
)
4141

42+
set(Analysis_SOURCES
43+
GreatCircleFit.cpp
44+
)
45+
4246
set( Analysis_HEADERS
4347
AssembleTrajectories.h
4448
ComputeDBSCANClustering.h
@@ -47,7 +51,7 @@ set( Analysis_HEADERS
4751
GuardedBoostGeometryRTreeHeader.h
4852
)
4953

50-
set( Detail_HEADERS
54+
set( Analysis_Detail_HEADERS
5155
detail/dbscan_points.h
5256
detail/point_converter.h
5357
detail/AssembleTrajectoriesIterator.h
@@ -60,25 +64,60 @@ set( Detail_HEADERS
6064

6165
#this adds the project to Visual Studio on Windows so the files are
6266
# visible without creating a build command
63-
add_custom_target( TracktableAnalysis
64-
SOURCES
67+
add_library( TracktableAnalysis
68+
${Analysis_SOURCES}
6569
${Analysis_HEADERS}
66-
${Detail_HEADERS}
70+
${Analysis_Detail_HEADERS}
71+
)
72+
73+
target_link_libraries(TracktableAnalysis
74+
TracktableDomain
75+
TracktableCore
6776
)
6877

78+
set_property(
79+
TARGET TracktableAnalysis
80+
PROPERTY SOVERSION ${SO_VERSION}
81+
)
82+
83+
set_property(
84+
TARGET TracktableAnalysis
85+
PROPERTY VERSION ${TRACKTABLE_VERSION}
86+
)
87+
6988
#This puts the header files under a separate folder in Visual Studio
70-
source_group ("Header Files\\Detail" FILES ${Detail_HEADERS})
89+
source_group ("Header Files\\Detail" FILES ${Analysis_Detail_HEADERS})
90+
91+
92+
generate_export_header( TracktableAnalysis
93+
BASE_NAME TRACKTABLE_ANALYSIS
94+
EXPORT_MACRO_NAME TRACKTABLE_ANALYSIS_EXPORT
95+
STATIC_DEFINE TRACKTABLE_ANALYSIS_STATIC_LIBRARY
96+
EXPORT_FILE_NAME ${PROJECT_BINARY_DIR}/tracktable/Analysis/TracktableAnalysisWindowsHeader.h
97+
)
98+
99+
if (APPLE)
100+
set(CMAKE_INSTALL_BINDIR bin)
101+
set(CMAKE_INSTALL_LIBDIR lib)
102+
endif ()
71103

72104
if (BUILD_TESTING)
73105
add_subdirectory(Tests)
74106
endif (BUILD_TESTING)
75107

108+
install(
109+
TARGETS TracktableAnalysis
110+
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
111+
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
112+
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
113+
)
114+
76115
install(
77116
FILES ${Analysis_HEADERS}
78117
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${TRACKTABLE_INCLUDEDIR}/Analysis
79118
)
80119

81120
install(
82-
FILES ${Detail_HEADERS}
121+
FILES ${Analysis_Detail_HEADERS}
83122
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${TRACKTABLE_INCLUDEDIR}/Analysis/detail
84123
)
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
/* Copyright (c) 2014-2020 National Technology and Engineering
2+
* Solutions of Sandia, LLC. Under the terms of Contract DE-NA0003525
3+
* with National Technology and Engineering Solutions of Sandia, LLC,
4+
* the U.S. Government retains certain rights in this software.
5+
*
6+
* Redistribution and use in source and binary forms, with or without
7+
* modification, are permitted provided that the following conditions
8+
* are met:
9+
*
10+
* 1. Redistributions of source code must retain the above copyright
11+
* notice, this list of conditions and the following disclaimer.
12+
*
13+
* 2. Redistributions in binary form must reproduce the above copyright
14+
* notice, this list of conditions and the following disclaimer in the
15+
* documentation and/or other materials provided with the distribution.
16+
*
17+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21+
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28+
*/
29+
30+
#include "GreatCircleFit.h"
31+
32+
#include <tracktable/Domain/Terrestrial.h>
33+
34+
// TODO: template/sfinae for double/float
35+
double constexpr sqrt_recursion(double _x, double _curr, double _prev) {
36+
return _curr == _prev ? _curr : sqrt_recursion(_x, 0.5 * (_curr + _x / _curr), _curr);
37+
}
38+
39+
/** @brief constexpr sqrt because sqrt are the spot to optimize and standard library lacks
40+
* it for some reason
41+
* @note Ternary(?) used because c++14 does not have constexpr_if
42+
* @param x The number to take the square root of
43+
* @return double constexpr
44+
*/
45+
double constexpr constsqrt(double _x) {
46+
return _x >= 0 && _x < std::numeric_limits<double>::infinity() ? sqrt_recursion(_x, _x, 0)
47+
: std::numeric_limits<double>::quiet_NaN();
48+
}
49+
50+
/* optimization function */
51+
double opt_fun(const Point3dT &_p, const TrajectoryT &_trajectory, std::string _altitudeString,
52+
tracktable::domain::terrestrial::AltitudeUnits _unit);
53+
Point3dT add_scaled_vector(const Point3dT &_v0, const Point3dT &_v1, double _fac);
54+
void project_point_onto_plane_in_place(TrajectoryPointT &_tPoint, const Point3dT &_normal,
55+
std::string _altitudeString,
56+
tracktable::domain::terrestrial::AltitudeUnits _unit);
57+
58+
namespace tracktable {
59+
namespace domain {
60+
namespace terrestrial {
61+
62+
void great_circle_fit_and_project_in_place(TrajectoryT &_trajectory, std::string _altitudeString,
63+
AltitudeUnits _unit) {
64+
Point3dT normal = find_best_fit_plane(_trajectory);
65+
project_trajectory_onto_plane(_trajectory, normal, _altitudeString, _unit);
66+
}
67+
68+
TrajectoryT great_circle_fit_and_project(TrajectoryT const &_trajectory, std::string _altitudeString,
69+
AltitudeUnits _unit) {
70+
TrajectoryT result(_trajectory);
71+
great_circle_fit_and_project_in_place(result, _altitudeString, _unit);
72+
return result;
73+
}
74+
75+
// TODO: Does not work well with trajectories with poor aspect ratio, should work direction of travel into it
76+
Point3dT find_best_fit_plane(const TrajectoryT &_trajectory, std::string _altitudeString,
77+
AltitudeUnits _unit) {
78+
if (_trajectory.size() < 2) {
79+
throw TooFewPoints();
80+
}
81+
// First guess at the perpendicular to the plane! There are a couple of
82+
// ways to do this, but this is the easiest.
83+
// First, we need to ensure we have two different points
84+
auto p = _trajectory.begin();
85+
auto v2 = _trajectory.back().ECEF(_altitudeString, _unit);
86+
auto v1 = (*p).ECEF(_altitudeString, _unit);
87+
while (std::next(p) != _trajectory.end() && v1 == v2) {
88+
v1 = (*(++p)).ECEF(_altitudeString, _unit);
89+
}
90+
if (v1 == v2) {
91+
throw IdenticalPositions();
92+
}
93+
94+
// Then we can use them to make a first guess
95+
auto normal = tracktable::arithmetic::normalize(tracktable::arithmetic::cross_product(v1, v2));
96+
97+
// Using our initial guess, see our optimization value. We are trying
98+
// to minimize this.
99+
double minSum = opt_fun(normal, _trajectory, _altitudeString, _unit);
100+
101+
// Tools for our optimization routine. The first two give us a way to
102+
// find a neighborhood of points, the second is or control over how
103+
// much we want to optimize
104+
105+
constexpr auto sqrt2 = constsqrt(2.0);
106+
constexpr auto numDirections = 8u;
107+
constexpr std::array<double,8> cyc= {0.0, sqrt2, 1.0, sqrt2, 0.0, -sqrt2, -1.0, -sqrt2};
108+
constexpr double eps = 5.0e-8;
109+
// TODO: implement acceleration/deceleration of epsilon
110+
111+
auto changed = false;
112+
do {
113+
changed = false;
114+
Point3dT curPoint = normal;
115+
116+
// Get some perpendiculars to our point on the sphere so we can walk
117+
// around it in a systematic way.
118+
v2 = tracktable::arithmetic::cross_product(normal,
119+
v1); // TODO: maybe point this at the point of greatest error
120+
v1 = tracktable::arithmetic::cross_product(normal, v2);
121+
122+
// Find the value of the optimization function in points around us.
123+
// We are done we all of the values are larger.
124+
for (auto i = 0u; i < numDirections; ++i) {
125+
auto temp = curPoint;
126+
temp = add_scaled_vector(temp, v1, eps * cyc.at(i));
127+
temp = add_scaled_vector(temp, v2, eps * cyc.at((i + 2u) % numDirections));
128+
tracktable::arithmetic::normalize_in_place(temp);
129+
auto sum = opt_fun(temp, _trajectory, _altitudeString, _unit);
130+
if (sum < minSum) {
131+
normal = temp;
132+
minSum = sum;
133+
changed = true;
134+
}
135+
}
136+
} while (changed);
137+
return normal;
138+
}
139+
140+
void project_trajectory_onto_plane(TrajectoryT &_trajectory, const Point3dT &_normal,
141+
std::string _altitudeString, AltitudeUnits _unit) {
142+
if (_trajectory.empty()) {
143+
throw TooFewPoints();
144+
}
145+
if (0.0 == tracktable::arithmetic::norm_squared(_normal)) {
146+
throw ZeroNorm();
147+
}
148+
for (auto &p : _trajectory) {
149+
project_point_onto_plane_in_place(p, _normal, _altitudeString, _unit);
150+
}
151+
}
152+
153+
} // namespace terrestrial
154+
} // namespace domain
155+
} // namespace tracktable
156+
157+
double opt_fun(const Point3dT &_p, const TrajectoryT &_trajectory, std::string _altitudeString,
158+
tracktable::domain::terrestrial::AltitudeUnits _unit) {
159+
auto sum = 0.0;
160+
for (const auto &p : _trajectory) {
161+
auto val =
162+
tracktable::arithmetic::dot(_p, tracktable::arithmetic::normalize(p.ECEF(_altitudeString, _unit)));
163+
// val += val * val * val / 6.0; // TODO: Ask Rintoul to confirm the +=
164+
sum += std::abs(val); // * val;
165+
}
166+
return sum;
167+
}
168+
169+
Point3dT add_scaled_vector(const Point3dT &_v0, const Point3dT &_v1, double _fac) {
170+
return tracktable::arithmetic::add(_v0, tracktable::arithmetic::multiply_scalar(_v1, _fac));
171+
}
172+
173+
// TODO: create a fromECEF() function or Trajectory point constructor that does the conversion
174+
void project_point_onto_plane_in_place(TrajectoryPointT &_tPoint, const Point3dT &_normal,
175+
std::string _altitudeString,
176+
tracktable::domain::terrestrial::AltitudeUnits _unit) {
177+
// An elegant way to project points. Basically, most of these points
178+
// are very close to the plane. So if you find the dot product between
179+
// the trajectory points and the perpendicular, that will be a tiny
180+
// number that essentially represents the amount you have to move the
181+
// point to get it to the plane. There are some small angle approximations
182+
// happening here, but to second order, it's all good, and to third order,
183+
// you are running out of digits on your double.
184+
constexpr double a = 6378.137;
185+
constexpr double e2 = 8.1819190842622e-2 * 8.1819190842622e-2;
186+
constexpr double a2 = a * a;
187+
constexpr double b2 = a2 * (1.0 - e2);
188+
constexpr double b = constsqrt(b2);
189+
constexpr double ep2 = (a2 - b2) / b2;
190+
191+
auto pt = _tPoint.ECEF(_altitudeString, _unit);
192+
pt = add_scaled_vector(pt, _normal, -1.0 * tracktable::arithmetic::dot(pt, _normal));
193+
194+
auto p = std::sqrt(pt[0] * pt[0] + pt[1] * pt[1]);
195+
auto th = std::atan2(a * pt[2], b * p);
196+
auto sinTh = std::sin(th);
197+
auto cosTh = std::cos(th);
198+
199+
auto lon = atan2(pt[1], pt[0]);
200+
auto lat = atan2(pt[2] + ep2 * b * sinTh * sinTh * sinTh, p - e2 * a * cosTh * cosTh * cosTh);
201+
202+
_tPoint.set_longitude(tracktable::conversions::degrees(lon));
203+
_tPoint.set_latitude(tracktable::conversions::degrees(lat));
204+
205+
// auto sin_lat = std::sin(lat);
206+
// auto N = a / std::sqrt(1.0 - e2 * sin_lat * sin_lat);
207+
// double alt = p / cos(lat) - N;
208+
//_tPoint.set_property(_altitudeString,alt);
209+
}

0 commit comments

Comments
 (0)