20 package org.sleuthkit.autopsy.report.modules.kml;
23 import javax.swing.JPanel;
24 import org.openide.util.NbBundle;
29 import java.io.FileOutputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.nio.file.Path;
34 import java.nio.file.Paths;
35 import java.text.SimpleDateFormat;
36 import java.util.List;
37 import java.util.logging.Level;
38 import java.util.stream.Collectors;
39 import org.jdom2.Document;
40 import org.jdom2.Element;
41 import org.jdom2.Namespace;
42 import org.jdom2.output.Format;
43 import org.jdom2.output.XMLOutputter;
44 import org.jdom2.CDATA;
45 import org.openide.filesystems.FileUtil;
46 import org.openide.util.NbBundle.Messages;
61 import org.
sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
72 private static final String
REPORT_KML =
"ReportKML.kml";
73 private static final String
STYLESHEETS_PATH =
"/org/sleuthkit/autopsy/report/stylesheets/";
77 private final SimpleDateFormat
kmlDateFormat =
new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ssX");
95 RED(
"style.kml#redFeature"),
96 GREEN(
"style.kml#greenFeature"),
97 BLUE(
"style.kml#blueFeature"),
99 WHITE(
"style.kml#whiteFeature"),
118 if (instance == null) {
132 "KMLReport.failedToCompleteReport=Failed to complete report.",
133 "KMLReport.partialFailure=There was an error creating the report. Some items were not exported.",
134 "KMLReport.unableToExtractPhotos=Could not extract photo information.",
135 "KMLReport.exifPhotoError=Could not extract photos with EXIF metadata.",
136 "KMLReport.bookmarkError=Could not extract Bookmark information.",
137 "KMLReport.gpsBookmarkError=Could not get GPS Bookmarks from database.",
138 "KMLReport.locationError=Could not extract Last Known Location information.",
139 "KMLReport.locationDatabaseError=Could not get GPS Last Known Location from database.",
140 "KMLReport.gpsRouteError=Could not extract GPS Route information.",
141 "KMLReport.gpsRouteDatabaseError=Could not get GPS Routes from database.",
142 "KMLReport.gpsSearchDatabaseError=Could not get GPS Searches from database.",
143 "KMLReport.trackpointError=Could not extract Trackpoint information.",
144 "KMLReport.trackpointDatabaseError=Could not get GPS Trackpoints from database.",
145 "KMLReport.stylesheetError=Error placing KML stylesheet. The .KML file will not function properly.",
146 "KMLReport.kmlFileWriteError=Could not write the KML file.",
148 "KMLReport.errorGeneratingReport=Error adding {0} to case as a report.",
149 "KMLReport.unableToOpenCase=Exception while getting open case.",
150 "Waypoint_Bookmark_Display_String=GPS Bookmark",
151 "Waypoint_Last_Known_Display_String=GPS Last Known Location",
152 "Waypoint_EXIF_Display_String=EXIF Metadata With Location",
153 "Waypoint_Route_Point_Display_String=GPS Individual Route Point",
154 "Waypoint_Search_Display_String=GPS Search",
155 "Waypoint_Trackpoint_Display_String=GPS Trackpoint",
156 "Waypoint_Track_Display_String=GPS Track",
157 "Route_Details_Header=GPS Route",
158 "ReportBodyFile.ingestWarning.text=Ingest Warning message",
159 "Waypoint_Track_Point_Display_String=GPS Individual Track Point",
160 "Waypoint_Area_Point_Display_String=GPS Area Outline Point",
180 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
190 .collect(Collectors.toList());
192 }
catch (TskCoreException ex) {
193 logger.log(Level.SEVERE,
"Could not get the datasources from the case", ex);
202 progressPanel.
start();
203 progressPanel.
updateStatusLabel(NbBundle.getMessage(
this.getClass(),
"ReportKML.progress.querying"));
204 String kmlFileFullPath = baseReportDir +
REPORT_KML;
205 String errorMessage =
"";
209 progressPanel.
updateStatusLabel(NbBundle.getMessage(
this.getClass(),
"ReportKML.progress.loading"));
217 boolean entirelySuccessful = makeTracks(skCase);
218 if (!entirelySuccessful) {
220 errorMessage = Bundle.KMLReport_partialFailure();
222 entirelySuccessful = makeAreas(skCase);
223 if (!entirelySuccessful) {
225 errorMessage = Bundle.KMLReport_partialFailure();
228 addLocationsToReport(skCase, baseReportDir);
230 errorMessage = Bundle.KMLReport_failedToCompleteReport();
231 logger.log(Level.SEVERE, errorMessage, ex);
237 InputStream input = getClass().getResourceAsStream(STYLESHEETS_PATH + KML_STYLE_FILE);
238 OutputStream output =
new FileOutputStream(baseReportDir + KML_STYLE_FILE);
239 FileUtil.copy(input, output);
240 }
catch (IOException ex) {
241 errorMessage = Bundle.KMLReport_stylesheetError();
242 logger.log(Level.SEVERE, errorMessage, ex);
246 try (FileOutputStream writer =
new FileOutputStream(kmlFileFullPath)) {
247 XMLOutputter outputter =
new XMLOutputter(Format.getPrettyFormat());
248 outputter.output(kmlDocument, writer);
249 String prependedStatus =
"";
251 prependedStatus =
"Incomplete ";
254 NbBundle.getMessage(
this.getClass(),
"ReportKML.genReport.srcModuleName.text"),
255 prependedStatus + NbBundle.getMessage(
this.getClass(),
"ReportKML.genReport.reportName"));
256 }
catch (IOException ex) {
257 errorMessage = Bundle.KMLReport_kmlFileWriteError();
258 logger.log(Level.SEVERE, errorMessage, ex);
260 }
catch (TskCoreException ex) {
261 errorMessage = Bundle.KMLReport_errorGeneratingReport(kmlFileFullPath);
262 logger.log(Level.SEVERE, errorMessage, ex);
265 errorMessage = Bundle.KMLReport_unableToOpenCase();
266 logger.log(Level.SEVERE, errorMessage, ex);
270 progressPanel.
complete(result, errorMessage);
279 ns = Namespace.getNamespace(
"",
"http://www.opengis.net/kml/2.2");
281 Element kml =
new Element(
"kml", ns);
282 kml.addNamespaceDeclaration(Namespace.getNamespace(
"gx",
"http://www.google.com/kml/ext/2.2"));
283 kml.addNamespaceDeclaration(Namespace.getNamespace(
"kml",
"http://www.opengis.net/kml/2.2"));
284 kml.addNamespaceDeclaration(Namespace.getNamespace(
"atom",
"http://www.w3.org/2005/Atom"));
285 Document kmlDocument =
new Document(kml);
287 Element document =
new Element(
"Document", ns);
288 kml.addContent(document);
290 Element name =
new Element(
"name", ns);
293 document.addContent(name);
297 Element ingestwarning =
new Element(
"snippet", ns);
298 ingestwarning.addContent(NbBundle.getMessage(
this.getClass(),
"ReportBodyFile.ingestWarning.text"));
299 document.addContent(ingestwarning);
303 gpsExifMetadataFolder =
new Element(
"Folder", ns);
304 CDATA cdataExifMetadataFolder =
new CDATA(
"https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/camera-icon-16.png");
305 Element hrefExifMetadata =
new Element(
"href", ns).addContent(cdataExifMetadataFolder);
306 gpsExifMetadataFolder.addContent(
new Element(
"Icon", ns).addContent(hrefExifMetadata));
308 gpsBookmarksFolder =
new Element(
"Folder", ns);
309 CDATA cdataBookmarks =
new CDATA(
"https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gpsfav.png");
310 Element hrefBookmarks =
new Element(
"href", ns).addContent(cdataBookmarks);
311 gpsBookmarksFolder.addContent(
new Element(
"Icon", ns).addContent(hrefBookmarks));
313 gpsLastKnownLocationFolder =
new Element(
"Folder", ns);
314 CDATA cdataLastKnownLocation =
new CDATA(
"https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-lastlocation.png");
315 Element hrefLastKnownLocation =
new Element(
"href", ns).addContent(cdataLastKnownLocation);
316 gpsLastKnownLocationFolder.addContent(
new Element(
"Icon", ns).addContent(hrefLastKnownLocation));
318 gpsRouteFolder =
new Element(
"Folder", ns);
319 CDATA cdataRoute =
new CDATA(
"https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png");
320 Element hrefRoute =
new Element(
"href", ns).addContent(cdataRoute);
321 gpsRouteFolder.addContent(
new Element(
"Icon", ns).addContent(hrefRoute));
323 gpsSearchesFolder =
new Element(
"Folder", ns);
324 CDATA cdataSearches =
new CDATA(
"https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-search.png");
325 Element hrefSearches =
new Element(
"href", ns).addContent(cdataSearches);
326 gpsSearchesFolder.addContent(
new Element(
"Icon", ns).addContent(hrefSearches));
328 gpsTrackpointsFolder =
new Element(
"Folder", ns);
329 CDATA cdataTrackpoints =
new CDATA(
"https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png");
330 Element hrefTrackpoints =
new Element(
"href", ns).addContent(cdataTrackpoints);
331 gpsTrackpointsFolder.addContent(
new Element(
"Icon", ns).addContent(hrefTrackpoints));
333 gpsTracksFolder =
new Element(
"Folder", ns);
334 CDATA cdataTrack =
new CDATA(
"https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png");
335 Element hrefTrack =
new Element(
"href", ns).addContent(cdataTrack);
336 gpsTracksFolder.addContent(
new Element(
"Icon", ns).addContent(hrefTrack));
338 gpsAreasFolder =
new Element(
"Folder", ns);
339 CDATA cdataArea =
new CDATA(
"https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-area.png");
340 Element hrefArea =
new Element(
"href", ns).addContent(cdataArea);
341 gpsAreasFolder.addContent(
new Element(
"Icon", ns).addContent(hrefArea));
343 gpsExifMetadataFolder.addContent(
new Element(
"name", ns).addContent(
"EXIF Metadata"));
344 gpsBookmarksFolder.addContent(
new Element(
"name", ns).addContent(
"GPS Bookmarks"));
345 gpsLastKnownLocationFolder.addContent(
new Element(
"name", ns).addContent(
"GPS Last Known Location"));
346 gpsRouteFolder.addContent(
new Element(
"name", ns).addContent(
"GPS Routes"));
347 gpsSearchesFolder.addContent(
new Element(
"name", ns).addContent(
"GPS Searches"));
348 gpsTrackpointsFolder.addContent(
new Element(
"name", ns).addContent(
"GPS Trackpoints"));
349 gpsTracksFolder.addContent(
new Element(
"name", ns).addContent(
"GPS Tracks"));
350 gpsAreasFolder.addContent(
new Element(
"name", ns).addContent(
"GPS Areas"));
352 document.addContent(gpsExifMetadataFolder);
353 document.addContent(gpsBookmarksFolder);
354 document.addContent(gpsLastKnownLocationFolder);
355 document.addContent(gpsRouteFolder);
356 document.addContent(gpsSearchesFolder);
357 document.addContent(gpsTrackpointsFolder);
358 document.addContent(gpsTracksFolder);
359 document.addContent(gpsAreasFolder);
373 void addExifMetadataContent(List<Waypoint> points, String baseReportDirectory)
throws IOException, TskCoreException {
380 if (mapPoint == null) {
384 AbstractFile abstractFile = point.getImage();
388 copyFileUsingStream(abstractFile, Paths.get(baseReportDirectory, abstractFile.getName()).toFile());
391 }
catch (TskCoreException ex) {
392 path = Paths.get(abstractFile.getParentPath(), abstractFile.getName());
395 path = Paths.get(abstractFile.getName());
411 void addLocationsToReport(SleuthkitCase skCase, String baseReportDir)
throws GeoLocationDataException, IOException, TskCoreException {
412 if (waypointList == null) {
435 void addWaypoints(List<Waypoint> points, Element folder,
FeatureColor waypointColor, String headerLabel)
throws TskCoreException {
440 addContent(folder, point.getLabel(), waypointColor,
getFormattedDetails(point, headerLabel), point.getTimestamp(),
makePoint(point), point.getLatitude(), point.getLongitude());
456 void addContent(Element folder, String waypointLabel,
FeatureColor waypointColor, String formattedDetails, Long timestamp, Element point, Double latitude, Double longitude) {
457 if (folder != null && point != null) {
459 folder.addContent(
makePlacemark(waypointLabel, waypointColor, formattedDetails, timestamp, point, formattedCords));
470 void makeRoutes(SleuthkitCase skCase)
throws GeoLocationDataException, TskCoreException {
471 List<Route> routes = null;
473 if (waypointList == null) {
479 for (
Route route : routes) {
493 List<Waypoint> routePoints = route.
getRoute();
501 if (routePoints != null && routePoints.size() > 1) {
502 start = routePoints.get(0);
503 end = routePoints.get(1);
506 if (start == null || end == null) {
519 if (reportRoute != null) {
523 if (startingPoint != null) {
529 if (endingPoint != null) {
545 boolean makeTracks(SleuthkitCase skCase)
throws GeoLocationDataException, TskCoreException {
546 List<Track> tracks = null;
547 boolean successful =
true;
549 if (waypointList == null) {
557 for (
Track track : tracks) {
573 List<Waypoint> trackPoints = track.
getPath();
577 Element trackFolder =
new Element(
"Folder", ns);
578 trackFolder.addContent(
new Element(
"name", ns).addContent(track.
getLabel()));
579 gpsTracksFolder.addContent(trackFolder);
581 for (
Waypoint point : trackPoints) {
582 Element element =
makePoint(point.getLatitude(), point.getLongitude(), point.getAltitude());
585 point.getTimestamp(), element,
formattedCoordinates(point.getLatitude(), point.getLongitude())));
597 boolean makeAreas(SleuthkitCase skCase)
throws GeoLocationDataException, TskCoreException {
599 boolean successful =
true;
601 if (waypointList == null) {
609 for (
Area area : areas) {
625 List<Waypoint> areaPoints = area.
getPath();
627 if (areaPoints.isEmpty()) {
633 Element areaFolder =
new Element(
"Folder", ns);
634 areaFolder.addContent(
new Element(
"name", ns).addContent(area.
getLabel()));
635 gpsAreasFolder.addContent(areaFolder);
639 Waypoint firstWp = areaPoints.get(0);
653 return kmlDateFormat.format(
new java.util.Date(timeStamp * 1000));
678 private Element
makePoint(Double latitude, Double longitude, Double altitude) {
679 if (latitude == null || longitude == null) {
683 Element point =
new Element(
"Point", ns);
686 Element coordinates =
new Element(
"coordinates", ns).addContent(longitude +
"," + latitude +
"," + (altitude != null ? altitude : 0.0));
688 if (altitude != null && altitude != 0) {
696 Element altitudeMode =
new Element(
"altitudeMode", ns).addContent(
"clampToGround");
697 point.addContent(altitudeMode);
699 point.addContent(coordinates);
720 private Element
makeLineString(Double startLatitude, Double startLongitude, Double stopLatitude, Double stopLongitude) {
721 if (startLatitude == null || startLongitude == null || stopLatitude == null || stopLongitude == null) {
725 Element lineString =
new Element(
"LineString", ns);
726 lineString.addContent(
new Element(
"extrude", ns).addContent(
"1"));
727 lineString.addContent(
new Element(
"tessellate", ns).addContent(
"1"));
728 lineString.addContent(
new Element(
"altitudeMode", ns).addContent(
"clampToGround"));
730 lineString.addContent(
new Element(
"coordinates", ns).addContent(
731 startLongitude +
"," + startLatitude +
",0.0," 732 + stopLongitude +
"," + stopLatitude +
",0.0"));
745 Element polygon =
new Element(
"Polygon", ns);
747 Element altitudeMode =
new Element(
"altitudeMode", ns).addContent(
"clampToGround");
748 polygon.addContent(altitudeMode);
751 Element coordinates =
new Element(
"coordinates", ns);
753 coordinates.addContent(wp.getLongitude() +
"," + wp.getLatitude() +
",0 ");
756 coordinates.addContent(waypoints.get(0).getLongitude() +
"," + waypoints.get(0).getLatitude() +
",0 ");
758 Element linearRing =
new Element(
"LinearRing", ns).addContent(coordinates);
759 Element outerBoundary =
new Element(
"outerBoundaryIs", ns).addContent(linearRing);
760 polygon.addContent(outerBoundary);
779 private Element
makePlacemark(String name,
FeatureColor color, String description, Long timestamp, Element feature, String coordinates) {
780 Element placemark =
new Element(
"Placemark", ns);
781 if (name != null && !name.isEmpty()) {
782 placemark.addContent(
new Element(
"name", ns).addContent(name));
783 }
else if (timestamp != null) {
784 placemark.addContent(
new Element(
"name", ns).addContent(
getTimeStamp(timestamp)));
786 placemark.addContent(
new Element(
"name", ns).addContent(
""));
788 placemark.addContent(
new Element(
"styleUrl", ns).addContent(color.
getColor()));
789 placemark.addContent(
new Element(
"description", ns).addContent(description));
790 if (timestamp != null) {
791 Element time =
new Element(
"TimeStamp", ns);
792 time.addContent(
new Element(
"when", ns).addContent(
getTimeStamp(timestamp)));
793 placemark.addContent(time);
795 placemark.addContent(feature);
796 if (coordinates != null && !coordinates.isEmpty()) {
797 placemark.addContent(
new Element(
"snippet", ns).addContent(coordinates));
818 Element placemark =
new Element(
"Placemark", ns);
819 Element desc =
new Element(
"description", ns);
820 if (name != null && !name.isEmpty()) {
821 placemark.addContent(
new Element(
"name", ns).addContent(name));
822 String image =
"<img src='" + name +
"' width='400'/>";
823 desc.addContent(image);
825 placemark.addContent(
new Element(
"styleUrl", ns).addContent(color.
getColor()));
827 String pathAsString = path.toString();
828 if (pathAsString != null && !pathAsString.isEmpty()) {
829 desc.addContent(description +
"<b>Source Path:</b> " + pathAsString);
832 placemark.addContent(desc);
834 if (timestamp != null) {
835 Element time =
new Element(
"TimeStamp", ns);
836 time.addContent(
new Element(
"when", ns).addContent(
getTimeStamp(timestamp)));
837 placemark.addContent(time);
839 placemark.addContent(feature);
840 if (coordinates != null && !coordinates.isEmpty()) {
841 placemark.addContent(
new Element(
"snippet", ns).addContent(coordinates));
856 private void copyFileUsingStream(AbstractFile inputFile, File outputFile)
throws ReadContentInputStreamException, IOException {
857 byte[] buffer =
new byte[65536];
859 outputFile.createNewFile();
860 try (InputStream is =
new ReadContentInputStream(inputFile);
861 OutputStream os =
new FileOutputStream(outputFile)) {
862 while ((length = is.read(buffer)) != -1) {
863 os.write(buffer, 0, length);
870 String name = NbBundle.getMessage(this.getClass(),
"ReportKML.getName.text");
876 return "ReportKML.kml";
881 String desc = NbBundle.getMessage(this.getClass(),
"ReportKML.getDesc.text");
902 String[] pathSegments = uniquePath.replaceFirst(
"^/*",
"").split(
"/");
905 if (pathSegments.length > 0) {
906 pathSegments[0] = pathSegments[0].replaceFirst(
"^img_",
"");
908 if (pathSegments.length > 1) {
909 pathSegments[1] = pathSegments[1].replaceFirst(
"^vol_",
"");
913 StringBuilder strbuf =
new StringBuilder();
914 for (String segment : pathSegments) {
915 if (!segment.isEmpty()) {
916 strbuf.append(
"/").append(segment);
919 return strbuf.toString();
931 StringBuilder result =
new StringBuilder();
932 result.append(String.format(
"<h3>%s</h3>", header))
936 if (timestamp != null) {
949 String value = prop.getValue();
950 if (value != null && !value.isEmpty()) {
955 return result.toString();
967 return String.format(HTML_PROP_FORMAT, title, value);
978 List<Waypoint> points = route.
getRoute();
979 StringBuilder result =
new StringBuilder();
981 result.append(String.format(
"<h3>%s</h3>", Bundle.Route_Details_Header()))
985 if (timestamp != null) {
989 if (points.size() > 1) {
997 if (altitude != null) {
1005 if (altitude != null) {
1012 String value = prop.getValue();
1013 if (value != null && !value.isEmpty()) {
1018 return result.toString();
1030 if (latitude == null || longitude == null) {
1034 return String.format(
"%.2f, %.2f", latitude, longitude);
1044 long dataSourceId = content.getDataSource().getId();
static final String HTML_PROP_FORMAT
List< Content > getDataSources()
boolean shouldFilterFromReport(Content content)
static GeoLocationParseResult< Area > getAreas(SleuthkitCase skCase, List<? extends Content > sourceList)
void copyFileUsingStream(AbstractFile inputFile, File outputFile)
Element makeLineString(Double startLatitude, Double startLongitude, Double stopLatitude, Double stopLongitude)
String getRelativeFilePath()
static synchronized IngestManager getInstance()
static List< Waypoint > getBookmarkWaypoints(SleuthkitCase skCase)
static List< Route > getRoutes(SleuthkitCase skCase)
List< Waypoint > waypointList
List< Waypoint.Property > getOtherProperties()
void generateReport(GeneralReportSettings settings, ReportProgressPanel progressPanel)
String getFormattedDetails(Route route)
void complete(ReportStatus reportStatus)
Element makePlacemarkWithPicture(String name, FeatureColor color, String description, Long timestamp, Element feature, Path path, String coordinates)
Element makePoint(Double latitude, Double longitude, Double altitude)
boolean isIngestRunning()
Element gpsTrackpointsFolder
Element gpsBookmarksFolder
List< Waypoint > getRoute()
void addReport(String localPath, String srcModuleName, String reportName)
static List< Waypoint > getSearchWaypoints(SleuthkitCase skCase)
void addAreaToReport(Area area)
void setIndeterminate(boolean indeterminate)
Element makePolygon(List< Waypoint > waypoints)
static List< Track > getTracks(List< Waypoint > waypoints)
void addRouteToReport(Route route)
static List< Waypoint > getTrackpointWaypoints(SleuthkitCase skCase)
static String removeLeadingImgAndVol(String uniquePath)
void addTrackToReport(Track track)
Element gpsExifMetadataFolder
Document setupReportDocument()
static final String KML_STYLE_FILE
static final Logger logger
static List< Route > getRoutes(List< Waypoint > waypoints)
static synchronized KMLReport getDefault()
void setReportDirectoryPath(String reportDirectoryPath)
static GeoLocationParseResult< Track > getTracks(SleuthkitCase skCase, List<? extends Content > sourceList)
final List< Waypoint > getPath()
List< Waypoint.Property > getOtherProperties()
void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List< Waypoint > waypointList)
SleuthkitCase getSleuthkitCase()
static final String REPORT_KML
FeatureColor(String color)
String formatAttribute(String title, String value)
static KMLReport instance
Element makePlacemark(String name, FeatureColor color, String description, Long timestamp, Element feature, String coordinates)
List< Long > getSelectedDataSources()
Element gpsLastKnownLocationFolder
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
void updateStatusLabel(String statusMessage)
Element gpsSearchesFolder
static List< Waypoint > getLastKnownWaypoints(SleuthkitCase skCase)
void setSelectedDataSources(List< Long > selectedDataSources)
static List< Waypoint > getEXIFWaypoints(SleuthkitCase skCase)
boolean isSuccessfullyParsed()
String getTimeStamp(long timeStamp)
final SimpleDateFormat kmlDateFormat
boolean supportsDataSourceSelection()
Element makePoint(Waypoint point)
static List< Area > getAreas(List< Waypoint > waypoints)
String getFormattedDetails(Waypoint point, String header)
String getReportDirectoryPath()
JPanel getConfigurationPanel()
GeneralReportSettings settings
static final String STYLESHEETS_PATH
String formattedCoordinates(Double latitude, Double longitude)