mirror of
https://github.com/JHUAPL/Terrasaur.git
synced 2026-01-06 19:43:53 -05:00
updates for v1.1.0
This commit is contained in:
15
CHANGELOG.md
15
CHANGELOG.md
@@ -1,5 +1,20 @@
|
||||
# Terrasaur Changelog
|
||||
|
||||
## July 30, 2025 - v1.1.0
|
||||
|
||||
- Updates to existing tools
|
||||
- AdjustShapeModelToOtherShapeModel
|
||||
- fix intersection bug
|
||||
- CreateSBMTStructure
|
||||
- new options: -flipX, -flipY, -spice, -date, -observer, -target, -cameraFrame
|
||||
- ValidateNormals
|
||||
- new option: -fast to only check for overhangs if center and normal point in opposite directions
|
||||
|
||||
- New tools:
|
||||
- FacetInfo: Print info about a facet
|
||||
- PointCloudOverlap: Find points in a point cloud which overlap a reference point cloud
|
||||
- TriAx: Generate a triaxial ellipsoid in ICQ format
|
||||
|
||||
## April 28, 2025 - v1.0.1
|
||||
|
||||
- Add MIT license to repository and source code
|
||||
|
||||
BIN
doc/dist/Terrasaur-2025.03.03-e1a0e14-src.tar.gz
vendored
BIN
doc/dist/Terrasaur-2025.03.03-e1a0e14-src.tar.gz
vendored
Binary file not shown.
BIN
doc/dist/Terrasaur-2025.03.03-e1a0e14_*.tar.gz
vendored
BIN
doc/dist/Terrasaur-2025.03.03-e1a0e14_*.tar.gz
vendored
Binary file not shown.
@@ -16,7 +16,7 @@ The Terrasaur package requires Java 21 or later. Some freely available versions
|
||||
Download
|
||||
~~~~~~~~
|
||||
|
||||
Binary packages for use on Mac OS X and Linux are available at ...
|
||||
Binary packages for use on Mac OS X and Linux are available at `GitHub <https://github.com/JHUAPL/Terrasaur/releases>`__.
|
||||
|
||||
We have not tried using the softare on Microsoft Windows, but users may try the Linux package with the `Windows Subsystem for Linux <https://docs.microsoft.com/en-us/windows/wsl/>`__.
|
||||
|
||||
|
||||
70
doc/make.bat
70
doc/make.bat
@@ -1,35 +1,35 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
||||
|
||||
70
doc/tools/ColorSpots.rst
Normal file
70
doc/tools/ColorSpots.rst
Normal file
@@ -0,0 +1,70 @@
|
||||
.. _ColorSpots:
|
||||
|
||||
##########
|
||||
ColorSpots
|
||||
##########
|
||||
|
||||
ColorSpots takes as input a shape model and a file containing (x, y, z, value),
|
||||
or (lat, lon, value). It writes out the mean and standard deviation of values
|
||||
within a specified range for each facet.
|
||||
|
||||
.. include:: ../toolDescriptions/ColorSpots.txt
|
||||
:literal:
|
||||
|
||||
********
|
||||
Examples
|
||||
********
|
||||
|
||||
Download the :download:`Apophis<./support_files/apophis_g_15618mm_rdr_obj_0000n00000_v001.obj>`
|
||||
shape model and the :download:`info<./support_files/xyzrandom.txt>` file containing
|
||||
cartesian coordinates and a random value.
|
||||
|
||||
Run ColorSpots:
|
||||
|
||||
::
|
||||
|
||||
ColorSpots -obj apophis_g_15618mm_rdr_obj_0000n00000_v001.obj -xyz \
|
||||
-info xyzrandom.txt -outFile apophis_value_at_vertex.csv -noWeight \
|
||||
-allFacets -additionalFields n -searchRadius 0.015 -writeVertices
|
||||
|
||||
The first few lines of apophis_value_at_vertex.csv look like:
|
||||
|
||||
::
|
||||
|
||||
% head apophis_value_at_vertex.csv
|
||||
0.000000e+00, 0.000000e+00, 1.664960e-01, -3.805764e-02, 5.342315e-01, 4.000000e+01
|
||||
1.589500e-02, 0.000000e+00, 1.591030e-01, 6.122849e-02, 6.017192e-01, 5.000000e+01
|
||||
7.837000e-03, 1.486800e-02, 1.591670e-01, -6.072964e-03, 5.220682e-01, 5.700000e+01
|
||||
-7.747000e-03, 1.506300e-02, 1.621040e-01, 9.146163e-02, 5.488631e-01, 4.900000e+01
|
||||
-1.554900e-02, 0.000000e+00, 1.657970e-01, -8.172811e-03, 5.270302e-01, 3.400000e+01
|
||||
-7.982000e-03, -1.571100e-02, 1.694510e-01, -2.840524e-02, 5.045911e-01, 3.900000e+01
|
||||
8.060000e-03, -1.543300e-02, 1.655150e-01, 3.531959e-02, 5.464390e-01, 4.900000e+01
|
||||
3.179500e-02, 0.000000e+00, 1.515820e-01, -1.472434e-02, 5.967265e-01, 5.400000e+01
|
||||
2.719700e-02, 1.658200e-02, 1.508930e-01, -9.050683e-03, 5.186966e-01, 4.700000e+01
|
||||
1.554100e-02, 2.901300e-02, 1.530770e-01, -7.053547e-02, 4.980369e-01, 7.000000e+01
|
||||
|
||||
The columns are:
|
||||
|
||||
.. list-table:: ColorSpots Vertex output
|
||||
:header-rows: 1
|
||||
|
||||
* - Column
|
||||
- Value
|
||||
* - 1
|
||||
- X
|
||||
* - 2
|
||||
- Y
|
||||
* - 3
|
||||
- Z
|
||||
* - 4
|
||||
- mean value in region
|
||||
* - 5
|
||||
- standard deviation in region
|
||||
* - 6
|
||||
- number of points in region
|
||||
|
||||
.. figure:: images/ColorSpots-n.png
|
||||
:alt: Number of points in region at each vertex
|
||||
|
||||
This image shows the number of points in the region at each vertex.
|
||||
|
||||
@@ -23,7 +23,7 @@ Local Model Comparison
|
||||
|
||||
Download the :download:`reference<./support_files/EVAL20_wtr.obj>` and :download:`comparison<./support_files/EVAL20.obj>`
|
||||
shape models. You can view them in a tool such as
|
||||
`ParaView<https://www.paraview.org/>`.
|
||||
`ParaView <https://www.paraview.org/>`__.
|
||||
|
||||
.. figure:: images/CompareOBJ_local_1.png
|
||||
|
||||
@@ -32,8 +32,8 @@ shape models. You can view them in a tool such as
|
||||
Run CompareOBJ to find the optimal transform to align the comparison with the reference:
|
||||
::
|
||||
|
||||
CompareOBJ -computeOptimalRotationAndTranslation -model F3H-1/EVAL20.obj \
|
||||
-reference F3H-1/EVAL20_wtr.obj -computeVerticalError verticalError.txt \
|
||||
CompareOBJ -computeOptimalRotationAndTranslation -model EVAL20.obj \
|
||||
-reference EVAL20_wtr.obj -computeVerticalError verticalError.txt \
|
||||
-saveOptimalShape optimal.obj -savePlateDiff plateDiff.txt -savePlateIndex plateIndex.txt
|
||||
|
||||
The screen output is
|
||||
@@ -77,7 +77,7 @@ model for comparison:
|
||||
|
||||
::
|
||||
|
||||
ShapeFormatConverter -input Bennu/Bennu49k.obj -output BennuComparison.obj \
|
||||
ShapeFormatConverter -input Bennu49k.obj -output BennuComparison.obj \
|
||||
-rotate 5,0,0,1 -translate 0.01,-0.01,0.01
|
||||
|
||||
This rotates the shape model by 5 degrees about the z axis and then translates
|
||||
@@ -94,11 +94,11 @@ Run CompareOBJ to find the optimal transform to align the comparison with the re
|
||||
|
||||
CompareOBJ -computeOptimalRotationAndTranslation \
|
||||
-model BennuComparison.obj \
|
||||
-reference Bennu/Bennu49k.obj \
|
||||
-computeVerticalError CompareOBJ/terrasaur-verticalError.txt \
|
||||
-saveOptimalShape CompareOBJ/terrasaur-optimal.obj \
|
||||
-savePlateDiff CompareOBJ/terrasaur-plateDiff.txt \
|
||||
-savePlateIndex CompareOBJ/terrasaur-plateIndex.txt
|
||||
-reference Bennu49k.obj \
|
||||
-computeVerticalError terrasaur-verticalError.txt \
|
||||
-saveOptimalShape terrasaur-optimal.obj \
|
||||
-savePlateDiff terrasaur-plateDiff.txt \
|
||||
-savePlateIndex terrasaur-plateIndex.txt
|
||||
|
||||
The screen output is
|
||||
|
||||
|
||||
38
doc/tools/PointCloudOverlap.rst
Normal file
38
doc/tools/PointCloudOverlap.rst
Normal file
@@ -0,0 +1,38 @@
|
||||
.. _PointCloudOverlap:
|
||||
|
||||
#################
|
||||
PointCloudOverlap
|
||||
#################
|
||||
|
||||
*****
|
||||
Usage
|
||||
*****
|
||||
|
||||
.. include:: ../toolDescriptions/PointCloudOverlap.txt
|
||||
:literal:
|
||||
|
||||
********
|
||||
Examples
|
||||
********
|
||||
|
||||
Download the :download:`reference<./support_files/EVAL20_wtr.obj>` and :download:`comparison<./support_files/EVAL20.obj>`
|
||||
shape models. You can view them in a tool such as
|
||||
`ParaView <https://www.paraview.org/>`__.
|
||||
|
||||
.. figure:: images/PointCloudOverlap_1.png
|
||||
|
||||
This image shows the reference (pink) and input (blue) shape models.
|
||||
|
||||
Run PointCloudOverlap:
|
||||
|
||||
::
|
||||
|
||||
PointCloudOverlap -inputFile EVAL20.obj -referenceFile EVAL20_wtr.obj -outputFile overlap.vtk
|
||||
|
||||
Note that OBJ is supported as an input format but not as an output format.
|
||||
|
||||
|
||||
.. figure:: images/PointCloudOverlap_2.png
|
||||
|
||||
The points in white are those in the input model that overlap the reference.
|
||||
|
||||
43
doc/tools/TriAx.rst
Normal file
43
doc/tools/TriAx.rst
Normal file
@@ -0,0 +1,43 @@
|
||||
.. _TriAx:
|
||||
|
||||
=====
|
||||
TriAx
|
||||
=====
|
||||
|
||||
TriAx is an implementation of the SPC tool TRIAX, which generates a triaxial ellipsoid in ICQ format.
|
||||
|
||||
*****
|
||||
Usage
|
||||
*****
|
||||
|
||||
.. include:: ../toolDescriptions/TriAx.txt
|
||||
:literal:
|
||||
|
||||
*******
|
||||
Example
|
||||
*******
|
||||
|
||||
Generate an ellipsoid with dimensions 10, 8, 6, with q = 8.
|
||||
|
||||
::
|
||||
|
||||
TriAx -A 10 -B 8 -C 6 -Q 8 -output triax.icq -saveOBJ
|
||||
|
||||
The following ellipsoid is generated:
|
||||
|
||||
.. container:: figures-row
|
||||
|
||||
.. figure:: images/TriAx_X.png
|
||||
:alt: looking down from the +X direction
|
||||
|
||||
looking down from the +X direction
|
||||
|
||||
.. figure:: images/TriAx_Y.png
|
||||
:alt: looking down from the +Y direction
|
||||
|
||||
looking down from the +Y direction
|
||||
|
||||
.. figure:: images/TriAx_Z.png
|
||||
:alt: looking down from the +Z direction
|
||||
|
||||
looking down from the +Z direction
|
||||
BIN
doc/tools/images/PointCloudOverlap_1.png
Normal file
BIN
doc/tools/images/PointCloudOverlap_1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 174 KiB |
BIN
doc/tools/images/PointCloudOverlap_2.png
Normal file
BIN
doc/tools/images/PointCloudOverlap_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 139 KiB |
BIN
doc/tools/images/TriAx_X.png
Normal file
BIN
doc/tools/images/TriAx_X.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
BIN
doc/tools/images/TriAx_Y.png
Normal file
BIN
doc/tools/images/TriAx_Y.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
doc/tools/images/TriAx_Z.png
Normal file
BIN
doc/tools/images/TriAx_Z.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
@@ -89,7 +89,7 @@ function build_jar() {
|
||||
|
||||
function make_scripts() {
|
||||
|
||||
classes=$(jar tf "${scriptPath}"/target/${packageName}.jar | grep $appSrcDir | grep -v '\$' | grep -v "package-info" | grep class)
|
||||
classes=$(jar tf ${scriptPath}/target/${packageName}.jar | grep $appSrcDir | grep -v '\$' | grep -v "package-info" | grep -v "Immutable" | grep class)
|
||||
|
||||
for class in $classes; do
|
||||
base=$(basename "$class" ".class")
|
||||
|
||||
48
pom.xml
48
pom.xml
@@ -1,5 +1,6 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>edu.jhuapl.ses.srn</groupId>
|
||||
<artifactId>Terrasaur</artifactId>
|
||||
@@ -55,25 +56,29 @@
|
||||
<groupId>com.mycila</groupId>
|
||||
<artifactId>license-maven-plugin</artifactId>
|
||||
<version>5.0.0</version>
|
||||
<configuration>
|
||||
<licenseSets>
|
||||
<licenseSet>
|
||||
<header>com/mycila/maven/plugin/license/templates/MIT.txt</header>
|
||||
<includes>
|
||||
<include>**/*.java</include>
|
||||
</includes>
|
||||
</licenseSet>
|
||||
</licenseSets>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>check-license</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<licenseSets>
|
||||
<licenseSet>
|
||||
<header>com/mycila/maven/plugin/license/templates/MIT.txt</header>
|
||||
<includes>
|
||||
<include>**/*.java</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>3rd-party/**/*.java</exclude>
|
||||
<exclude>support-libraries/3rd-party/**/*.java</exclude>
|
||||
</excludes>
|
||||
</licenseSet>
|
||||
</licenseSets>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>check-license</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
@@ -126,7 +131,8 @@
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.5.3</version>
|
||||
<configuration>
|
||||
<argLine> -Djava.library.path=${project.basedir}/3rd-party/${env.ARCH}/spice/JNISpice/lib
|
||||
<argLine>
|
||||
-Djava.library.path=${project.basedir}/3rd-party/${env.ARCH}/spice/JNISpice/lib
|
||||
</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
@@ -25,7 +25,6 @@ package terrasaur.apps;
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.Option;
|
||||
import org.apache.commons.cli.Options;
|
||||
@@ -33,14 +32,14 @@ import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import spice.basic.Plane;
|
||||
import spice.basic.Vector3;
|
||||
import terrasaur.smallBodyModel.BoundingBox;
|
||||
import terrasaur.smallBodyModel.SmallBodyModel;
|
||||
import terrasaur.templates.TerrasaurTool;
|
||||
import terrasaur.utils.Log4j2Configurator;
|
||||
import terrasaur.utils.NativeLibraryLoader;
|
||||
import terrasaur.utils.PolyDataUtil;
|
||||
import spice.basic.Plane;
|
||||
import spice.basic.Vector3;
|
||||
import vtk.vtkGenericCell;
|
||||
import vtk.vtkPoints;
|
||||
import vtk.vtkPolyData;
|
||||
@@ -76,7 +75,7 @@ public class AdjustShapeModelToOtherShapeModel implements TerrasaurTool {
|
||||
return TerrasaurTool.super.fullDescription(options, header, "");
|
||||
}
|
||||
|
||||
private static Options defineOptions() {
|
||||
private static Options defineOptions() {
|
||||
Options options = TerrasaurTool.defineOptions();
|
||||
options.addOption(
|
||||
Option.builder("from")
|
||||
@@ -182,6 +181,7 @@ public class AdjustShapeModelToOtherShapeModel implements TerrasaurTool {
|
||||
Vector3D origin = new Vector3D(0., 0., 0.);
|
||||
for (int i = 0; i < numberPoints; ++i) {
|
||||
points.GetPoint(i, p);
|
||||
Vector3D thisPoint = new Vector3D(p);
|
||||
|
||||
Vector3D lookDir;
|
||||
|
||||
@@ -198,18 +198,20 @@ public class AdjustShapeModelToOtherShapeModel implements TerrasaurTool {
|
||||
}
|
||||
|
||||
Vector3D lookPt = lookDir.scalarMultiply(diagonalLength);
|
||||
lookPt = lookPt.add(origin);
|
||||
lookPt = lookPt.add(thisPoint);
|
||||
|
||||
List<Vector3D> intersections = new ArrayList<>();
|
||||
for (vtksbCellLocator cellLocator : cellLocators) {
|
||||
double[] intersectPoint = new double[3];
|
||||
|
||||
// trace ray from the lookPt to the origin - first intersection is the farthest intersection
|
||||
// from the origin
|
||||
// trace ray from thisPoint to the lookPt - Assume cell intersection is the closest one if
|
||||
// there are multiple?
|
||||
// NOTE: result should return 1 in case of intersection but doesn't sometimes.
|
||||
// Use the norm of intersection point to test for intersection instead.
|
||||
int result =
|
||||
cellLocator.IntersectWithLine(
|
||||
thisPoint.toArray(),
|
||||
lookPt.toArray(),
|
||||
origin.toArray(),
|
||||
tol,
|
||||
t,
|
||||
intersectPoint,
|
||||
@@ -219,38 +221,32 @@ public class AdjustShapeModelToOtherShapeModel implements TerrasaurTool {
|
||||
cell);
|
||||
Vector3D intersectVector = new Vector3D(intersectPoint);
|
||||
|
||||
if (fitPlane || localModel) {
|
||||
// NOTE: result should return 1 in case of intersection but doesn't sometimes.
|
||||
// Use the norm of intersection point to test for intersection instead.
|
||||
|
||||
NavigableMap<Double, Vector3D> pointsMap = new TreeMap<>();
|
||||
if (intersectVector.getNorm() > 0) {
|
||||
pointsMap.put(origin.subtract(intersectVector).getNorm(), intersectVector);
|
||||
}
|
||||
|
||||
lookPt = lookDir.scalarMultiply(-diagonalLength);
|
||||
lookPt = lookPt.add(origin);
|
||||
result =
|
||||
cellLocator.IntersectWithLine(
|
||||
lookPt.toArray(),
|
||||
origin.toArray(),
|
||||
tol,
|
||||
t,
|
||||
intersectPoint,
|
||||
pcoords,
|
||||
subId,
|
||||
cell_id,
|
||||
cell);
|
||||
|
||||
intersectVector = new Vector3D(intersectPoint);
|
||||
if (intersectVector.getNorm() > 0) {
|
||||
pointsMap.put(origin.subtract(intersectVector).getNorm(), intersectVector);
|
||||
}
|
||||
|
||||
if (!pointsMap.isEmpty()) intersections.add(pointsMap.get(pointsMap.firstKey()));
|
||||
} else {
|
||||
if (result > 0) intersections.add(intersectVector);
|
||||
NavigableMap<Double, Vector3D> pointsMap = new TreeMap<>();
|
||||
if (intersectVector.getNorm() > 0) {
|
||||
pointsMap.put(thisPoint.subtract(intersectVector).getNorm(), intersectVector);
|
||||
}
|
||||
|
||||
// look in the other direction
|
||||
lookPt = lookDir.scalarMultiply(-diagonalLength);
|
||||
lookPt = lookPt.add(thisPoint);
|
||||
result =
|
||||
cellLocator.IntersectWithLine(
|
||||
thisPoint.toArray(),
|
||||
lookPt.toArray(),
|
||||
tol,
|
||||
t,
|
||||
intersectPoint,
|
||||
pcoords,
|
||||
subId,
|
||||
cell_id,
|
||||
cell);
|
||||
|
||||
intersectVector = new Vector3D(intersectPoint);
|
||||
if (intersectVector.getNorm() > 0) {
|
||||
pointsMap.put(thisPoint.subtract(intersectVector).getNorm(), intersectVector);
|
||||
}
|
||||
|
||||
if (!pointsMap.isEmpty()) intersections.add(pointsMap.get(pointsMap.firstKey()));
|
||||
}
|
||||
|
||||
if (intersections.isEmpty()) throw new Exception("Error: no intersections at all");
|
||||
|
||||
@@ -60,8 +60,7 @@ public class CreateSBMTStructure implements TerrasaurTool {
|
||||
|
||||
@Override
|
||||
public String fullDescription(Options options) {
|
||||
String header =
|
||||
"This tool creates an SBMT ellipse file from a set of point on an image.";
|
||||
String header = "This tool creates an SBMT ellipse file from a set of points on an image.";
|
||||
String footer = "";
|
||||
return TerrasaurTool.super.fullDescription(options, header, footer);
|
||||
}
|
||||
@@ -129,20 +128,31 @@ public class CreateSBMTStructure implements TerrasaurTool {
|
||||
|
||||
private static Options defineOptions() {
|
||||
Options options = TerrasaurTool.defineOptions();
|
||||
options.addOption(
|
||||
Option.builder("flipX")
|
||||
.desc("If present, negate the X coordinate of the input points.")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("flipY")
|
||||
.desc("If present, negate the Y coordinate of the input points.")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("input")
|
||||
.required()
|
||||
.hasArg()
|
||||
.desc(
|
||||
"""
|
||||
Required. Name or input file. This is a text file with a pair of pixel coordinates per line. The pixel
|
||||
"""
|
||||
Required. Name or input file. This is a text file with a pair of pixel coordinates (X and Y) per line. The pixel
|
||||
coordinates are offsets from the image center. For example:
|
||||
|
||||
# My test file
|
||||
|
||||
627.51274 876.11775
|
||||
630.53612 883.55992
|
||||
626.3499 881.46681
|
||||
89.6628 285.01
|
||||
97.8027 280.126
|
||||
95.0119 285.01
|
||||
-13.8299 323.616
|
||||
-1.9689 331.756
|
||||
-11.7367 330.826
|
||||
|
||||
Empty lines or lines beginning with # are ignored.
|
||||
|
||||
@@ -161,11 +171,38 @@ axis and the third is a location for the semi-minor axis.""")
|
||||
.hasArg()
|
||||
.desc("Required. Name of output file.")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("spice")
|
||||
.hasArg()
|
||||
.desc(
|
||||
"If present, name of metakernel to read. Other required options with -spice are -date, -observer, -target, and -cameraFrame.")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("date")
|
||||
.hasArgs()
|
||||
.desc("Only used with -spice. Date of image (e.g. 2022 SEP 26 23:11:12.649).")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("observer")
|
||||
.hasArg()
|
||||
.desc("Only used with -spice. Observing body (e.g. DART)")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("target")
|
||||
.hasArg()
|
||||
.desc("Only used with -spice. Target body (e.g. DIMORPHOS).")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("cameraFrame")
|
||||
.hasArg()
|
||||
.desc("Only used with -spice. Camera frame (e.g. DART_DRACO).")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("sumFile")
|
||||
.required()
|
||||
.hasArg()
|
||||
.desc("Required. Name of sum file to read.")
|
||||
.desc(
|
||||
"Required. Name of sum file to read. This is still required with -spice, but only used as a template to create a new sum file.")
|
||||
.build());
|
||||
return options;
|
||||
}
|
||||
@@ -179,7 +216,7 @@ axis and the third is a location for the semi-minor axis.""")
|
||||
|
||||
Map<MessageLabel, String> startupMessages = defaultOBJ.startupMessages(cl);
|
||||
for (MessageLabel ml : startupMessages.keySet())
|
||||
logger.info(String.format("%s %s", ml.label, startupMessages.get(ml)));
|
||||
logger.info("{} {}", ml.label, startupMessages.get(ml));
|
||||
|
||||
NativeLibraryLoader.loadSpiceLibraries();
|
||||
NativeLibraryLoader.loadVtkLibraries();
|
||||
@@ -195,6 +232,8 @@ axis and the third is a location for the semi-minor axis.""")
|
||||
}
|
||||
RangeFromSumFile rfsf = new RangeFromSumFile(sumFile, polyData);
|
||||
|
||||
boolean flipX = cl.hasOption("flipX");
|
||||
boolean flipY = cl.hasOption("flipY");
|
||||
List<Vector3D> intercepts = new ArrayList<>();
|
||||
List<String> lines =
|
||||
FileUtils.readLines(new File(cl.getOptionValue("input")), Charset.defaultCharset());
|
||||
@@ -204,6 +243,9 @@ axis and the third is a location for the semi-minor axis.""")
|
||||
int ix = (int) Math.round(Double.parseDouble(parts[0]));
|
||||
int iy = (int) Math.round(Double.parseDouble(parts[1]));
|
||||
|
||||
if (flipX) ix *= -1;
|
||||
if (flipY) iy *= -1;
|
||||
|
||||
Map.Entry<Long, Vector3D> entry = rfsf.findIntercept(ix, iy);
|
||||
long cellID = entry.getKey();
|
||||
if (cellID > -1) intercepts.add(entry.getValue());
|
||||
@@ -216,12 +258,13 @@ axis and the third is a location for the semi-minor axis.""")
|
||||
|
||||
// p1 and p2 define the long axis of the ellipse
|
||||
Vector3D p1 = intercepts.get(i);
|
||||
Vector3D p2 = intercepts.get(i+1);
|
||||
Vector3D p2 = intercepts.get(i + 1);
|
||||
|
||||
// p3 lies on the short axis
|
||||
Vector3D p3 = intercepts.get(i+2);
|
||||
Vector3D p3 = intercepts.get(i + 2);
|
||||
|
||||
SBMTEllipseRecord record = createRecord(i/3, String.format("Ellipse %d", i/3), p1, p2, p3);
|
||||
SBMTEllipseRecord record =
|
||||
createRecord(i / 3, String.format("Ellipse %d", i / 3), p1, p2, p3);
|
||||
records.add(record);
|
||||
}
|
||||
|
||||
|
||||
258
src/main/java/terrasaur/apps/FacetInfo.java
Normal file
258
src/main/java/terrasaur/apps/FacetInfo.java
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2025 Johns Hopkins University Applied Physics Laboratory
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package terrasaur.apps;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.TreeSet;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.Option;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.math3.geometry.euclidean.threed.SphericalCoordinates;
|
||||
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.immutables.value.Value;
|
||||
import terrasaur.templates.TerrasaurTool;
|
||||
import terrasaur.utils.CellInfo;
|
||||
import terrasaur.utils.NativeLibraryLoader;
|
||||
import terrasaur.utils.PolyDataStatistics;
|
||||
import terrasaur.utils.PolyDataUtil;
|
||||
import vtk.vtkIdList;
|
||||
import vtk.vtkOBBTree;
|
||||
import vtk.vtkPolyData;
|
||||
|
||||
public class FacetInfo implements TerrasaurTool {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
|
||||
/**
|
||||
* This doesn't need to be private, or even declared, but you might want to if you have other
|
||||
* constructors.
|
||||
*/
|
||||
private FacetInfo() {}
|
||||
|
||||
@Override
|
||||
public String shortDescription() {
|
||||
return "Print info about a facet.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fullDescription(Options options) {
|
||||
String header = "Prints information about facet(s).";
|
||||
String footer =
|
||||
"""
|
||||
|
||||
This tool prints out facet center, normal, angle between center and
|
||||
normal, and other information about the specified facet(s).""";
|
||||
|
||||
return TerrasaurTool.super.fullDescription(options, header, footer);
|
||||
}
|
||||
|
||||
private vtkPolyData polyData;
|
||||
private vtkOBBTree searchTree;
|
||||
private Vector3D origin;
|
||||
|
||||
private FacetInfo(vtkPolyData polyData) {
|
||||
this.polyData = polyData;
|
||||
PolyDataStatistics stats = new PolyDataStatistics(polyData);
|
||||
origin = new Vector3D(stats.getCentroid());
|
||||
|
||||
logger.info("Origin is at {}", origin);
|
||||
|
||||
logger.info("Creating search tree");
|
||||
searchTree = new vtkOBBTree();
|
||||
searchTree.SetDataSet(polyData);
|
||||
searchTree.SetTolerance(1e-12);
|
||||
searchTree.BuildLocator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cellId id of this cell
|
||||
* @return Set of neighboring cells (ones which share a vertex with this one)
|
||||
*/
|
||||
private NavigableSet<Long> neighbors(long cellId) {
|
||||
NavigableSet<Long> neighborCellIds = new TreeSet<>();
|
||||
|
||||
vtkIdList vertexIdlist = new vtkIdList();
|
||||
CellInfo.getCellInfo(polyData, cellId, vertexIdlist);
|
||||
|
||||
vtkIdList facetIdlist = new vtkIdList();
|
||||
for (long i = 0; i < vertexIdlist.GetNumberOfIds(); i++) {
|
||||
long vertexId = vertexIdlist.GetId(i);
|
||||
polyData.GetPointCells(vertexId, facetIdlist);
|
||||
}
|
||||
for (long i = 0; i < facetIdlist.GetNumberOfIds(); i++) {
|
||||
long id = facetIdlist.GetId(i);
|
||||
if (id == cellId) continue;
|
||||
neighborCellIds.add(id);
|
||||
}
|
||||
|
||||
return neighborCellIds;
|
||||
}
|
||||
|
||||
@Value.Immutable
|
||||
public abstract static class FacetInfoLine {
|
||||
|
||||
public abstract long index();
|
||||
|
||||
public abstract Vector3D radius();
|
||||
|
||||
public abstract Vector3D normal();
|
||||
|
||||
/**
|
||||
* @return facets between this and origin
|
||||
*/
|
||||
public abstract NavigableSet<Long> interiorIntersections();
|
||||
|
||||
/**
|
||||
* @return facets between this and infinity
|
||||
*/
|
||||
public abstract NavigableSet<Long> exteriorIntersections();
|
||||
|
||||
public static String getHeader() {
|
||||
return "# Index, "
|
||||
+ "Center Lat (deg), "
|
||||
+ "Center Lon (deg), "
|
||||
+ "Radius, "
|
||||
+ "Radial Vector, "
|
||||
+ "Normal Vector, "
|
||||
+ "Angle between radial and normal (deg), "
|
||||
+ "facets between this and origin, "
|
||||
+ "facets between this and infinity";
|
||||
}
|
||||
|
||||
public String toCSV() {
|
||||
|
||||
SphericalCoordinates spc = new SphericalCoordinates(radius());
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(String.format("%d, ", index()));
|
||||
sb.append(String.format("%.4f, ", 90 - Math.toDegrees(spc.getPhi())));
|
||||
sb.append(String.format("%.4f, ", Math.toDegrees(spc.getTheta())));
|
||||
sb.append(String.format("%.6f, ", spc.getR()));
|
||||
sb.append(
|
||||
String.format("%.6f %.6f %.6f, ", radius().getX(), radius().getY(), radius().getZ()));
|
||||
sb.append(
|
||||
String.format("%.6f %.6f %.6f, ", normal().getX(), normal().getY(), normal().getZ()));
|
||||
sb.append(String.format("%.3f, ", Math.toDegrees(Vector3D.angle(radius(), normal()))));
|
||||
sb.append(String.format("%d", interiorIntersections().size()));
|
||||
if (!interiorIntersections().isEmpty()) {
|
||||
for (long id : interiorIntersections()) sb.append(String.format(" %d", id));
|
||||
}
|
||||
sb.append(", ");
|
||||
sb.append(String.format("%d", exteriorIntersections().size()));
|
||||
if (!exteriorIntersections().isEmpty()) {
|
||||
for (long id : exteriorIntersections()) sb.append(String.format(" %d", id));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private FacetInfoLine getFacetInfoLine(long cellId) {
|
||||
CellInfo ci = CellInfo.getCellInfo(polyData, cellId, new vtkIdList());
|
||||
|
||||
vtkIdList cellIds = new vtkIdList();
|
||||
searchTree.IntersectWithLine(origin.toArray(), ci.center().toArray(), null, cellIds);
|
||||
|
||||
// count up all crossings of the surface between the origin and the facet.
|
||||
NavigableSet<Long> insideIds = new TreeSet<>();
|
||||
for (long j = 0; j < cellIds.GetNumberOfIds(); j++) {
|
||||
if (cellIds.GetId(j) == cellId) continue;
|
||||
insideIds.add(cellIds.GetId(j));
|
||||
}
|
||||
|
||||
Vector3D infinity = ci.center().scalarMultiply(1e9);
|
||||
|
||||
cellIds = new vtkIdList();
|
||||
searchTree.IntersectWithLine(infinity.toArray(), ci.center().toArray(), null, cellIds);
|
||||
|
||||
// count up all crossings of the surface between the infinity and the facet.
|
||||
NavigableSet<Long> outsideIds = new TreeSet<>();
|
||||
for (long j = 0; j < cellIds.GetNumberOfIds(); j++) {
|
||||
if (cellIds.GetId(j) == cellId) continue;
|
||||
outsideIds.add(cellIds.GetId(j));
|
||||
}
|
||||
|
||||
return ImmutableFacetInfoLine.builder()
|
||||
.index(cellId)
|
||||
.radius(ci.center())
|
||||
.normal(ci.normal())
|
||||
.interiorIntersections(insideIds)
|
||||
.exteriorIntersections(outsideIds)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static Options defineOptions() {
|
||||
Options options = TerrasaurTool.defineOptions();
|
||||
options.addOption(
|
||||
Option.builder("facet")
|
||||
.required()
|
||||
.hasArgs()
|
||||
.desc("Facet(s) to query. Separate multiple indices with whitespace.")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("obj").required().hasArg().desc("Shape model to validate.").build());
|
||||
options.addOption(
|
||||
Option.builder("output").required().hasArg().desc("CSV file to write.").build());
|
||||
return options;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
TerrasaurTool defaultOBJ = new FacetInfo();
|
||||
|
||||
Options options = defineOptions();
|
||||
|
||||
CommandLine cl = defaultOBJ.parseArgs(args, options);
|
||||
|
||||
Map<MessageLabel, String> startupMessages = defaultOBJ.startupMessages(cl);
|
||||
for (MessageLabel ml : startupMessages.keySet())
|
||||
logger.info("{} {}", ml.label, startupMessages.get(ml));
|
||||
|
||||
NativeLibraryLoader.loadVtkLibraries();
|
||||
|
||||
try {
|
||||
vtkPolyData polydata = PolyDataUtil.loadShapeModel(cl.getOptionValue("obj"));
|
||||
FacetInfo app = new FacetInfo(polydata);
|
||||
try (PrintWriter pw = new PrintWriter(cl.getOptionValue("output"))) {
|
||||
pw.println(FacetInfoLine.getHeader());
|
||||
for (long cellId :
|
||||
Arrays.stream(cl.getOptionValues("facet")).mapToLong(Long::parseLong).toArray()) {
|
||||
pw.println(app.getFacetInfoLine(cellId).toCSV());
|
||||
|
||||
NavigableSet<Long> neighbors = app.neighbors(cellId);
|
||||
for (long neighborCellId : neighbors)
|
||||
pw.println(app.getFacetInfoLine(neighborCellId).toCSV());
|
||||
}
|
||||
}
|
||||
logger.info("Wrote {}", cl.getOptionValue("output"));
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getLocalizedMessage(), e);
|
||||
}
|
||||
|
||||
logger.info("Finished.");
|
||||
}
|
||||
}
|
||||
@@ -305,8 +305,8 @@ public class PointCloudFormatConverter implements TerrasaurTool {
|
||||
}
|
||||
} else {
|
||||
if (halfSize < 0 || groundSampleDistance < 0) {
|
||||
System.out.printf(
|
||||
"Must supply -halfSize and -groundSampleDistance for %s output\n",
|
||||
logger.error(
|
||||
"Must supply -halfSize and -groundSampleDistance for {} output",
|
||||
outFormat);
|
||||
return;
|
||||
}
|
||||
|
||||
417
src/main/java/terrasaur/apps/PointCloudOverlap.java
Normal file
417
src/main/java/terrasaur/apps/PointCloudOverlap.java
Normal file
@@ -0,0 +1,417 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2025 Johns Hopkins University Applied Physics Laboratory
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package terrasaur.apps;
|
||||
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import nom.tam.fits.FitsException;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.Option;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
|
||||
import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
|
||||
import org.apache.commons.math3.geometry.euclidean.twod.hull.MonotoneChain;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import picante.math.coords.LatitudinalVector;
|
||||
import spice.basic.SpiceException;
|
||||
import spice.basic.Vector3;
|
||||
import terrasaur.enums.FORMATS;
|
||||
import terrasaur.templates.TerrasaurTool;
|
||||
import terrasaur.utils.NativeLibraryLoader;
|
||||
import terrasaur.utils.VectorStatistics;
|
||||
import terrasaur.utils.tessellation.StereographicProjection;
|
||||
import vtk.vtkPoints;
|
||||
|
||||
public class PointCloudOverlap implements TerrasaurTool {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
|
||||
@Override
|
||||
public String shortDescription() {
|
||||
return "Find points in a point cloud which overlap a reference point cloud.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fullDescription(Options options) {
|
||||
String header = "";
|
||||
String footer =
|
||||
"\nThis program finds all points in the input point cloud which overlap the points in a reference point cloud.\n\n"
|
||||
+ "Supported input formats are ASCII, BINARY, L2, OBJ, and VTK. Supported output formats are ASCII, BINARY, L2, and VTK. "
|
||||
+ "ASCII format is white spaced delimited x y z coordinates. BINARY files must contain double precision x y z coordinates.\n\n"
|
||||
+ "A plane is fit to the reference point cloud and all points in each cloud are projected onto this plane. Any point in the "
|
||||
+ "projected input cloud which falls within the outline of the projected reference cloud is considered to be overlapping.";
|
||||
return TerrasaurTool.super.fullDescription(options, header, footer);
|
||||
}
|
||||
|
||||
private Path2D.Double polygon;
|
||||
|
||||
private StereographicProjection proj;
|
||||
|
||||
/*- Useful for debugging
|
||||
private vtkPoints ref2DPoints;
|
||||
private vtkPoints input2DPoints;
|
||||
private vtkPolyData polygonPolyData;
|
||||
private vtkCellArray polygonCells;
|
||||
private vtkPoints polygonPoints;
|
||||
private vtkDoubleArray polygonSuccessArray;
|
||||
*/
|
||||
|
||||
public PointCloudOverlap(Collection<double[]> refPoints) {
|
||||
if (refPoints != null) {
|
||||
VectorStatistics vStats = new VectorStatistics();
|
||||
for (double[] pt : refPoints) vStats.add(new Vector3(pt));
|
||||
|
||||
Vector3D centerXYZ = vStats.getMean();
|
||||
|
||||
proj =
|
||||
new StereographicProjection(
|
||||
new LatitudinalVector(1, centerXYZ.getDelta(), centerXYZ.getAlpha()));
|
||||
|
||||
createRefPolygon(refPoints);
|
||||
}
|
||||
}
|
||||
|
||||
public StereographicProjection getProjection() {
|
||||
return proj;
|
||||
}
|
||||
|
||||
private void createRefPolygon(Collection<double[]> refPoints) {
|
||||
List<Vector2D> stereographicPoints = new ArrayList<>();
|
||||
for (double[] refPt : refPoints) {
|
||||
Vector3D point3D = new Vector3D(refPt);
|
||||
Point2D point = proj.forward(point3D.getDelta(), point3D.getAlpha());
|
||||
stereographicPoints.add(new Vector2D(point.getX(), point.getY()));
|
||||
}
|
||||
|
||||
/*-
|
||||
ref2DPoints = new vtkPoints();
|
||||
input2DPoints = new vtkPoints();
|
||||
polygonPolyData = new vtkPolyData();
|
||||
polygonCells = new vtkCellArray();
|
||||
polygonPoints = new vtkPoints();
|
||||
polygonSuccessArray = new vtkDoubleArray();
|
||||
polygonSuccessArray.SetName("success")
|
||||
polygonPolyData.SetPoints(polygonPoints);
|
||||
polygonPolyData.SetLines(polygonCells);
|
||||
polygonPolyData.GetCellData().AddArray(polygonSuccessArray);
|
||||
|
||||
for (Vector2D refPoint : refPoints)
|
||||
ref2DPoints.InsertNextPoint(refPoint.getX(), refPoint.getY(), 0);
|
||||
*/
|
||||
|
||||
MonotoneChain mc = new MonotoneChain();
|
||||
List<Vector2D> vertices = new ArrayList<>(mc.findHullVertices(stereographicPoints));
|
||||
/*-
|
||||
for (int i = 1; i < vertices.size(); i++) {
|
||||
Vector2D lastPt = vertices.get(i - 1);
|
||||
Vector2D thisPt = vertices.get(i);
|
||||
System.out.printf("%f %f to %f %f distance %f\n", lastPt.getX(), lastPt.getY(), thisPt.getX(),
|
||||
thisPt.getY(), lastPt.distance(thisPt));
|
||||
}
|
||||
Vector2D lastPt = vertices.get(vertices.size() - 1);
|
||||
Vector2D thisPt = vertices.get(0);
|
||||
System.out.printf("%f %f to %f %f distance %f\n", lastPt.getX(), lastPt.getY(), thisPt.getX(),
|
||||
thisPt.getY(), lastPt.distance(thisPt));
|
||||
*/
|
||||
// int id0 = 0;
|
||||
for (Vector2D vertex : vertices) {
|
||||
// int id1 = polygonPoints.InsertNextPoint(vertex.getX(), vertex.getY(), 0);
|
||||
|
||||
if (polygon == null) {
|
||||
polygon = new Path2D.Double();
|
||||
polygon.moveTo(vertex.getX(), vertex.getY());
|
||||
} else {
|
||||
polygon.lineTo(vertex.getX(), vertex.getY());
|
||||
/*-
|
||||
vtkLine line = new vtkLine();
|
||||
line.GetPointIds().SetId(0, id0);
|
||||
line.GetPointIds().SetId(1, id1);
|
||||
polygonCells.InsertNextCell(line);
|
||||
*/
|
||||
}
|
||||
// id0 = id1;
|
||||
}
|
||||
polygon.closePath();
|
||||
|
||||
/*-
|
||||
vtkPolyDataWriter writer = new vtkPolyDataWriter();
|
||||
writer.SetInputData(polygonPolyData);
|
||||
writer.SetFileName("polygon2D.vtk");
|
||||
writer.SetFileTypeToBinary();
|
||||
writer.Update();
|
||||
|
||||
writer = new vtkPolyDataWriter();
|
||||
polygonPolyData = new vtkPolyData();
|
||||
polygonPolyData.SetPoints(ref2DPoints);
|
||||
writer.SetInputData(polygonPolyData);
|
||||
writer.SetFileName("refPoints.vtk");
|
||||
writer.SetFileTypeToBinary();
|
||||
writer.Update();
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
public boolean isEnclosed(double[] xyz) {
|
||||
Vector3D point = new Vector3D(xyz);
|
||||
Point2D projected = proj.forward(point.getDelta(), point.getAlpha());
|
||||
return polygon.contains(projected.getX(), projected.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param inputPoints points to consider
|
||||
* @param scale scale factor
|
||||
* @return indices of all points inside the scaled polygon
|
||||
*/
|
||||
public List<Integer> scalePoints(List<double[]> inputPoints, double scale) {
|
||||
|
||||
List<Vector2D> projected = new ArrayList<>();
|
||||
for (double[] inputPoint : inputPoints) {
|
||||
Vector3D point = new Vector3D(inputPoint);
|
||||
Point2D projectedPoint = proj.forward(point.getDelta(), point.getAlpha());
|
||||
projected.add(new Vector2D(projectedPoint.getX(), projectedPoint.getY()));
|
||||
}
|
||||
|
||||
Vector2D center = new Vector2D(0, 0);
|
||||
for (Vector2D inputPoint : projected) center = center.add(inputPoint);
|
||||
|
||||
center = center.scalarMultiply(1. / inputPoints.size());
|
||||
|
||||
List<Vector2D> translatedPoints = new ArrayList<>();
|
||||
for (Vector2D inputPoint : projected) translatedPoints.add(inputPoint.subtract(center));
|
||||
|
||||
Path2D.Double thisPolygon = null;
|
||||
MonotoneChain mc = new MonotoneChain();
|
||||
Collection<Vector2D> vertices = mc.findHullVertices(translatedPoints);
|
||||
for (Vector2D vertex : vertices) {
|
||||
if (thisPolygon == null) {
|
||||
thisPolygon = new Path2D.Double();
|
||||
thisPolygon.moveTo(scale * vertex.getX(), scale * vertex.getY());
|
||||
} else {
|
||||
thisPolygon.lineTo(scale * vertex.getX(), scale * vertex.getY());
|
||||
}
|
||||
}
|
||||
thisPolygon.closePath();
|
||||
|
||||
List<Integer> indices = new ArrayList<>();
|
||||
for (int i = 0; i < projected.size(); i++) {
|
||||
Vector2D inputPoint = projected.get(i);
|
||||
if (thisPolygon.contains(
|
||||
inputPoint.getX() - center.getX(), inputPoint.getY() - center.getY())) indices.add(i);
|
||||
}
|
||||
return indices;
|
||||
}
|
||||
|
||||
private static Options defineOptions() {
|
||||
Options options = new Options();
|
||||
options.addOption(
|
||||
Option.builder("inputFormat")
|
||||
.hasArg()
|
||||
.desc(
|
||||
"Format of input file. If not present format will be inferred from file extension.")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("inputFile")
|
||||
.required()
|
||||
.hasArg()
|
||||
.desc("Required. Name of input file.")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("inllr")
|
||||
.desc(
|
||||
"If present, input values are assumed to be lon, lat, rad. Default is x, y, z. Only used with ASCII or BINARY formats.")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("referenceFormat")
|
||||
.hasArg()
|
||||
.desc(
|
||||
"Format of reference file. If not present format will be inferred from file extension.")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("referenceFile")
|
||||
.required()
|
||||
.hasArg()
|
||||
.desc("Required. Name of reference file.")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("refllr")
|
||||
.desc(
|
||||
"If present, reference values are assumed to be lon, lat, rad. Default is x, y, z. Only used with ASCII or BINARY formats.")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("outputFormat")
|
||||
.hasArg()
|
||||
.desc(
|
||||
"Format of output file. If not present format will be inferred from file extension.")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("outputFile")
|
||||
.required()
|
||||
.hasArg()
|
||||
.desc("Required. Name of output file.")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("outllr")
|
||||
.desc(
|
||||
"If present, output values will be lon, lat, rad. Default is x, y, z. Only used with ASCII or BINARY formats.")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("scale")
|
||||
.hasArg()
|
||||
.desc("Value to scale bounding box containing intersect region. Default is 1.0.")
|
||||
.build());
|
||||
return options;
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
throws SpiceException, IOException, InterruptedException, FitsException {
|
||||
|
||||
NativeLibraryLoader.loadVtkLibraries();
|
||||
NativeLibraryLoader.loadSpiceLibraries();
|
||||
|
||||
TerrasaurTool defaultOBJ = new PointCloudOverlap(null);
|
||||
|
||||
Options options = defineOptions();
|
||||
|
||||
CommandLine cl = defaultOBJ.parseArgs(args, options);
|
||||
|
||||
Map<MessageLabel, String> startupMessages = defaultOBJ.startupMessages(cl);
|
||||
for (MessageLabel ml : startupMessages.keySet())
|
||||
logger.info(String.format("%s %s", ml.label, startupMessages.get(ml)));
|
||||
|
||||
// Read the reference file
|
||||
FORMATS refFormat =
|
||||
cl.hasOption("referenceFormat")
|
||||
? FORMATS.valueOf(cl.getOptionValue("referenceFormat").toUpperCase())
|
||||
: FORMATS.formatFromExtension(cl.getOptionValue("referenceFile"));
|
||||
String refFile = cl.getOptionValue("referenceFile");
|
||||
boolean refLLR = cl.hasOption("refllr");
|
||||
|
||||
PointCloudFormatConverter pcfc = new PointCloudFormatConverter(refFormat, FORMATS.VTK);
|
||||
pcfc.read(refFile, refLLR);
|
||||
vtkPoints referencePoints = pcfc.getPoints();
|
||||
logger.info("{} points read from {}", referencePoints.GetNumberOfPoints(), refFile);
|
||||
|
||||
List<double[]> refPts = new ArrayList<>();
|
||||
for (int i = 0; i < referencePoints.GetNumberOfPoints(); i++) {
|
||||
refPts.add(referencePoints.GetPoint(i));
|
||||
}
|
||||
|
||||
// create the overlap object and set the enclosing polygon
|
||||
PointCloudOverlap pco = new PointCloudOverlap(refPts);
|
||||
|
||||
// Read the input point cloud
|
||||
FORMATS inFormat =
|
||||
cl.hasOption("inputFormat")
|
||||
? FORMATS.valueOf(cl.getOptionValue("inputFormat").toUpperCase())
|
||||
: FORMATS.formatFromExtension(cl.getOptionValue("inputFile"));
|
||||
String inFile = cl.getOptionValue("inputFile");
|
||||
boolean inLLR = cl.hasOption("inllr");
|
||||
|
||||
pcfc = new PointCloudFormatConverter(inFormat, FORMATS.VTK);
|
||||
pcfc.read(inFile, inLLR);
|
||||
vtkPoints inputPoints = pcfc.getPoints();
|
||||
logger.info("{} points read from {}", inputPoints.GetNumberOfPoints(), inFile);
|
||||
|
||||
List<Integer> enclosedIndices = new ArrayList<>();
|
||||
for (int i = 0; i < inputPoints.GetNumberOfPoints(); i++) {
|
||||
double[] pt = inputPoints.GetPoint(i);
|
||||
if (pco.isEnclosed(pt)) enclosedIndices.add(i);
|
||||
}
|
||||
|
||||
if (cl.hasOption("scale")) {
|
||||
List<double[]> pts = new ArrayList<>();
|
||||
for (Integer i : enclosedIndices) pts.add(inputPoints.GetPoint(i));
|
||||
|
||||
// this list includes which of the enclosed points are inside the scaled polygon
|
||||
List<Integer> theseIndices = pco.scalePoints(pts, Double.parseDouble(cl.getOptionValue("scale")));
|
||||
|
||||
// now relate this list back to the original list of points
|
||||
List<Integer> newIndices = new ArrayList<>();
|
||||
for (Integer i : theseIndices) newIndices.add(enclosedIndices.get(i));
|
||||
enclosedIndices = newIndices;
|
||||
}
|
||||
|
||||
VectorStatistics xyzStats = new VectorStatistics();
|
||||
VectorStatistics xyStats = new VectorStatistics();
|
||||
for (Integer i : enclosedIndices) {
|
||||
double[] thisPt = inputPoints.GetPoint(i);
|
||||
Vector3D thisPt3D = new Vector3D(thisPt);
|
||||
xyzStats.add(thisPt3D);
|
||||
Point2D projectedPt = pco.getProjection().forward(thisPt3D.getDelta(), thisPt3D.getAlpha());
|
||||
xyStats.add(new Vector3(projectedPt.getX(), projectedPt.getY(), 0));
|
||||
}
|
||||
|
||||
logger.info(
|
||||
"Center XYZ: {}, {}, {}",
|
||||
xyzStats.getMean().getX(), xyzStats.getMean().getY(), xyzStats.getMean().getZ());
|
||||
Vector3D centerXYZ = xyzStats.getMean();
|
||||
logger.info(
|
||||
"Center lon, lat: {}, {}\n",
|
||||
Math.toDegrees(centerXYZ.getAlpha()), Math.toDegrees(centerXYZ.getDelta()));
|
||||
logger.info(
|
||||
"xmin/xmax/extent: {}/{}/{}\n",
|
||||
xyzStats.getMin().getX(),
|
||||
xyzStats.getMax().getX(),
|
||||
xyzStats.getMax().getX() - xyzStats.getMin().getX());
|
||||
logger.info(
|
||||
"ymin/ymax/extent: {}/{}/{}\n",
|
||||
xyzStats.getMin().getY(),
|
||||
xyzStats.getMax().getY(),
|
||||
xyzStats.getMax().getY() - xyzStats.getMin().getY());
|
||||
logger.info(
|
||||
"zmin/zmax/extent: {}/{}/{}\n",
|
||||
xyzStats.getMin().getZ(),
|
||||
xyzStats.getMax().getZ(),
|
||||
xyzStats.getMax().getZ() - xyzStats.getMin().getZ());
|
||||
|
||||
FORMATS outFormat =
|
||||
cl.hasOption("outputFormat")
|
||||
? FORMATS.valueOf(cl.getOptionValue("outputFormat").toUpperCase())
|
||||
: FORMATS.formatFromExtension(cl.getOptionValue("outputFile"));
|
||||
|
||||
vtkPoints pointsToWrite = new vtkPoints();
|
||||
for (Integer i : enclosedIndices) pointsToWrite.InsertNextPoint(inputPoints.GetPoint(i));
|
||||
pcfc = new PointCloudFormatConverter(FORMATS.VTK, outFormat);
|
||||
pcfc.setPoints(pointsToWrite);
|
||||
String outputFilename = cl.getOptionValue("outputFile");
|
||||
pcfc.write(outputFilename, cl.hasOption("outllr"));
|
||||
if (new File(outputFilename).exists()) {
|
||||
logger.info(
|
||||
"{} points written to {}",
|
||||
pointsToWrite.GetNumberOfPoints(), outputFilename);
|
||||
} else {
|
||||
logger.error("Could not write {}", outputFilename);
|
||||
}
|
||||
|
||||
logger.info("Finished");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO write out center of output pointcloud
|
||||
@@ -313,7 +313,7 @@ public class RenderShapeFromSumFile implements TerrasaurTool {
|
||||
logger.printf(
|
||||
Level.DEBUG,
|
||||
"Thread %d lat/lon %.2f/%.2f, %s, sum %f, cells %d, %.2f",
|
||||
Thread.currentThread().getId(),
|
||||
Thread.currentThread().threadId(),
|
||||
Math.toDegrees(intersectPoint.getDelta()),
|
||||
Math.toDegrees(intersectPoint.getAlpha()),
|
||||
intersectPoint.toString(),
|
||||
@@ -338,7 +338,7 @@ public class RenderShapeFromSumFile implements TerrasaurTool {
|
||||
@Override
|
||||
public Map<Integer, Brightness> call() throws Exception {
|
||||
|
||||
logger.info("Thread {}: starting", Thread.currentThread().getId());
|
||||
logger.info("Thread {}: starting", Thread.currentThread().threadId());
|
||||
|
||||
int xPixels = subPixel * nPixelsX;
|
||||
|
||||
@@ -380,7 +380,7 @@ public class RenderShapeFromSumFile implements TerrasaurTool {
|
||||
logger.debug(
|
||||
String.format(
|
||||
"Thread %d: No intersection with local model for pixel (%d,%d): lat/lon %.2f/%.2f, using global intersection %d %s",
|
||||
Thread.currentThread().getId(),
|
||||
Thread.currentThread().threadId(),
|
||||
i,
|
||||
j,
|
||||
Math.toDegrees(intersectPt3D.getDelta()),
|
||||
@@ -397,7 +397,7 @@ public class RenderShapeFromSumFile implements TerrasaurTool {
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Thread {}: finished", Thread.currentThread().getId());
|
||||
logger.info("Thread {}: finished", Thread.currentThread().threadId());
|
||||
|
||||
return brightness;
|
||||
}
|
||||
|
||||
166
src/main/java/terrasaur/apps/TriAx.java
Normal file
166
src/main/java/terrasaur/apps/TriAx.java
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2025 Johns Hopkins University Applied Physics Laboratory
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package terrasaur.apps;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Map;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.Option;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import terrasaur.templates.TerrasaurTool;
|
||||
import terrasaur.utils.ICQUtils;
|
||||
import terrasaur.utils.NativeLibraryLoader;
|
||||
import terrasaur.utils.PolyDataUtil;
|
||||
import vtk.vtkPolyData;
|
||||
|
||||
public class TriAx implements TerrasaurTool {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
|
||||
private TriAx() {}
|
||||
|
||||
@Override
|
||||
public String shortDescription() {
|
||||
return "Generate a triaxial ellipsoid in ICQ format.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fullDescription(Options options) {
|
||||
|
||||
String footer =
|
||||
"\nTriAx is an implementation of the SPC tool TRIAX, which generates a triaxial ellipsoid in ICQ format.\n";
|
||||
return TerrasaurTool.super.fullDescription(options, "", footer);
|
||||
}
|
||||
|
||||
static Options defineOptions() {
|
||||
Options options = TerrasaurTool.defineOptions();
|
||||
options.addOption(
|
||||
Option.builder("A")
|
||||
.required()
|
||||
.hasArg()
|
||||
.desc("Long axis of the ellipsoid, arbitrary units (usually assumed to be km).")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("B")
|
||||
.required()
|
||||
.hasArg()
|
||||
.desc("Medium axis of the ellipsoid, arbitrary units (usually assumed to be km).")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("C")
|
||||
.required()
|
||||
.hasArg()
|
||||
.desc("Short axis of the ellipsoid, arbitrary units (usually assumed to be km).")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("Q")
|
||||
.required()
|
||||
.hasArg()
|
||||
.desc("ICQ size parameter. This is conventionally but not necessarily a power of 2.")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("saveOBJ")
|
||||
.desc(
|
||||
"If present, save in OBJ format as well. "
|
||||
+ "File will have the same name as ICQ file with an OBJ extension.")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("output").hasArg().required().desc("Name of ICQ file to write.").build());
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
static final int MAX_Q = 512;
|
||||
|
||||
public static void main(String[] args) {
|
||||
TerrasaurTool defaultOBJ = new TriAx();
|
||||
|
||||
Options options = defineOptions();
|
||||
|
||||
CommandLine cl = defaultOBJ.parseArgs(args, options);
|
||||
|
||||
Map<MessageLabel, String> startupMessages = defaultOBJ.startupMessages(cl);
|
||||
for (MessageLabel ml : startupMessages.keySet())
|
||||
logger.info("{} {}", ml.label, startupMessages.get(ml));
|
||||
|
||||
int q = Integer.parseInt(cl.getOptionValue("Q"));
|
||||
String shapefile = cl.getOptionValue("output");
|
||||
|
||||
double[] ax = new double[3];
|
||||
ax[0] = Double.parseDouble(cl.getOptionValue("A"));
|
||||
ax[1] = Double.parseDouble(cl.getOptionValue("B"));
|
||||
ax[2] = Double.parseDouble(cl.getOptionValue("C"));
|
||||
|
||||
double[][][][] vec = new double[3][MAX_Q + 1][MAX_Q + 1][6];
|
||||
for (int f = 0; f < 6; f++) {
|
||||
for (int i = 0; i <= q; i++) {
|
||||
for (int j = 0; j <= q; j++) {
|
||||
|
||||
double[] u = ICQUtils.xyf2u(q, i, j, f, ax);
|
||||
double z =
|
||||
1
|
||||
/ Math.sqrt(
|
||||
Math.pow(u[0] / ax[0], 2)
|
||||
+ Math.pow(u[1] / ax[1], 2)
|
||||
+ Math.pow(u[2] / ax[2], 2));
|
||||
|
||||
double[] v = new Vector3D(u).scalarMultiply(z).toArray();
|
||||
for (int k = 0; k < 3; k++) {
|
||||
vec[k][i][j][f] = v[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ICQUtils.writeICQ(q, vec, shapefile);
|
||||
|
||||
if (cl.hasOption("saveOBJ")) {
|
||||
|
||||
String basename = FilenameUtils.getBaseName(shapefile);
|
||||
String dirname = FilenameUtils.getFullPath(shapefile);
|
||||
if (dirname.isEmpty()) dirname = ".";
|
||||
File obj = new File(dirname, basename + ".obj");
|
||||
|
||||
NativeLibraryLoader.loadVtkLibraries();
|
||||
try {
|
||||
vtkPolyData polydata = PolyDataUtil.loadShapeModel(shapefile);
|
||||
if (polydata == null) {
|
||||
logger.error("Cannot read {}", shapefile);
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
polydata = PolyDataUtil.removeDuplicatePoints(polydata);
|
||||
polydata = PolyDataUtil.removeUnreferencedPoints(polydata);
|
||||
polydata = PolyDataUtil.removeZeroAreaFacets(polydata);
|
||||
|
||||
PolyDataUtil.saveShapeModelAsOBJ(polydata, obj.getPath());
|
||||
} catch (Exception e) {
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,6 @@ import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.Option;
|
||||
import org.apache.commons.cli.Options;
|
||||
@@ -65,6 +64,12 @@ public class ValidateNormals implements TerrasaurTool {
|
||||
|
||||
static Options defineOptions() {
|
||||
Options options = TerrasaurTool.defineOptions();
|
||||
options.addOption(
|
||||
Option.builder("fast")
|
||||
.desc("If present, only check for overhangs if center and normal point in opposite " +
|
||||
"directions. Default behavior is to always check for intersections between body center " +
|
||||
"and facet center.")
|
||||
.build());
|
||||
options.addOption(
|
||||
Option.builder("origin")
|
||||
.hasArg()
|
||||
@@ -126,10 +131,12 @@ public class ValidateNormals implements TerrasaurTool {
|
||||
|
||||
private final long index0;
|
||||
private final long index1;
|
||||
private final boolean fast;
|
||||
|
||||
public FlippedNormalFinder(long index0, long index1) {
|
||||
public FlippedNormalFinder(long index0, long index1, boolean fast) {
|
||||
this.index0 = index0;
|
||||
this.index1 = index1;
|
||||
this.fast = fast;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -158,20 +165,21 @@ public class ValidateNormals implements TerrasaurTool {
|
||||
long index = index0 + i;
|
||||
|
||||
CellInfo ci = CellInfo.getCellInfo(polyData, index, idList);
|
||||
getOBBTree().IntersectWithLine(origin, ci.center().toArray(), null, cellIds);
|
||||
boolean isOpposite = (ci.center().dotProduct(ci.normal()) < 0);
|
||||
|
||||
// count up all crossings of the surface between the origin and the facet.
|
||||
int numCrossings = 0;
|
||||
for (int j = 0; j < cellIds.GetNumberOfIds(); j++) {
|
||||
if (cellIds.GetId(j) == index) break;
|
||||
numCrossings++;
|
||||
if (isOpposite || !fast) {
|
||||
// count up all crossings of the surface between the origin and the facet.
|
||||
getOBBTree().IntersectWithLine(origin, ci.center().toArray(), null, cellIds);
|
||||
for (int j = 0; j < cellIds.GetNumberOfIds(); j++) {
|
||||
if (cellIds.GetId(j) == index) break;
|
||||
numCrossings++;
|
||||
}
|
||||
}
|
||||
|
||||
// if numCrossings is even, the radial and normal should point in the same direction. If it
|
||||
// is odd, the
|
||||
// radial and normal should point in opposite directions.
|
||||
// is odd, the radial and normal should point in opposite directions.
|
||||
boolean shouldBeOpposite = (numCrossings % 2 == 1);
|
||||
boolean isOpposite = (ci.center().dotProduct(ci.normal()) < 0);
|
||||
|
||||
// XOR operator - true if both conditions are different
|
||||
if (isOpposite ^ shouldBeOpposite) flippedNormals.add(index);
|
||||
@@ -208,7 +216,7 @@ public class ValidateNormals implements TerrasaurTool {
|
||||
|
||||
Map<MessageLabel, String> startupMessages = defaultOBJ.startupMessages(cl);
|
||||
for (MessageLabel ml : startupMessages.keySet())
|
||||
logger.info(String.format("%s %s", ml.label, startupMessages.get(ml)));
|
||||
logger.info("{} {}", ml.label, startupMessages.get(ml));
|
||||
|
||||
NativeLibraryLoader.loadVtkLibraries();
|
||||
|
||||
@@ -234,6 +242,7 @@ public class ValidateNormals implements TerrasaurTool {
|
||||
|
||||
Set<Long> flippedNormals = new HashSet<>();
|
||||
|
||||
boolean fast = cl.hasOption("fast");
|
||||
int numThreads =
|
||||
cl.hasOption("numThreads") ? Integer.parseInt(cl.getOptionValue("numThreads")) : 1;
|
||||
try (ExecutorService executor = Executors.newFixedThreadPool(numThreads)) {
|
||||
@@ -244,7 +253,7 @@ public class ValidateNormals implements TerrasaurTool {
|
||||
long fromIndex = i * numFacets;
|
||||
long toIndex = Math.min(polyData.GetNumberOfCells(), fromIndex + numFacets);
|
||||
|
||||
FlippedNormalFinder fnf = app.new FlippedNormalFinder(fromIndex, toIndex);
|
||||
FlippedNormalFinder fnf = app.new FlippedNormalFinder(fromIndex, toIndex, fast);
|
||||
futures.add(executor.submit(fnf));
|
||||
}
|
||||
|
||||
|
||||
@@ -308,7 +308,7 @@ public class ValidateOBJ implements TerrasaurTool {
|
||||
TriangularFacet facet =
|
||||
new TriangularFacet(new VectorIJK(pt0), new VectorIJK(pt1), new VectorIJK(pt2));
|
||||
if (facet.getArea() > 0) {
|
||||
stats.addValue(facet.getCenter().getDot(facet.getNormal()));
|
||||
stats.addValue(facet.getCenter().createUnitized().getDot(facet.getNormal()));
|
||||
cStats.add(facet.getCenter());
|
||||
nStats.add(facet.getNormal());
|
||||
}
|
||||
@@ -374,7 +374,7 @@ public class ValidateOBJ implements TerrasaurTool {
|
||||
|
||||
Map<MessageLabel, String> startupMessages = defaultOBJ.startupMessages(cl);
|
||||
for (MessageLabel ml : startupMessages.keySet())
|
||||
logger.info(String.format("%s %s", ml.label, startupMessages.get(ml)));
|
||||
logger.info("{} {}", ml.label, startupMessages.get(ml));
|
||||
|
||||
NativeLibraryLoader.loadVtkLibraries();
|
||||
vtkPolyData polyData = PolyDataUtil.loadShapeModel(cl.getOptionValue("obj"));
|
||||
@@ -418,7 +418,7 @@ public class ValidateOBJ implements TerrasaurTool {
|
||||
|
||||
if (cl.hasOption("output")) {
|
||||
PolyDataUtil.saveShapeModelAsOBJ(polyData, cl.getOptionValue("output"));
|
||||
logger.info(String.format("Wrote OBJ file %s", cl.getOptionValue("output")));
|
||||
logger.info("Wrote OBJ file {}", cl.getOptionValue("output"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ public abstract class CellInfo {
|
||||
polydata.GetPoint(idList.GetId(2), pt2);
|
||||
}
|
||||
|
||||
private static CellInfo fromPoints(double[] pt0, double[] pt1, double[] pt2) {
|
||||
public static CellInfo fromPoints(double[] pt0, double[] pt1, double[] pt2) {
|
||||
|
||||
ImmutableCellInfo.Builder builder = ImmutableCellInfo.builder();
|
||||
builder.pt0(new Vector3D(pt0));
|
||||
|
||||
111
src/main/java/terrasaur/utils/ICQUtils.java
Normal file
111
src/main/java/terrasaur/utils/ICQUtils.java
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2025 Johns Hopkins University Applied Physics Laboratory
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package terrasaur.utils;
|
||||
|
||||
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
public class ICQUtils {
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
|
||||
public static double[] xyf2u(int q, double x, double y, int face, double[] ax) {
|
||||
double pi = Math.acos(-1.0);
|
||||
double[] dt = new double[3];
|
||||
double[] v = new double[3];
|
||||
int[][][] u = defu();
|
||||
|
||||
dt[0] = Math.tan((2 * x / q - 1) * pi / 4);
|
||||
dt[1] = Math.tan((2 * y / q - 1) * pi / 4);
|
||||
dt[2] = 1 / Math.sqrt(1 + dt[0] * dt[0] + dt[1] * dt[1]);
|
||||
dt[0] *= dt[2];
|
||||
dt[1] *= dt[2];
|
||||
|
||||
for (int k = 0; k < 3; k++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
int sign = u[j][k][face];
|
||||
if (sign != 0) {
|
||||
v[k] = dt[j] * sign * Math.sqrt(ax[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Vector3D(v).normalize().toArray();
|
||||
}
|
||||
|
||||
static int[][][] defu() {
|
||||
int[][][] u = new int[3][3][6];
|
||||
|
||||
u[0][0][0] = 1;
|
||||
u[1][1][0] = -1;
|
||||
u[2][2][0] = 1;
|
||||
|
||||
u[0][0][1] = 1;
|
||||
u[1][2][1] = -1;
|
||||
u[2][1][1] = -1;
|
||||
|
||||
u[0][1][2] = -1;
|
||||
u[1][2][2] = -1;
|
||||
u[2][0][2] = -1;
|
||||
|
||||
u[0][0][3] = -1;
|
||||
u[1][2][3] = -1;
|
||||
u[2][1][3] = 1;
|
||||
|
||||
u[0][1][4] = 1;
|
||||
u[1][2][4] = -1;
|
||||
u[2][0][4] = 1;
|
||||
|
||||
u[0][0][5] = 1;
|
||||
u[1][1][5] = 1;
|
||||
u[2][2][5] = -1;
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
public static void writeICQ(int q, double[][][][] vec, String filename){
|
||||
|
||||
try (PrintWriter out = new PrintWriter(new FileWriter(filename))) {
|
||||
out.println(q);
|
||||
for (int f = 0; f < 6; f++) {
|
||||
for (int j = 0; j <= q; j++) {
|
||||
for (int i = 0; i <= q; i++) {
|
||||
for (int k = 0; k < 3; k++) {
|
||||
out.printf("%12.5f", vec[k][i][j][f]);
|
||||
}
|
||||
out.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -521,17 +521,17 @@ public class PolyDataUtil {
|
||||
n[q][q][4] = n[0][q][3];
|
||||
n[q][q][5] = n[0][q][3];
|
||||
|
||||
try (PrintWriter pw = new PrintWriter("java.txt")) {
|
||||
for (int f = 0; f < 6; f++) {
|
||||
for (int i = 0; i < q; i++) {
|
||||
for (int j = 0; j < q; j++) {
|
||||
pw.printf("%3d%3d%3d%6d\n", i, j, f + 1, n[i][j][f]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
logger.error(e.getLocalizedMessage());
|
||||
}
|
||||
// try (PrintWriter pw = new PrintWriter("java.txt")) {
|
||||
// for (int f = 0; f < 6; f++) {
|
||||
// for (int i = 0; i < q; i++) {
|
||||
// for (int j = 0; j < q; j++) {
|
||||
// pw.printf("%3d%3d%3d%6d\n", i, j, f + 1, n[i][j][f]);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } catch (FileNotFoundException e) {
|
||||
// logger.error(e.getLocalizedMessage());
|
||||
// }
|
||||
|
||||
pltLines.add(String.format("%d", 12 * q * q));
|
||||
n0 = 0;
|
||||
|
||||
@@ -29,32 +29,35 @@ import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
import net.jafama.FastMath;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
|
||||
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.immutables.value.Value;
|
||||
import net.jafama.FastMath;
|
||||
import terrasaur.utils.ImmutableSumFile.Builder;
|
||||
import terrasaur.utils.math.MathConversions;
|
||||
import picante.math.vectorspace.RotationMatrixIJK;
|
||||
import picante.math.vectorspace.VectorIJK;
|
||||
import picante.mechanics.*;
|
||||
import picante.mechanics.providers.aberrated.AberrationCorrection;
|
||||
import spice.basic.Plane;
|
||||
import spice.basic.Ray;
|
||||
import spice.basic.RayPlaneIntercept;
|
||||
import spice.basic.SpiceException;
|
||||
import spice.basic.Vector3;
|
||||
import terrasaur.utils.ImmutableSumFile.Builder;
|
||||
import terrasaur.utils.math.MathConversions;
|
||||
import terrasaur.utils.spice.SpiceBundle;
|
||||
|
||||
/**
|
||||
* Class describing Bob Gaskell's sum file object
|
||||
*
|
||||
* @author nairah1
|
||||
*
|
||||
* @author nairah1
|
||||
*/
|
||||
@Value.Immutable
|
||||
public abstract class SumFile {
|
||||
|
||||
private final static Logger logger = LogManager.getLogger(SumFile.class);
|
||||
private static final Logger logger = LogManager.getLogger(SumFile.class);
|
||||
|
||||
// line 1
|
||||
public abstract String picnm();
|
||||
@@ -107,23 +110,63 @@ public abstract class SumFile {
|
||||
// line 13
|
||||
public abstract Vector3D sig_ptg();
|
||||
|
||||
@Nullable
|
||||
public abstract Vector3D frustum1();
|
||||
|
||||
@Nullable
|
||||
public abstract Vector3D frustum2();
|
||||
|
||||
@Nullable
|
||||
public abstract Vector3D frustum3();
|
||||
|
||||
@Nullable
|
||||
public abstract Vector3D frustum4();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param translation
|
||||
* @param rotation
|
||||
* @return A SumFile with the scobj transformed and the cx, cy, cz, and sz vectors rotated.
|
||||
* @param bundle SPICE bundle
|
||||
* @param observer spacecraft
|
||||
* @param target target
|
||||
* @param cameraFrame Camera Frame
|
||||
* @param t time
|
||||
* @return SumFile with scobj, C matrix, and sun direction evaluated using SPICE at time t
|
||||
*/
|
||||
public SumFile fromSpice(
|
||||
SpiceBundle bundle, EphemerisID observer, EphemerisID target, FrameID cameraFrame, double t) {
|
||||
FrameID bodyFixedFrame = bundle.getBodyFixedFrame(target);
|
||||
VectorIJK scobj =
|
||||
bundle
|
||||
.getAbProvider()
|
||||
.createAberratedPositionVectorFunction(
|
||||
target, observer, bodyFixedFrame, Coverage.ALL_TIME, AberrationCorrection.LT_S)
|
||||
.getPosition(t);
|
||||
RotationMatrixIJK cMatrix =
|
||||
bundle
|
||||
.getAbProvider()
|
||||
.getFrameProvider()
|
||||
.createFrameTransformFunction(cameraFrame, bodyFixedFrame, Coverage.ALL_TIME)
|
||||
.getTransform(t);
|
||||
VectorIJK sunDir =
|
||||
bundle
|
||||
.getAbProvider()
|
||||
.createAberratedPositionVectorFunction(
|
||||
CelestialBodies.SUN,
|
||||
target,
|
||||
bodyFixedFrame,
|
||||
Coverage.ALL_TIME,
|
||||
AberrationCorrection.LT_S)
|
||||
.getPosition(t)
|
||||
.unitize();
|
||||
|
||||
Builder b = ImmutableSumFile.builder().from(this);
|
||||
b.utcString(bundle.getTimeConversion().tdbToUTCString(t, "C"));
|
||||
b.scobj(MathConversions.toVector3D(scobj));
|
||||
b.cx(MathConversions.toVector3D(cMatrix.mxv(VectorIJK.I)));
|
||||
b.cy(MathConversions.toVector3D(cMatrix.mxv(VectorIJK.J)));
|
||||
b.cz(MathConversions.toVector3D(cMatrix.mxv(VectorIJK.K)));
|
||||
b.sz(MathConversions.toVector3D(cMatrix.mxv(sunDir)));
|
||||
return b.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param translation translation to apply to scobj
|
||||
* @param rotation rotation to apply to C matrix and sun direction
|
||||
* @return SumFile with the scobj transformed and the cx, cy, cz, and sz vectors rotated.
|
||||
*/
|
||||
public SumFile transform(Vector3D translation, Rotation rotation) {
|
||||
Builder b = ImmutableSumFile.builder().from(this);
|
||||
@@ -141,10 +184,8 @@ public abstract class SumFile {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a SumFile from the input file
|
||||
*
|
||||
* @param file
|
||||
* @return
|
||||
* @param file file to read
|
||||
* @return SumFile
|
||||
*/
|
||||
public static SumFile fromFile(File file) {
|
||||
SumFile s = null;
|
||||
@@ -157,10 +198,8 @@ public abstract class SumFile {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a SumFile from the input string list
|
||||
*
|
||||
* @param lines
|
||||
* @return
|
||||
* @param lines lines to read
|
||||
* @return SumFile
|
||||
*/
|
||||
public static SumFile fromLines(List<String> lines) {
|
||||
Builder b = ImmutableSumFile.builder();
|
||||
@@ -180,95 +219,114 @@ public abstract class SumFile {
|
||||
|
||||
parts = lines.get(4).trim().split("\\s+");
|
||||
double[] tmp = new double[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
tmp[i] = parseFortranDouble(parts[i]);
|
||||
for (int i = 0; i < 3; i++) tmp[i] = parseFortranDouble(parts[i]);
|
||||
b.scobj(new Vector3D(tmp));
|
||||
|
||||
parts = lines.get(5).trim().split("\\s+");
|
||||
tmp = new double[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
tmp[i] = parseFortranDouble(parts[i]);
|
||||
for (int i = 0; i < 3; i++) tmp[i] = parseFortranDouble(parts[i]);
|
||||
b.cx(new Vector3D(tmp).normalize());
|
||||
|
||||
parts = lines.get(6).trim().split("\\s+");
|
||||
tmp = new double[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
tmp[i] = parseFortranDouble(parts[i]);
|
||||
for (int i = 0; i < 3; i++) tmp[i] = parseFortranDouble(parts[i]);
|
||||
b.cy(new Vector3D(tmp).normalize());
|
||||
|
||||
parts = lines.get(7).trim().split("\\s+");
|
||||
tmp = new double[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
tmp[i] = parseFortranDouble(parts[i]);
|
||||
for (int i = 0; i < 3; i++) tmp[i] = parseFortranDouble(parts[i]);
|
||||
b.cz(new Vector3D(tmp).normalize());
|
||||
|
||||
parts = lines.get(8).trim().split("\\s+");
|
||||
tmp = new double[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
tmp[i] = parseFortranDouble(parts[i]);
|
||||
for (int i = 0; i < 3; i++) tmp[i] = parseFortranDouble(parts[i]);
|
||||
b.sz(new Vector3D(tmp).normalize());
|
||||
|
||||
parts = lines.get(9).trim().split("\\s+");
|
||||
tmp = new double[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
tmp[i] = parseFortranDouble(parts[i]);
|
||||
for (int i = 0; i < 3; i++) tmp[i] = parseFortranDouble(parts[i]);
|
||||
b.kmat1(new Vector3D(tmp));
|
||||
tmp = new double[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
tmp[i] = parseFortranDouble(parts[i + 3]);
|
||||
for (int i = 0; i < 3; i++) tmp[i] = parseFortranDouble(parts[i + 3]);
|
||||
b.kmat2(new Vector3D(tmp));
|
||||
|
||||
parts = lines.get(10).trim().split("\\s+");
|
||||
for (int i = 0; i < 4; i++)
|
||||
b.addDistortion(parseFortranDouble(parts[i]));
|
||||
for (int i = 0; i < 4; i++) b.addDistortion(parseFortranDouble(parts[i]));
|
||||
|
||||
parts = lines.get(11).trim().split("\\s+");
|
||||
tmp = new double[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
tmp[i] = parseFortranDouble(parts[i]);
|
||||
for (int i = 0; i < 3; i++) tmp[i] = parseFortranDouble(parts[i]);
|
||||
b.sig_vso(new Vector3D(tmp));
|
||||
|
||||
parts = lines.get(11).trim().split("\\s+");
|
||||
tmp = new double[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
tmp[i] = parseFortranDouble(parts[i]);
|
||||
for (int i = 0; i < 3; i++) tmp[i] = parseFortranDouble(parts[i]);
|
||||
b.sig_ptg(new Vector3D(tmp));
|
||||
|
||||
b.frustum1(Vector3D.ZERO);
|
||||
b.frustum2(Vector3D.ZERO);
|
||||
b.frustum3(Vector3D.ZERO);
|
||||
b.frustum4(Vector3D.ZERO);
|
||||
|
||||
SumFile s = b.build();
|
||||
double fov1 = Math.abs(Math.atan(s.npx() / (2.0 * s.mmfl() * s.kmat1().getX())));
|
||||
double fov2 = Math.abs(Math.atan(s.nln() / (2.0 * s.mmfl() * s.kmat2().getY())));
|
||||
Vector3D cornerVector = new Vector3D(-FastMath.tan(fov1), -FastMath.tan(fov2), 1.0);
|
||||
|
||||
double fx = cornerVector.getX() * s.cx().getX() + cornerVector.getY() * s.cy().getX()
|
||||
+ cornerVector.getZ() * s.cz().getX();
|
||||
double fy = cornerVector.getX() * s.cx().getY() + cornerVector.getY() * s.cy().getY()
|
||||
+ cornerVector.getZ() * s.cz().getY();
|
||||
double fz = cornerVector.getX() * s.cx().getZ() + cornerVector.getY() * s.cy().getZ()
|
||||
+ cornerVector.getZ() * s.cz().getZ();
|
||||
double fx =
|
||||
cornerVector.getX() * s.cx().getX()
|
||||
+ cornerVector.getY() * s.cy().getX()
|
||||
+ cornerVector.getZ() * s.cz().getX();
|
||||
double fy =
|
||||
cornerVector.getX() * s.cx().getY()
|
||||
+ cornerVector.getY() * s.cy().getY()
|
||||
+ cornerVector.getZ() * s.cz().getY();
|
||||
double fz =
|
||||
cornerVector.getX() * s.cx().getZ()
|
||||
+ cornerVector.getY() * s.cy().getZ()
|
||||
+ cornerVector.getZ() * s.cz().getZ();
|
||||
b.frustum3(new Vector3D(fx, fy, fz).normalize());
|
||||
|
||||
fx = -cornerVector.getX() * s.cx().getX() + cornerVector.getY() * s.cy().getX()
|
||||
+ cornerVector.getZ() * s.cz().getX();
|
||||
fy = -cornerVector.getX() * s.cx().getY() + cornerVector.getY() * s.cy().getY()
|
||||
+ cornerVector.getZ() * s.cz().getY();
|
||||
fz = -cornerVector.getX() * s.cx().getZ() + cornerVector.getY() * s.cy().getZ()
|
||||
+ cornerVector.getZ() * s.cz().getZ();
|
||||
fx =
|
||||
-cornerVector.getX() * s.cx().getX()
|
||||
+ cornerVector.getY() * s.cy().getX()
|
||||
+ cornerVector.getZ() * s.cz().getX();
|
||||
fy =
|
||||
-cornerVector.getX() * s.cx().getY()
|
||||
+ cornerVector.getY() * s.cy().getY()
|
||||
+ cornerVector.getZ() * s.cz().getY();
|
||||
fz =
|
||||
-cornerVector.getX() * s.cx().getZ()
|
||||
+ cornerVector.getY() * s.cy().getZ()
|
||||
+ cornerVector.getZ() * s.cz().getZ();
|
||||
b.frustum4(new Vector3D(fx, fy, fz).normalize());
|
||||
|
||||
fx = cornerVector.getX() * s.cx().getX() - cornerVector.getY() * s.cy().getX()
|
||||
+ cornerVector.getZ() * s.cz().getX();
|
||||
fy = cornerVector.getX() * s.cx().getY() - cornerVector.getY() * s.cy().getY()
|
||||
+ cornerVector.getZ() * s.cz().getY();
|
||||
fz = cornerVector.getX() * s.cx().getZ() - cornerVector.getY() * s.cy().getZ()
|
||||
+ cornerVector.getZ() * s.cz().getZ();
|
||||
fx =
|
||||
cornerVector.getX() * s.cx().getX()
|
||||
- cornerVector.getY() * s.cy().getX()
|
||||
+ cornerVector.getZ() * s.cz().getX();
|
||||
fy =
|
||||
cornerVector.getX() * s.cx().getY()
|
||||
- cornerVector.getY() * s.cy().getY()
|
||||
+ cornerVector.getZ() * s.cz().getY();
|
||||
fz =
|
||||
cornerVector.getX() * s.cx().getZ()
|
||||
- cornerVector.getY() * s.cy().getZ()
|
||||
+ cornerVector.getZ() * s.cz().getZ();
|
||||
b.frustum1(new Vector3D(fx, fy, fz).normalize());
|
||||
|
||||
fx = -cornerVector.getX() * s.cx().getX() - cornerVector.getY() * s.cy().getX()
|
||||
+ cornerVector.getZ() * s.cz().getX();
|
||||
fy = -cornerVector.getX() * s.cx().getY() - cornerVector.getY() * s.cy().getY()
|
||||
+ cornerVector.getZ() * s.cz().getY();
|
||||
fz = -cornerVector.getX() * s.cx().getZ() - cornerVector.getY() * s.cy().getZ()
|
||||
+ cornerVector.getZ() * s.cz().getZ();
|
||||
fx =
|
||||
-cornerVector.getX() * s.cx().getX()
|
||||
- cornerVector.getY() * s.cy().getX()
|
||||
+ cornerVector.getZ() * s.cz().getX();
|
||||
fy =
|
||||
-cornerVector.getX() * s.cx().getY()
|
||||
- cornerVector.getY() * s.cy().getY()
|
||||
+ cornerVector.getZ() * s.cz().getY();
|
||||
fz =
|
||||
-cornerVector.getX() * s.cx().getZ()
|
||||
- cornerVector.getY() * s.cy().getZ()
|
||||
+ cornerVector.getZ() * s.cz().getZ();
|
||||
b.frustum2(new Vector3D(fx, fy, fz).normalize());
|
||||
|
||||
return b.build();
|
||||
@@ -277,17 +335,15 @@ public abstract class SumFile {
|
||||
/**
|
||||
* Account for numbers of the form .1192696009D+03 rather than .1192696009E+03 (i.e. a D instead
|
||||
* of an E). This function replaces D's with E's.
|
||||
*
|
||||
* @param s
|
||||
* @return
|
||||
*
|
||||
* @param s String
|
||||
* @return input string with all instances of 'D' replaced with 'E'
|
||||
*/
|
||||
private static double parseFortranDouble(String s) {
|
||||
return Double.parseDouble(s.replace('D', 'E'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the sum file object to a string
|
||||
*/
|
||||
/** Write the sum file object to a string */
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -297,25 +353,52 @@ public abstract class SumFile {
|
||||
String.format("%6d%6d%6d%6d%56s\n", npx(), nln(), t1(), t2(), " NPX, NLN, THRSH "));
|
||||
sb.append(
|
||||
String.format("%20.10e%20.10e%20.10e%20s\n", mmfl(), px0(), ln0(), " MMFL, CTR "));
|
||||
sb.append(String.format("%20.10e%20.10e%20.10e%20s\n", scobj().getX(), scobj().getY(),
|
||||
scobj().getZ(), " SCOBJ "));
|
||||
sb.append(String.format("%20.10e%20.10e%20.10e%20s\n", cx().getX(), cx().getY(), cx().getZ(),
|
||||
" CX "));
|
||||
sb.append(String.format("%20.10e%20.10e%20.10e%20s\n", cy().getX(), cy().getY(), cy().getZ(),
|
||||
" CY "));
|
||||
sb.append(String.format("%20.10e%20.10e%20.10e%20s\n", cz().getX(), cz().getY(), cz().getZ(),
|
||||
" CZ "));
|
||||
sb.append(String.format("%20.10e%20.10e%20.10e%20s\n", sz().getX(), sz().getY(), sz().getZ(),
|
||||
" SZ "));
|
||||
sb.append(String.format("%10.4f%10.4f%10.4f%10.4f%10.4f%10.4f%20s\n", kmat1().getX(),
|
||||
kmat1().getY(), kmat1().getZ(), kmat2().getX(), kmat2().getY(), kmat2().getZ(),
|
||||
" K-MATRIX "));
|
||||
sb.append(String.format("%15.5f%15.5f%15.5f%15.5f%20s\n", distortion().get(0),
|
||||
distortion().get(1), distortion().get(2), distortion().get(3), " DISTORTION "));
|
||||
sb.append(String.format("%20.10e%20.10e%20.10e%20s\n", sig_vso().getX(), sig_vso().getY(),
|
||||
sig_vso().getZ(), " SIGMA_VSO "));
|
||||
sb.append(String.format("%20.10e%20.10e%20.10e%20s\n", sig_ptg().getX(), sig_ptg().getY(),
|
||||
sig_ptg().getZ(), " SIGMA_PTG "));
|
||||
sb.append(
|
||||
String.format(
|
||||
"%20.10e%20.10e%20.10e%20s\n",
|
||||
scobj().getX(), scobj().getY(), scobj().getZ(), " SCOBJ "));
|
||||
sb.append(
|
||||
String.format(
|
||||
"%20.10e%20.10e%20.10e%20s\n",
|
||||
cx().getX(), cx().getY(), cx().getZ(), " CX "));
|
||||
sb.append(
|
||||
String.format(
|
||||
"%20.10e%20.10e%20.10e%20s\n",
|
||||
cy().getX(), cy().getY(), cy().getZ(), " CY "));
|
||||
sb.append(
|
||||
String.format(
|
||||
"%20.10e%20.10e%20.10e%20s\n",
|
||||
cz().getX(), cz().getY(), cz().getZ(), " CZ "));
|
||||
sb.append(
|
||||
String.format(
|
||||
"%20.10e%20.10e%20.10e%20s\n",
|
||||
sz().getX(), sz().getY(), sz().getZ(), " SZ "));
|
||||
sb.append(
|
||||
String.format(
|
||||
"%10.4f%10.4f%10.4f%10.4f%10.4f%10.4f%20s\n",
|
||||
kmat1().getX(),
|
||||
kmat1().getY(),
|
||||
kmat1().getZ(),
|
||||
kmat2().getX(),
|
||||
kmat2().getY(),
|
||||
kmat2().getZ(),
|
||||
" K-MATRIX "));
|
||||
sb.append(
|
||||
String.format(
|
||||
"%15.5f%15.5f%15.5f%15.5f%20s\n",
|
||||
distortion().get(0),
|
||||
distortion().get(1),
|
||||
distortion().get(2),
|
||||
distortion().get(3),
|
||||
" DISTORTION "));
|
||||
sb.append(
|
||||
String.format(
|
||||
"%20.10e%20.10e%20.10e%20s\n",
|
||||
sig_vso().getX(), sig_vso().getY(), sig_vso().getZ(), " SIGMA_VSO "));
|
||||
sb.append(
|
||||
String.format(
|
||||
"%20.10e%20.10e%20.10e%20s\n",
|
||||
sig_ptg().getX(), sig_ptg().getY(), sig_ptg().getZ(), " SIGMA_PTG "));
|
||||
sb.append(String.format("%s\n", "LANDMARKS"));
|
||||
sb.append(String.format("%s\n", "LIMB FITS"));
|
||||
sb.append(String.format("%s\n", "END FILE"));
|
||||
@@ -324,95 +407,77 @@ public abstract class SumFile {
|
||||
}
|
||||
|
||||
/**
|
||||
* Boresight direction. This is the same as {@link #cz()}.
|
||||
*
|
||||
* @return
|
||||
* @return Boresight direction. This is the same as {@link #cz()}.
|
||||
*/
|
||||
public Vector3D boresight() {
|
||||
return cz();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sun direction. This is the same as {@link #sz()}.
|
||||
*
|
||||
* @return
|
||||
* @return Sun direction. This is the same as {@link #sz()}.
|
||||
*/
|
||||
public Vector3D sunDirection() {
|
||||
return sz();
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* return new Vector3D(1. / npx(), frustum3().subtract(frustum4()))
|
||||
* </pre>
|
||||
*
|
||||
* @return
|
||||
* <pre>new Vector3D(1. / npx(), frustum3().subtract(frustum4()))</pre>
|
||||
*/
|
||||
public Vector3D xPerPixel() {
|
||||
return new Vector3D(1. / npx(), frustum3().subtract(frustum4()));
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* return new Vector3D(1. / nln(), frustum3().subtract(frustum1()));
|
||||
* </pre>
|
||||
*
|
||||
* @return
|
||||
* <pre> new Vector3D(1. / nln(), frustum3().subtract(frustum1()));
|
||||
* </pre>
|
||||
*/
|
||||
public Vector3D yPerPixel() {
|
||||
return new Vector3D(1. / nln(), frustum3().subtract(frustum1()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Angular size per pixel in the X direction.
|
||||
*
|
||||
* @return
|
||||
* @return Angular size per pixel in the X direction calculated using
|
||||
* <pre>Vector3D.angle(frustum3(), frustum4()) / npx()</pre>
|
||||
*/
|
||||
public double horizontalResolution() {
|
||||
return Vector3D.angle(frustum3(), frustum4()) / npx();
|
||||
}
|
||||
|
||||
/**
|
||||
* Angular size per pixel in the Y direction.
|
||||
*
|
||||
* @return
|
||||
* @return Angular size per pixel in the Y direction calculated using
|
||||
* <pre>Vector3D.angle(frustum3(), frustum1()) / nln()</pre>
|
||||
*/
|
||||
public double verticalResolution() {
|
||||
return Vector3D.angle(frustum3(), frustum1()) / nln();
|
||||
}
|
||||
|
||||
/**
|
||||
* Height of the image in pixels.
|
||||
*
|
||||
* @return
|
||||
* @return Height of the image in pixels.
|
||||
*/
|
||||
public int imageHeight() {
|
||||
double kmatrix00 = Math.abs(kmat1().getX());
|
||||
double kmatrix11 = Math.abs(kmat2().getY());
|
||||
int imageHeight = nln();
|
||||
if (kmatrix00 > kmatrix11)
|
||||
imageHeight = (int) Math.round(nln() * (kmatrix00 / kmatrix11));
|
||||
if (kmatrix00 > kmatrix11) imageHeight = (int) Math.round(nln() * (kmatrix00 / kmatrix11));
|
||||
return imageHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Width of the image in pixels.
|
||||
*
|
||||
* @return
|
||||
* @return Width of the image in pixels.
|
||||
*/
|
||||
public int imageWidth() {
|
||||
double kmatrix00 = Math.abs(kmat1().getX());
|
||||
double kmatrix11 = Math.abs(kmat2().getY());
|
||||
int imageWidth = npx();
|
||||
if (kmatrix11 > kmatrix00)
|
||||
imageWidth = (int) Math.round(npx() * (kmatrix11 / kmatrix00));
|
||||
if (kmatrix11 > kmatrix00) imageWidth = (int) Math.round(npx() * (kmatrix11 / kmatrix00));
|
||||
return imageWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rotation to convert from body fixed coordinates to camera coordinates. {@link #cx()},
|
||||
* {@link #cx()}, {@link #cz()} are the rows of this matrix.
|
||||
*
|
||||
* @return rotation to convert from body fixed coordinates to camera coordinates. {@link #cx()},
|
||||
* {@link #cx()}, {@link #cz()} are the rows of this matrix.
|
||||
*/
|
||||
public Rotation getBodyFixedToCamera() {
|
||||
double[][] m = new double[3][];
|
||||
@@ -423,9 +488,8 @@ public abstract class SumFile {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param directions from the spacecraft, in the body fixed frame.
|
||||
* @return
|
||||
* @return list of booleans set to true if direction is in the field of view, or false if not
|
||||
* @throws SpiceException
|
||||
*/
|
||||
public List<Boolean> isInFOV(List<Vector3> directions) throws SpiceException {
|
||||
@@ -450,10 +514,10 @@ public abstract class SumFile {
|
||||
}
|
||||
|
||||
Path2D.Double shape = new Path2D.Double();
|
||||
shape.moveTo(xAxis.dot(points.get(0)), yAxis.dot(points.get(0)));
|
||||
shape.moveTo(xAxis.dot(points.getFirst()), yAxis.dot(points.getFirst()));
|
||||
for (int i = 1; i < points.size(); i++)
|
||||
shape.lineTo(xAxis.dot(points.get(i)), yAxis.dot(points.get(i)));
|
||||
shape.lineTo(xAxis.dot(points.get(0)), yAxis.dot(points.get(0)));
|
||||
shape.lineTo(xAxis.dot(points.getFirst()), yAxis.dot(points.getFirst()));
|
||||
|
||||
for (Vector3 direction : directions) {
|
||||
RayPlaneIntercept rpi =
|
||||
@@ -464,5 +528,4 @@ public abstract class SumFile {
|
||||
|
||||
return Collections.unmodifiableList(isInFOV);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ public class FibonacciSphere implements SphericalTessellation {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get statistics on the distances between each point and its closest neighbor
|
||||
*
|
||||
* @return statistics on the distances between each point and its closest neighbor
|
||||
*/
|
||||
@@ -181,9 +182,10 @@ public class FibonacciSphere implements SphericalTessellation {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the nearest point from the desired location.
|
||||
*
|
||||
* @param lv input location
|
||||
* @return key is distance to tile center in radians, value is tile index
|
||||
* @return key is distance in radians, value is point index
|
||||
*/
|
||||
public Map.Entry<Double, Long> getNearest(LatitudinalVector lv) {
|
||||
return getNearest(CoordConverters.convert(lv));
|
||||
@@ -198,7 +200,7 @@ public class FibonacciSphere implements SphericalTessellation {
|
||||
*
|
||||
*
|
||||
* @param ijk cartesian coordinates
|
||||
* @return key is distance to tile center in radians, value is tile index
|
||||
* @return key is distance in radians, value is point index
|
||||
*/
|
||||
public Map.Entry<Double, Long> getNearest(UnwritableVectorIJK ijk) {
|
||||
final long n = getNumTiles();
|
||||
@@ -248,7 +250,7 @@ public class FibonacciSphere implements SphericalTessellation {
|
||||
}
|
||||
}
|
||||
|
||||
return new AbstractMap.SimpleEntry<>(Math.sqrt(d), j);
|
||||
return new AbstractMap.SimpleEntry<Double, Long>(Math.sqrt(d), j);
|
||||
}
|
||||
|
||||
private MatrixIJ getLocalToGlobalTransform(UnwritableVectorIJK p) {
|
||||
@@ -282,8 +284,8 @@ public class FibonacciSphere implements SphericalTessellation {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param i tile index
|
||||
* @return distance to this tile's closest neighbor center
|
||||
* @param i point index
|
||||
* @return distance to this point's closest neighbor
|
||||
*/
|
||||
public Double getDist(int i) {
|
||||
return getClosestNeighborDistance().get(i);
|
||||
@@ -292,7 +294,7 @@ public class FibonacciSphere implements SphericalTessellation {
|
||||
/**
|
||||
*
|
||||
* @param lv input point
|
||||
* @return map of tile indices sorted by distance from the input point
|
||||
* @return map of points sorted by distance from the input point
|
||||
*/
|
||||
public NavigableMap<Double, Integer> getDistanceMap(LatitudinalVector lv) {
|
||||
UnwritableVectorIJK ijk = CoordConverters.convert(lv);
|
||||
|
||||
@@ -29,13 +29,12 @@ import picante.math.vectorspace.UnwritableVectorIJK;
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
/**
|
||||
* Implement a stereographic projection. This is package private so that there's no conflict with
|
||||
* anything in the cartography package. Based on Snyder (1987).
|
||||
* Implement a stereographic projection. Based on Snyder (1987).
|
||||
*
|
||||
* @author nairah1
|
||||
*
|
||||
*/
|
||||
class StereographicProjection {
|
||||
public class StereographicProjection {
|
||||
|
||||
private final double centerLat, centerLon;
|
||||
private final double sinCenterLat, cosCenterLat;
|
||||
@@ -47,7 +46,7 @@ class StereographicProjection {
|
||||
*
|
||||
* @param center projection center
|
||||
*/
|
||||
StereographicProjection(LatitudinalVector center) {
|
||||
public StereographicProjection(LatitudinalVector center) {
|
||||
this(1.0, center);
|
||||
}
|
||||
|
||||
@@ -58,7 +57,7 @@ class StereographicProjection {
|
||||
* @param R scale
|
||||
* @param center projection center
|
||||
*/
|
||||
StereographicProjection(double R, LatitudinalVector center) {
|
||||
public StereographicProjection(double R, LatitudinalVector center) {
|
||||
this.R = R;
|
||||
this.centerLat = center.getLatitude();
|
||||
this.centerLon = center.getLongitude();
|
||||
|
||||
@@ -23,13 +23,20 @@
|
||||
package terrasaur.utils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import picante.mechanics.EphemerisID;
|
||||
import picante.spice.fov.FOV;
|
||||
import picante.spice.fov.FOVFactory;
|
||||
import terrasaur.utils.spice.SpiceBundle;
|
||||
|
||||
public class SumFileTest {
|
||||
|
||||
@@ -38,19 +45,63 @@ public class SumFileTest {
|
||||
|
||||
File file = ResourceUtils.writeResourceToFile("/M605862153F5.SUM");
|
||||
|
||||
Assert.assertNotNull(file);
|
||||
List<String> lines = FileUtils.readLines(file, Charset.defaultCharset());
|
||||
|
||||
SumFile sumFile = SumFile.fromLines(lines);
|
||||
|
||||
assertEquals(Vector3D.angle(sumFile.frustum1(),
|
||||
new Vector3D(0.4395120989622179, 0.898199076601014, -0.008217886523401116)), 0, 1E-10);
|
||||
assertEquals(Vector3D.angle(sumFile.frustum2(),
|
||||
new Vector3D(0.43798867001719116, 0.8968954485311167, 0.06119215097329732)), 0, 1E-10);
|
||||
assertEquals(Vector3D.angle(sumFile.frustum3(),
|
||||
new Vector3D(0.3761137519676121, 0.926529032684429, -0.009077288896014253)), 0, 1E-10);
|
||||
assertEquals(Vector3D.angle(sumFile.frustum4(),
|
||||
new Vector3D(0.37459032302258627, 0.9252254046145307, 0.060332748600675515)), 0, 1E-10);
|
||||
|
||||
assertEquals(
|
||||
0,
|
||||
Vector3D.angle(
|
||||
sumFile.frustum1(),
|
||||
new Vector3D(0.4395120989622179, 0.898199076601014, -0.008217886523401116)),
|
||||
1E-10);
|
||||
assertEquals(
|
||||
0,
|
||||
Vector3D.angle(
|
||||
sumFile.frustum2(),
|
||||
new Vector3D(0.43798867001719116, 0.8968954485311167, 0.06119215097329732)),
|
||||
1E-10);
|
||||
assertEquals(
|
||||
0,
|
||||
Vector3D.angle(
|
||||
sumFile.frustum3(),
|
||||
new Vector3D(0.3761137519676121, 0.926529032684429, -0.009077288896014253)),
|
||||
1E-10);
|
||||
assertEquals(
|
||||
0,
|
||||
Vector3D.angle(
|
||||
sumFile.frustum4(),
|
||||
new Vector3D(0.37459032302258627, 0.9252254046145307, 0.060332748600675515)),
|
||||
1E-10);
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testSPICE() {
|
||||
SumFile sumFile1 =
|
||||
SumFile.fromFile(
|
||||
new File(
|
||||
"/project/sis/users/nairah1/SBCLT/tickets/52-create-sbmt-structure-file/spice/D717505942G0.SUM"));
|
||||
|
||||
SpiceBundle bundle =
|
||||
new SpiceBundle.Builder()
|
||||
.addMetakernels(List.of("/project/dart/data/SPICE/dra/mk/d520_v02_N0066.tm"))
|
||||
.build();
|
||||
EphemerisID observer = bundle.getObject("DART");
|
||||
EphemerisID target = bundle.getObject("DIMORPHOS");
|
||||
FOV fov = new FOVFactory(bundle.getKernelPool()).create(-135101);
|
||||
|
||||
double t = bundle.getTimeConversion().utcStringToTDB("2022 SEP 26 23:14:23.328");
|
||||
|
||||
SumFile sumFile2 =
|
||||
SumFile.fromFile(
|
||||
new File(
|
||||
"/project/sis/users/nairah1/SBCLT/tickets/52-create-sbmt-structure-file/spice/D717506133G0.SUM"));
|
||||
SumFile sumFile3 = sumFile1.fromSpice(bundle, observer, target, fov.getFrameID(), t);
|
||||
|
||||
System.out.println(sumFile1);
|
||||
System.out.println(sumFile2);
|
||||
System.out.println(sumFile3);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user