use of org.hipparchus.geometry.spherical.twod.SphericalPolygonsSet in project Orekit by CS-SI.
the class DOPComputation method sample.
/**
* Mesh an area of interest into a grid of geodetic points.
*
* @param zone the area to mesh
* @param meshSize the size of the square meshes as a distance on the Earth surface (in meters)
* @return a list of geodetic points sampling the zone of interest
* @throws OrekitException if the area cannot be meshed
*/
private List<List<GeodeticPoint>> sample(final OneAxisEllipsoid shape, final List<GeodeticPoint> zone, final double meshSize) throws OrekitException {
// Convert the area into a SphericalPolygonsSet
final SphericalPolygonsSet sps = computeSphericalPolygonsSet(zone);
// Build the tesselator
final TileAiming aiming = new ConstantAzimuthAiming(shape, 0.);
final EllipsoidTessellator tessellator = new EllipsoidTessellator(shape, aiming, 4);
// Returns the sampled area as a grid of geodetic points
return tessellator.sample(sps, meshSize, meshSize);
}
use of org.hipparchus.geometry.spherical.twod.SphericalPolygonsSet in project Orekit by CS-SI.
the class EllipsoidTessellator method extractTiles.
/**
* Extract tiles from a mesh.
* @param mesh mesh from which tiles should be extracted
* @param zone zone covered by the mesh
* @param lengthOverlap overlap between adjacent tiles
* @param widthOverlap overlap between adjacent tiles
* @param truncateLastWidth true if we can reduce last tile width
* @param truncateLastLength true if we can reduce last tile length
* @return extracted tiles
* @exception OrekitException if tile direction cannot be computed
*/
private List<Tile> extractTiles(final Mesh mesh, final SphericalPolygonsSet zone, final double lengthOverlap, final double widthOverlap, final boolean truncateLastWidth, final boolean truncateLastLength) throws OrekitException {
final List<Tile> tiles = new ArrayList<Tile>();
final List<RangePair> rangePairs = new ArrayList<RangePair>();
final int minAcross = mesh.getMinAcrossIndex();
final int maxAcross = mesh.getMaxAcrossIndex();
for (Range acrossPair : nodesIndices(minAcross, maxAcross, truncateLastWidth)) {
int minAlong = mesh.getMaxAlongIndex() + 1;
int maxAlong = mesh.getMinAlongIndex() - 1;
for (int c = acrossPair.lower; c <= acrossPair.upper; ++c) {
minAlong = FastMath.min(minAlong, mesh.getMinAlongIndex(c));
maxAlong = FastMath.max(maxAlong, mesh.getMaxAlongIndex(c));
}
for (Range alongPair : nodesIndices(minAlong, maxAlong, truncateLastLength)) {
// get the base vertex nodes
final Mesh.Node node0 = mesh.addNode(alongPair.lower, acrossPair.lower);
final Mesh.Node node1 = mesh.addNode(alongPair.upper, acrossPair.lower);
final Mesh.Node node2 = mesh.addNode(alongPair.upper, acrossPair.upper);
final Mesh.Node node3 = mesh.addNode(alongPair.lower, acrossPair.upper);
// apply tile overlap
final S2Point s2p0 = node0.move(new Vector3D(-0.5 * lengthOverlap, node0.getAlong(), -0.5 * widthOverlap, node0.getAcross()));
final S2Point s2p1 = node1.move(new Vector3D(+0.5 * lengthOverlap, node1.getAlong(), -0.5 * widthOverlap, node1.getAcross()));
final S2Point s2p2 = node2.move(new Vector3D(+0.5 * lengthOverlap, node2.getAlong(), +0.5 * widthOverlap, node2.getAcross()));
final S2Point s2p3 = node3.move(new Vector3D(-0.5 * lengthOverlap, node2.getAlong(), +0.5 * widthOverlap, node2.getAcross()));
// create a quadrilateral region corresponding to the candidate tile
final SphericalPolygonsSet quadrilateral = new SphericalPolygonsSet(zone.getTolerance(), s2p0, s2p1, s2p2, s2p3);
if (!new RegionFactory<Sphere2D>().intersection(zone.copySelf(), quadrilateral).isEmpty()) {
// the tile does cover part of the zone, it contributes to the tessellation
tiles.add(new Tile(toGeodetic(s2p0), toGeodetic(s2p1), toGeodetic(s2p2), toGeodetic(s2p3)));
rangePairs.add(new RangePair(acrossPair, alongPair));
}
}
}
// neighboring tile as they share some nodes that will be enabled here
for (final RangePair rangePair : rangePairs) {
for (int c = rangePair.across.lower; c < rangePair.across.upper; ++c) {
mesh.addNode(rangePair.along.lower, c + 1).setEnabled();
mesh.addNode(rangePair.along.upper, c).setEnabled();
}
for (int l = rangePair.along.lower; l < rangePair.along.upper; ++l) {
mesh.addNode(l, rangePair.across.lower).setEnabled();
mesh.addNode(l + 1, rangePair.across.upper).setEnabled();
}
}
return tiles;
}
use of org.hipparchus.geometry.spherical.twod.SphericalPolygonsSet in project Orekit by CS-SI.
the class EllipsoidTessellator method tessellate.
/**
* Tessellate a zone of interest into tiles.
* <p>
* The created tiles will completely cover the zone of interest.
* </p>
* <p>
* The distance between a vertex at a tile corner and the vertex at the same corner
* in the next vertex are computed by subtracting the overlap width (resp. overlap length)
* from the full width (resp. full length). If for example the full width is specified to
* be 55 km and the overlap in width is specified to be +5 km, successive tiles would span
* as follows:
* </p>
* <ul>
* <li>tile 1 covering from 0 km to 55 km</li>
* <li>tile 2 covering from 50 km to 105 km</li>
* <li>tile 3 covering from 100 km to 155 km</li>
* <li>...</li>
* </ul>
* <p>
* In order to achieve the same 50 km step but using a 5 km gap instead of an overlap, one would
* need to specify the full width to be 45 km and the overlap to be -5 km. With these settings,
* successive tiles would span as follows:
* </p>
* <ul>
* <li>tile 1 covering from 0 km to 45 km</li>
* <li>tile 2 covering from 50 km to 95 km</li>
* <li>tile 3 covering from 100 km to 155 km</li>
* <li>...</li>
* </ul>
* @param zone zone of interest to tessellate
* @param fullWidth full tiles width as a distance on surface, including overlap (in meters)
* @param fullLength full tiles length as a distance on surface, including overlap (in meters)
* @param widthOverlap overlap between adjacent tiles (in meters), if negative the tiles
* will have a gap between each other instead of an overlap
* @param lengthOverlap overlap between adjacent tiles (in meters), if negative the tiles
* will have a gap between each other instead of an overlap
* @param truncateLastWidth if true, the first tiles strip will be started as close as
* possible to the zone of interest, and the last tiles strip will have its width reduced
* to also remain close to the zone of interest; if false all tiles strip will have the
* same {@code fullWidth} and they will be balanced around zone of interest
* @param truncateLastLength if true, the first tile in each strip will be started as close as
* possible to the zone of interest, and the last tile in each strip will have its length reduced
* to also remain close to the zone of interest; if false all tiles in each strip will have the
* same {@code fullLength} and they will be balanced around zone of interest
* @return a list of lists of tiles covering the zone of interest,
* each sub-list corresponding to a part not connected to the other
* parts (for example for islands)
* @exception OrekitException if the zone cannot be tessellated
*/
public List<List<Tile>> tessellate(final SphericalPolygonsSet zone, final double fullWidth, final double fullLength, final double widthOverlap, final double lengthOverlap, final boolean truncateLastWidth, final boolean truncateLastLength) throws OrekitException {
final double splitWidth = (fullWidth - widthOverlap) / quantization;
final double splitLength = (fullLength - lengthOverlap) / quantization;
final Map<Mesh, List<Tile>> map = new IdentityHashMap<Mesh, List<Tile>>();
final RegionFactory<Sphere2D> factory = new RegionFactory<Sphere2D>();
SphericalPolygonsSet remaining = (SphericalPolygonsSet) zone.copySelf();
S2Point inside = getInsidePoint(remaining);
while (inside != null) {
// find a mesh covering at least one connected part of the zone
final List<Mesh.Node> mergingSeeds = new ArrayList<Mesh.Node>();
Mesh mesh = new Mesh(ellipsoid, zone, aiming, splitLength, splitWidth, inside);
mergingSeeds.add(mesh.getNode(0, 0));
List<Tile> tiles = null;
while (!mergingSeeds.isEmpty()) {
// expand the mesh around the seed
neighborExpandMesh(mesh, mergingSeeds, zone);
// extract the tiles from the mesh
// this further expands the mesh so tiles dimensions are multiples of quantization,
// hence it must be performed here before checking meshes independence
tiles = extractTiles(mesh, zone, lengthOverlap, widthOverlap, truncateLastWidth, truncateLastLength);
// check the mesh is independent from existing meshes
mergingSeeds.clear();
for (final Map.Entry<Mesh, List<Tile>> entry : map.entrySet()) {
if (!factory.intersection(mesh.getCoverage(), entry.getKey().getCoverage()).isEmpty()) {
// the meshes are not independent, they intersect each other!
// merge the two meshes together
mesh = mergeMeshes(mesh, entry.getKey(), mergingSeeds);
map.remove(entry.getKey());
break;
}
}
}
// remove the part of the zone covered by the mesh
remaining = (SphericalPolygonsSet) factory.difference(remaining, mesh.getCoverage());
inside = getInsidePoint(remaining);
map.put(mesh, tiles);
}
// concatenate the lists from the independent meshes
final List<List<Tile>> tilesLists = new ArrayList<List<Tile>>(map.size());
for (final Map.Entry<Mesh, List<Tile>> entry : map.entrySet()) {
tilesLists.add(entry.getValue());
}
return tilesLists;
}
use of org.hipparchus.geometry.spherical.twod.SphericalPolygonsSet in project Orekit by CS-SI.
the class EllipsoidTessellator method sample.
/**
* Sample a zone of interest into a grid sample of {@link GeodeticPoint geodetic points}.
* <p>
* The created points will be entirely within the zone of interest.
* </p>
* @param zone zone of interest to sample
* @param width grid sample cells width as a distance on surface (in meters)
* @param length grid sample cells length as a distance on surface (in meters)
* @return a list of lists of points sampling the zone of interest,
* each sub-list corresponding to a part not connected to the other
* parts (for example for islands)
* @exception OrekitException if the zone cannot be sampled
*/
public List<List<GeodeticPoint>> sample(final SphericalPolygonsSet zone, final double width, final double length) throws OrekitException {
final double splitWidth = width / quantization;
final double splitLength = length / quantization;
final Map<Mesh, List<GeodeticPoint>> map = new IdentityHashMap<Mesh, List<GeodeticPoint>>();
final RegionFactory<Sphere2D> factory = new RegionFactory<Sphere2D>();
SphericalPolygonsSet remaining = (SphericalPolygonsSet) zone.copySelf();
S2Point inside = getInsidePoint(remaining);
while (inside != null) {
// find a mesh covering at least one connected part of the zone
final List<Mesh.Node> mergingSeeds = new ArrayList<Mesh.Node>();
Mesh mesh = new Mesh(ellipsoid, zone, aiming, splitLength, splitWidth, inside);
mergingSeeds.add(mesh.getNode(0, 0));
List<GeodeticPoint> sample = null;
while (!mergingSeeds.isEmpty()) {
// expand the mesh around the seed
neighborExpandMesh(mesh, mergingSeeds, zone);
// extract the sample from the mesh
// this further expands the mesh so sample cells dimensions are multiples of quantization,
// hence it must be performed here before checking meshes independence
sample = extractSample(mesh, zone);
// check the mesh is independent from existing meshes
mergingSeeds.clear();
for (final Map.Entry<Mesh, List<GeodeticPoint>> entry : map.entrySet()) {
if (!factory.intersection(mesh.getCoverage(), entry.getKey().getCoverage()).isEmpty()) {
// the meshes are not independent, they intersect each other!
// merge the two meshes together
mesh = mergeMeshes(mesh, entry.getKey(), mergingSeeds);
map.remove(entry.getKey());
break;
}
}
}
// remove the part of the zone covered by the mesh
remaining = (SphericalPolygonsSet) factory.difference(remaining, mesh.getCoverage());
inside = getInsidePoint(remaining);
map.put(mesh, sample);
}
// concatenate the lists from the independent meshes
final List<List<GeodeticPoint>> sampleLists = new ArrayList<List<GeodeticPoint>>(map.size());
for (final Map.Entry<Mesh, List<GeodeticPoint>> entry : map.entrySet()) {
sampleLists.add(entry.getValue());
}
return sampleLists;
}
use of org.hipparchus.geometry.spherical.twod.SphericalPolygonsSet in project Orekit by CS-SI.
the class FieldOfView method buildDihedra.
/**
* Build a dihedra.
* @param factory factory for regions
* @param tolerance tolerance below which points are considered equal
* @param center Direction of the FOV center, in spacecraft frame
* @param axis FOV dihedral axis, in spacecraft frame
* @param halfAperture FOV dihedral half aperture angle,
* must be less than π/2, i.e. full dihedra must be smaller then
* an hemisphere
* @return dihedra
* @exception OrekitException if half aperture is larger than π/2
*/
private Region<Sphere2D> buildDihedra(final RegionFactory<Sphere2D> factory, final double tolerance, final Vector3D center, final Vector3D axis, final double halfAperture) throws OrekitException {
if (halfAperture > 0.5 * FastMath.PI) {
throw new OrekitException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, halfAperture, 0.0, 0.5 * FastMath.PI);
}
final Rotation r = new Rotation(axis, halfAperture, RotationConvention.VECTOR_OPERATOR);
final Vector3D normalCenterPlane = Vector3D.crossProduct(axis, center);
final Vector3D normalSidePlus = r.applyInverseTo(normalCenterPlane);
final Vector3D normalSideMinus = r.applyTo(normalCenterPlane.negate());
return factory.intersection(new SphericalPolygonsSet(normalSidePlus, tolerance), new SphericalPolygonsSet(normalSideMinus, tolerance));
}
Aggregations