10#include <QRegularExpression>
11#include <QScopedPointer>
14#include <QXmlStreamWriter>
16#include <geos/geom/MultiPolygon.h>
17#include <geos/io/WKTReader.h>
18#include <geos/io/WKTWriter.h>
21#include "CameraFactory.h"
22#include "ControlPoint.h"
24#include "CubeAttribute.h"
25#include "DisplayProperties.h"
28#include "IException.h"
29#include "ImagePolygon.h"
31#include "PolygonTools.h"
33#include "ProjectionFactory.h"
34#include "SerialNumber.h"
35#include "ShapeDisplayProperties.h"
94 void Shape::initMemberData() {
102 m_aspectRatio = Null;
104 m_lineResolution = Null;
105 m_sampleResolution = Null;
108 m_projectionName =
"";
109 m_pixelResolution = Null;
114 void Shape::initShape() {
117 m_id =
new QUuid(QUuid::createUuid());
120 m_radiusSource = ControlPoint::RadiusSource::None;
122 if (
cube()->hasTable(
"ShapeModelStatistics")) {
123 m_surfacePointSource = ControlPoint::SurfacePointSource::Basemap;
124 m_radiusSource = ControlPoint::RadiusSource::DEM;
131 m_surfacePointSource = ControlPoint::SurfacePointSource::Basemap;
132 m_radiusSource = ControlPoint::RadiusSource::Ellipsoid;
133 m_shapeType = Basemap;
135 catch (IException &) {
139 m_surfacePointSource = ControlPoint::SurfacePointSource::Reference;
141 PvlGroup kernels =
cube()->
group(
"Kernels");
142 if (kernels.hasKeyword(
"ShapeModel")) {
143 QString shapeFile = kernels[
"ShapeModel"];
144 if (shapeFile.contains(
"dem")) {
145 m_radiusSource = ControlPoint::RadiusSource::DEM;
148 m_radiusSource = ControlPoint::RadiusSource::Ellipsoid;
151 m_shapeType = Unprojected;
153 catch (IException &e) {
154 m_surfacePointSource = ControlPoint::SurfacePointSource::None;
155 m_radiusSource = ControlPoint::RadiusSource::None;
156 m_shapeType = Unknown;
157 QString message =
"Cannot create either Camera or Projections "
159 "Check the validity of the cube labels. The cube must either be projected or "
160 " run through spiceinit.";
161 throw IException(e, IException::Io, message, _FILEINFO_);
167 if (m_shapeType == Unprojected) {
170 else if (m_shapeType == Basemap || m_shapeType == Dem) {
172 if (m_shapeType == Dem) {
177 catch (IException &e) {
178 QString message =
"Cannot initialize the camera, map or dem statistics for this shape file [" +
180 "cube must either be projected or run through spiceinit. \n";
181 message += e.toString();
182 QMessageBox::warning((QWidget *) parent(),
"Warning", message);
186 initQuickFootprint();
188 catch (IException &e) {
194 ControlPoint::SurfacePointSource::Source Shape::surfacePointSource() {
195 return m_surfacePointSource;
199 ControlPoint::RadiusSource::Source Shape::radiusSource() {
200 return m_radiusSource;
204 Shape::ShapeType Shape::shapeType() {
222 QString pvlFileName = ((IString)pvl[
"FileName"][0]).ToQt();
224 throw IException(IException::Unknown,
225 tr(
"Tried to load Shape [%1] with properties/information from [%2].")
232 if (pvl.hasKeyword(
"ID")) {
233 QByteArray hexValues(pvl[
"ID"][0].toLatin1());
234 QDataStream valuesStream(QByteArray::fromHex(hexValues));
235 valuesStream >> *
m_id;
254 PvlObject output(
"Shape");
260 dataBuffer.open(QIODevice::ReadWrite);
262 QDataStream idStream(&dataBuffer);
267 output += PvlKeyword(
"ID", QString(dataBuffer.data().toHex()));
288 QString blobType = example.Type();
289 QString blobName = example.Name();
291 Pvl &labels = *
m_cube->label();
293 for (
int i = 0; i < labels.objects(); i++) {
294 PvlObject &obj = labels.object(i);
296 if (obj.isNamed(blobType) && obj.hasKeyword(
"Name") && obj[
"Name"][0] == blobName)
314 catch (IException &e) {
315 throw IException(e, IException::Programmer,
"Cube cannot be created", _FILEINFO_);
388 *
m_id = QUuid(QString(
"{%1}").arg(
id));
409 initQuickFootprint();
411 catch (IException &e) {
415 catch(IException &e) {
416 IString msg =
"Could not read the footprint from cube [" +
418 "sure footprintinit has been run";
419 throw IException(e, IException::Io, msg, _FILEINFO_);
435 return m_aspectRatio;
445 return m_id->toString().remove(QRegularExpression(
"[{}]"));
467 return m_emissionAngle;
478 return m_incidenceAngle;
489 return m_lineResolution;
500 return m_localRadius;
511 return m_northAzimuth;
533 return m_sampleResolution;
541 if (FileName(newProjectRoot) != FileName(project->
projectRoot())) {
547 QScopedPointer<Cube> newExternalLabel(
557 FileName newDnFileName = newExternalLabelFileName.setExtension(
"cub");
562 newExternalLabel->relocateDnData(newDnFileName.name());
581 throw IException(IException::Io,
582 tr(
"Could not remove file [%1]").arg(
m_fileName),
587 FileName cubFile = FileName(
m_fileName).setExtension(
"cub");
588 if (!QFile::remove(cubFile.expanded())) {
589 throw IException(IException::Io,
590 tr(
"Could not remove file [%1]").arg(
m_fileName),
612 original.dir().dirName() +
"/" + original.name());
624 QMutexLocker lock(cameraMutex);
630 if (sampleStepSize <= 0) sampleStepSize = 1;
633 if (lineStepSize <= 0) lineStepSize = 1;
635 imgPoly.
Create(*
cube(), sampleStepSize, lineStepSize);
637 IException e = IException(IException::User,
638 tr(
"Warning: Polygon re-calculated for [%1] which can be very slow")
651 bool hasCamStats =
false;
654 for (
int i = 0; !hasCamStats && i < label.objects(); i++) {
655 PvlObject &obj = label.object(i);
658 if (obj.name() ==
"Table") {
659 if (obj[
"Name"][0] ==
"CameraStatistics") {
664 catch (IException &e) {
670 Table camStatsTable(
"CameraStatistics",
m_fileName, label);
672 int numRecords = camStatsTable.Records();
673 for (
int recordIndex = 0; recordIndex < numRecords; recordIndex++) {
674 TableRecord &record = camStatsTable[recordIndex];
678 QString recordName((QString)record[
"Name"]);
679 double avgValue = (double)record[
"Average"];
681 if (recordName ==
"AspectRatio") {
682 m_aspectRatio = avgValue;
684 else if (recordName ==
"Resolution") {
685 m_resolution = avgValue;
687 else if (recordName ==
"EmissionAngle") {
690 else if (recordName ==
"IncidenceAngle") {
693 else if (recordName ==
"LineResolution") {
694 m_lineResolution = avgValue;
696 else if (recordName ==
"LocalRadius") {
699 else if (recordName ==
"NorthAzimuth") {
702 else if (recordName ==
"PhaseAngle") {
705 else if (recordName ==
"SampleResolution") {
706 m_sampleResolution = avgValue;
711 for (
int i = 0; i < label.objects(); i++) {
712 PvlObject &obj = label.object(i);
714 if (obj.hasGroup(
"Instrument")) {
715 PvlGroup instGroup = obj.findGroup(
"Instrument");
717 if (instGroup.hasKeyword(
"SpacecraftName"))
720 if (instGroup.hasKeyword(
"InstrumentId"))
724 catch (IException &e) {
731 void Shape::initMapStats() {
734 for (
int i = 0; i < label.objects(); i++) {
735 PvlObject &obj = label.object(i);
737 if (obj.hasGroup(
"Instrument")) {
738 PvlGroup instGroup = obj.findGroup(
"Instrument");
740 if (instGroup.hasKeyword(
"SpacecraftName"))
743 if (instGroup.hasKeyword(
"InstrumentId"))
747 if (obj.hasGroup(
"Mapping")) {
748 PvlGroup mapGroup = obj.findGroup(
"Mapping");
750 if (mapGroup.hasKeyword(
"TargetName"))
751 m_targetName = obj.findGroup(
"Mapping")[
"TargetName"][0];
753 if (mapGroup.hasKeyword(
"ProjectionName"))
754 m_projectionName = obj.findGroup(
"Mapping")[
"ProjectionName"][0];
756 if (mapGroup.hasKeyword(
"CenterLongitude"))
757 m_centerLongitude = Longitude(toDouble(obj.findGroup(
"Mapping")[
"CenterLongitude"][0]),
760 if (mapGroup.hasKeyword(
"CenterLatitude"))
761 m_centerLatitude = Latitude(toDouble(obj.findGroup(
"Mapping")[
"CenterLatitude"][0]),
764 if (mapGroup.hasKeyword(
"MinimumLatitude"))
765 m_minimumLatitude = Latitude(toDouble(obj.findGroup(
"Mapping")[
"MinimumLatitude"][0]),
768 if (mapGroup.hasKeyword(
"MaximumLatitude"))
769 m_maximumLatitude = Latitude(toDouble(obj.findGroup(
"Mapping")[
"MaximumLatitude"][0]),
772 if (mapGroup.hasKeyword(
"MinimumLongitude"))
773 m_minimumLongitude = Longitude(toDouble(obj.findGroup(
"Mapping")[
"MinimumLongitude"][0]),
776 if (mapGroup.hasKeyword(
"MaximumLongitude"))
777 m_maximumLongitude = Longitude(toDouble(obj.findGroup(
"Mapping")[
"MaximumLongitude"][0]),
780 if (mapGroup.hasKeyword(
"PixelResolution"))
781 m_pixelResolution = obj.findGroup(
"Mapping")[
"PixelResolution"];
783 if (mapGroup.hasKeyword(
"Scale"))
784 m_scale = obj.findGroup(
"Mapping")[
"Scale"];
787 catch (IException &e) {
794 void Shape::initDemStats() {
800 void Shape::initQuickFootprint() {
819 stream.writeStartElement(
"shape");
821 stream.writeAttribute(
"id",
m_id->toString());
822 stream.writeAttribute(
"fileName", FileName(
m_fileName).name());
826 if (m_shapeType == Unprojected) {
827 type =
"Unprojected";
829 else if (m_shapeType == Basemap) {
835 stream.writeAttribute(
"shapeType", type);
836 stream.writeAttribute(
"surfacePointSource",
838 stream.writeAttribute(
"radiusSource",
841 if (m_shapeType == Unprojected) {
845 if (!IsSpecial(m_aspectRatio)) {
846 stream.writeAttribute(
"aspectRatio", IString(m_aspectRatio).ToQt());
849 if (!IsSpecial(m_resolution)) {
850 stream.writeAttribute(
"resolution", IString(m_resolution).ToQt());
853 if (m_emissionAngle.isValid()) {
854 stream.writeAttribute(
"emissionAngle", IString(m_emissionAngle.radians()).ToQt());
857 if (m_incidenceAngle.isValid()) {
858 stream.writeAttribute(
"incidenceAngle", IString(m_incidenceAngle.radians()).ToQt());
861 if (!IsSpecial(m_lineResolution)) {
862 stream.writeAttribute(
"lineResolution", IString(m_lineResolution).ToQt());
865 if (m_localRadius.isValid()) {
866 stream.writeAttribute(
"localRadius", IString(m_localRadius.meters()).ToQt());
869 if (m_northAzimuth.isValid()) {
870 stream.writeAttribute(
"northAzimuth", IString(m_northAzimuth.radians()).ToQt());
873 if (m_phaseAngle.isValid()) {
874 stream.writeAttribute(
"phaseAngle", IString(m_phaseAngle.radians()).ToQt());
877 if (!IsSpecial(m_sampleResolution)) {
878 stream.writeAttribute(
"sampleResolution", IString(m_sampleResolution).ToQt());
881 else if (m_shapeType == Basemap) {
884 else if (m_shapeType == Dem) {
889 stream.writeStartElement(
"footprint");
891 geos::io::WKTWriter wktWriter;
892 stream.writeCharacters(QString::fromStdString(wktWriter.write(
m_footprint)));
894 stream.writeEndElement();
899 stream.writeEndElement();
Defines an angle and provides unit conversions.
@ Degrees
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
static Camera * Create(Cube &cube)
Creates a Camera object using Pvl Specifications.
static QString RadiusSourceToString(RadiusSource::Source source)
Obtain a string representation of a given RadiusSource.
static QString SurfacePointSourceToString(SurfacePointSource::Source source)
Obtain a string representation of a given SurfacePointSource.
Manipulate and parse attributes of output cube filenames.
IO Handler for Isis Cubes.
ImagePolygon readFootprint() const
Read the footprint polygon for the Cube.
FileName externalCubeFileName() const
If this is an external cube label file, this will give you the cube dn file that this label reference...
@ ExternalLabel
The label is pointing to an external DN file - the label is also external to the data.
PvlGroup & group(const QString &group) const
Read a group from the cube into a Label.
Cube * copy(FileName newFile, const CubeAttributeOutput &newFileAttributes)
Copies the cube to the new fileName.
LabelAttachment labelsAttached() const
Test if labels are attached.
virtual QString fileName() const
Returns the opened cube's filename.
Pvl * label() const
Returns a pointer to the IsisLabel object associated with the cube.
QString displayName() const
Returns the display name.
PvlObject toPvl() const
Convert to Pvl for project files.
Distance measurement, usually in meters.
@ Meters
The distance is being specified in meters.
Create cube polygons, read/write polygons to blobs.
void Create(Cube &cube, int sinc=1, int linc=1, int ss=1, int sl=1, int ns=0, int nl=0, int band=1, bool increasePrecision=false)
Create a Polygon from given cube.
geos::geom::MultiPolygon * Polys()
Return a geos Multipolygon.
Blob toBlob() const
Serialize the ImagePolygon to a Blob.
The main project for ipce.
static QString shapeDataRoot(QString projectRoot)
Appends the root directory name 'shapes' to the project .
QString projectRoot() const
Get the top-level folder of the project.
static Isis::Projection * CreateFromCube(Isis::Cube &cube)
This method is a helper method.
static QString Compose(Pvl &label, bool def2filename=false)
Compose a SerialNumber from a PVL.
This is the GUI communication mechanism for shape objects.
~Shape()
Clean up this shape.
geos::geom::MultiPolygon * createFootprint(QMutex *cameraMutex)
Calculate a footprint for an Shape using the camera or projection information.
Shape(QString shapeFileName, QObject *parent=0)
Create an Shape from a cube file on disk.
void closeCube()
Cleans up the Cube *.
PvlObject toPvl() const
Convert this Shape to PVL.
bool isFootprintable() const
Test to see if it's possible to create a footprint from this shape.
SpiceInt * m_bodyCode
The NaifBodyCode value, if it exists in the labels.
Distance localRadius() const
Get the local radius of this shape, as calculated and attached by camstats.
Angle phaseAngle() const
Get the phase angle of this shape, as calculated and attached by camstats.
void updateFileName(Project *)
Change the on-disk file name for this cube to be where the shape ought to be in the given project.
double resolution() const
Get the resolution of this shape, as calculated and attached by camstats.
QString serialNumber()
Get the serial number.
QString m_spacecraftName
Spacecraft name associated with this Shape.
double sampleResolution() const
Get the sample resolution of this shape, as calculated and attached by camstats.
ShapeDisplayProperties * displayProperties()
Get the display (GUI) properties (information) associated with this shape.
double lineResolution() const
Get the line resolution of this shape, as calculated and attached by camstats.
void deleteFromDisk()
Delete the shape data from disk.
Angle emissionAngle() const
Get the emission angle of this shape, as calculated and attached by camstats.
QString id() const
Get a unique, identifying string associated with this shape.
void save(QXmlStreamWriter &stream, const Project *project, FileName newProjectRoot) const
Output format:
double aspectRatio() const
Get the aspect ratio of this shape, as calculated and attached by camstats.
QString fileName() const
Get the file name of the cube that this shape represents.
Cube * m_cube
The cube associated with this Shape.
geos::geom::MultiPolygon * m_footprint
A 0-360 ocentric lon,lat degrees footprint of this Shape.
Cube * cube()
Get the Cube * associated with this display property.
Angle incidenceAngle() const
Get the incidence angle of this shape, as calculated and attached by camstats.
void copyToNewProjectRoot(const Project *project, FileName newProjectRoot)
Copy the cub/ecub files associated with this shape into the new project.
QUuid * m_id
A unique ID for this Shape (useful for others to reference this Shape when saving to disk).
QString m_serialNumber
This will always be simply the filename and is created on construction.
QString m_instrumentId
Instrument id associated with this Shape.
bool initFootprint(QMutex *cameraMutex)
Calculate a footprint for this shape.
void fromPvl(const PvlObject &pvl)
Read the shape settings from a Pvl.
void setId(QString id)
Override the automatically generated ID with the given ID.
Angle northAzimuth() const
Get the north azimuth of this shape, as calculated and attached by camstats.
geos::geom::MultiPolygon * footprint()
Get the footprint of this shape (if available).
QString m_fileName
The on-disk file name of the cube associated with this Shape.
ShapeDisplayProperties * m_displayProperties
The GUI information for how this Shape ought to be displayed.
This is free and unencumbered software released into the public domain.