CaveConverter_src/ 0000755 0000000 0000000 00000000000 13036510056 013170 5 ustar root root CaveConverter_src/src/ 0000755 0000000 0000000 00000000000 13036510056 013757 5 ustar root root CaveConverter_src/src/image/ 0000755 0000000 0000000 00000000000 13036467352 015053 5 ustar root root CaveConverter_src/src/image/exit.png 0000644 0000000 0000000 00000001554 12560646324 016536 0 ustar root root PNG
IHDR a 3IDAT8m_L[`RBKmˈs,721LpaJF4Abq^M6$ȦƘm:-0a ȟB7HKmm)9NDxJkygy!ycfw*=k@zÝO_}gv~#
'o7ffy_c?إl+c-"7f.[?]_0G=3pB{$B<^xIRs1,'N9e,'H
M À@]Ef;EOkg _=<ټ̐zSs+%Ȥ!fUZi1b*JKzS-"
b#Ɲ T._|^~
r i6ҜrlLv %2ʛA D= CdsTC*"5 ɄbEhkFx)I9Pw o誂MQ
m;@^/1sф6fn.l١AW(}
ܭ54Qx%:53eADx_1*eI_1r9I!Cۥ璻NU4g5
ŪcӺ 柯cuTƠtHwWF-aC
YN(q51n-8>?D&_;fn;4`55fIf˯m=|`/7(9q*{.{ p4ߍf3?Z`p] IENDB` CaveConverter_src/src/footleg/ 0000755 0000000 0000000 00000000000 13036467352 015430 5 ustar root root CaveConverter_src/src/footleg/cavesurvey/ 0000755 0000000 0000000 00000000000 13036504774 017624 5 ustar root root CaveConverter_src/src/footleg/cavesurvey/data/ 0000755 0000000 0000000 00000000000 13036467352 020535 5 ustar root root CaveConverter_src/src/footleg/cavesurvey/data/model/ 0000755 0000000 0000000 00000000000 13036467352 021635 5 ustar root root CaveConverter_src/src/footleg/cavesurvey/data/model/CaveSurvey.java 0000644 0000000 0000000 00000017572 13036416434 024603 0 ustar root root /**
* Copyright (C) 2009-2017 Paul Fretwell - aka 'Footleg' (drfootleg@gmail.com)
*
* This file is part of Cave Converter.
*
* Cave Converter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cave Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cave Converter. If not, see .
*/
package footleg.cavesurvey.data.model;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import javax.swing.event.EventListenerList;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import footleg.cavesurvey.converter.Logger;
/**
* Class representing a complete cave survey data model. This can consist of
* one or more cave surveys.
*
* @author Footleg
* @version 2017.01.09 (ISO 8601 YYYY.MM.DD)
* @since 1.6 (The Java version used)
*/
public class CaveSurvey implements TreeModel {
private String surveyName;
private List survey;
private EventListenerList listenerList = new EventListenerList();
private Logger logger;
/**
* Class constructor
* @param logger Logging class to output information, warning and error messages to
*/
public CaveSurvey( Logger logger ) {
super();
this.logger = logger;
//Create new list of survey series to hold data
survey = new ArrayList();
}
public int size() {
return survey.size();
}
public String getSurveyName() {
return surveyName;
}
public void setSurveyName(String surveyName) {
this.surveyName = surveyName;
}
public boolean isEmpty() {
return survey.isEmpty();
}
public boolean contains(Object o) {
return survey.contains(o);
}
public Iterator iterator() {
return survey.listIterator();
}
public boolean add(SurveySeries e) {
boolean result = survey.add(e);
fireTreeStructureChanged( this );
return result;
}
public boolean remove(SurveySeries o) {
return survey.remove(o);
}
public void clear() {
survey.clear();
}
public SurveySeries get(int index) {
return survey.get(index);
}
public SurveySeries set(int index, SurveySeries element) {
return survey.set(index, element);
}
public void add(int index, SurveySeries element) {
survey.add(index, element);
}
public SurveySeries remove(int index) {
return survey.remove(index);
}
public ListIterator listIterator() {
return survey.listIterator();
}
public ListIterator listIterator(int index) {
return survey.listIterator(index);
}
/**
* Generates LRUD data from splays for all legs in all series
*/
public void generateLRUDfromSplays() {
//Loop through all series
ListIterator seriesIterator = survey.listIterator();
while ( seriesIterator.hasNext() ) {
SurveySeries series = seriesIterator.next();
//Process series and then recursively call inner series
processLRUDfromSplays( series );
}
}
/**
* Process the legs in this series to generate LRUD data from splays,
* then loop through all inner series recursively to process them too.
* @param series The survey series to process
*/
private void processLRUDfromSplays( SurveySeries series ) {
//Process series and then recursively call inner series
series.generateLRUDFromSplays( logger );
//TODO Fix the way the option to remove splays used for LRUD is triggered, and don't do it by default.
//series.removeSplaysUsedForLRUD();
//Loop through all inner series
ListIterator seriesIterator = series.getInnerSeriesList().listIterator();
while ( seriesIterator.hasNext() ) {
SurveySeries innerSeries = seriesIterator.next();
processLRUDfromSplays( innerSeries );
}
}
/*
* TreeModel interface methods
*/
/*
* (non-Javadoc)
* @see javax.swing.tree.TreeModel#getRoot()
*/
@Override
public Object getRoot() {
//Root is the cave survey class itself
return this;
}
@Override
public Object getChild(Object parent, int index) {
//Return inner series at the index specified of the parent series passed in
if ( parent instanceof CaveSurvey ) {
return survey.get(index);
}
else if ( parent instanceof SurveySeries ) {
SurveySeries series = (SurveySeries)parent;
//Determine whether this index points to an inner series or a leg
if ( index < series.innerSeriesCount() ) {
//Return an inner series
return series.getInnerSeries(index);
}
else {
//Return a survey leg
int legIdx = index - series.innerSeriesCount();
return series.getLegRaw( legIdx );
}
}
else {
return null;
}
}
@Override
public int getChildCount(Object parent) {
//Return count of inner series plus surveyed legs for the parent series passed in
if ( parent instanceof CaveSurvey ) {
return size();
}
else if ( parent instanceof SurveySeries ) {
SurveySeries series = (SurveySeries)parent;
return series.getInnerSeriesList().size() + series.legCount();
}
else {
return 0;
}
}
@Override
public boolean isLeaf(Object node) {
//Return true if node is a survey leg
boolean leaf = false;
if ( node instanceof SurveyLeg ) {
leaf = true;
}
return leaf;
}
@Override
public void valueForPathChanged(TreePath path, Object newValue) {
// TODO Auto-generated method stub
Object[] p = path.getPath();
Object[] pp = p;
Object node;
int index;
if ( p.length == 1 ) {
//Editing root node
setSurveyName( (String)newValue );
node = this;
index = -1;
}
else {
//Editing a Survey series or leg inside a series
node = p[p.length - 1];
SurveySeries parent = (SurveySeries)p[p.length - 2];
index = parent.getIndexOfChild( node );
//EDIT HERE
if ( node instanceof SurveySeries ) {
((SurveySeries) node).setSeriesName( (String)newValue );
}
}
// int[] ci = new int[] { index };
// Object[] cc = new Object[] { node };
// fireTreeNodesChanged();
}
@Override
public int getIndexOfChild(Object parent, Object child) {
//Look up index of the specified child class instance in the specified parent instance
if ( parent instanceof SurveySeries ) {
return ((SurveySeries)parent).getIndexOfChild( child );
}
else {
return -1;
}
}
@Override
public void addTreeModelListener(TreeModelListener l) {
listenerList.add(TreeModelListener.class, l);
}
@Override
public void removeTreeModelListener(TreeModelListener l) {
listenerList.remove(TreeModelListener.class, l);
}
protected void fireTreeStructureChanged(Object oldRoot) {
TreeModelEvent event = new TreeModelEvent(this, new Object[] { oldRoot });
EventListener[] listeners = listenerList.getListeners(TreeModelListener.class);
for (int i = 0; i < listeners.length; i++)
((TreeModelListener) listeners[i]).treeStructureChanged(event);
}
/**
* Provides a string representation of a cave survey to display in a tree view of the data model
* @return String representation of Cave Survey class
*/
public String toString() {
String text = getSurveyName();
if ( ( text == null ) || ( text.equals("") ) ) {
text = "Cave Survey";
}
return text;
}
}
CaveConverter_src/src/footleg/cavesurvey/data/model/SurveySeries.java 0000644 0000000 0000000 00000117400 13036417060 025142 0 ustar root root /**
* Copyright (C) 2009-2017 Paul Fretwell - aka 'Footleg' (drfootleg@gmail.com)
*
* This file is part of Cave Converter.
*
* Cave Converter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cave Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cave Converter. If not, see .
*/
package footleg.cavesurvey.data.model;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.ListIterator;
import footleg.cavesurvey.converter.CaveConverter.BearingUnit;
import footleg.cavesurvey.converter.CaveConverter.GradientUnit;
import footleg.cavesurvey.converter.CaveConverter.LengthUnit;
import footleg.cavesurvey.converter.Logger;
import footleg.cavesurvey.tools.UtilityFunctions;
/**
* This class represents a series of cave survey legs joined together by
* sharing stations in common. Branches are allowed, but all legs in a series
* are required to have the same instrument calibration values and have the
* same date. A series can also contain other series, and details of how they
* are linked together.
*
* A survey series contains the survey legs in the series, the date,
* calibrations for instruments (these default to zero if not set)
* and magnetic declination (defaults to zero if not set).
* A series name is required. Optionally a series can also indicate that stations
* used in legs in the series are equivalent (i.e. Represent the same point in
* the cave). The series can also contain other series, and details of links
* between them.
* Data is stored and calculations done in metric units (metres and degrees), but
* conversion to other units on input/output is supported.
*
* @author Footleg
* @version 2017.01.09 (ISO 8601 YYYY.MM.DD)
* @since 1.6 (The Java version used)
*
* @to.do
* TODO Add support for calibration comments fields
* TODO Add support for team fields
* TODO Add support for instrument fields
*/
public class SurveySeries {
/**
* Private data class to represent the LRUD data for the toStn of a leg.
* Used for stations which are not used as 'from' stations in any legs.
* This class enables both the LRUD dimensions and the vector normal to the plane
* of the LRUD dimensions to be stored.
*
* @author Footleg
*
*/
public class ToStnLRUD {
private SurveyStation fromStn;
//LRUD applies to fromStn
private double left = 0.0;
private double right = 0.0;
private double up = 0.0;
private double down = 0.0;
//Getters and Setters
public SurveyStation getFromStn() {
return fromStn;
}
public void setFromStn(SurveyStation fromStn) {
this.fromStn = fromStn;
}
public double getLeft() {
return left;
}
public void setLeft(double left) {
this.left = left;
}
public double getRight() {
return right;
}
public void setRight(double right) {
this.right = right;
}
public double getUp() {
return up;
}
public void setUp(double up) {
this.up = up;
}
public double getDown() {
return down;
}
public void setDown(double down) {
this.down = down;
}
}
private String seriesName;
private List legs;
private List toStnLRUDs;
private List links;
private List innerSeries;
private int stnRenumberSequence;
private List stnRenameCache;
private double declination = 0;
private double tapeCalibration = 0;
private double compassCalibration = 0;
private double clinoCalibration = 0;
private double clinoCalScaleFactor = 1;
private Date surveyDate;
private String comment = "";
private static double LRUD_SPECIAL_FLAG = -999;
private LengthUnit lengthUnit = LengthUnit.Metres;
private LengthUnit depthUnit = LengthUnit.Metres;
private BearingUnit bearingUnit = BearingUnit.Degrees;
private GradientUnit gradientUnit = GradientUnit.Degrees;
private List dataOrder;
private List dataOrder2;
/**
* Create a Survey Series with the name given
* @param name Name of the series
*/
public SurveySeries( String name ) {
super();
init();
this.seriesName = name;
}
private void init() {
legs = new ArrayList();
toStnLRUDs = new ArrayList();
links = new ArrayList();
innerSeries = new ArrayList();
stnRenameCache = new ArrayList();
stnRenumberSequence = 0;
}
public LengthUnit getLengthUnit() {
return lengthUnit;
}
public void setLengthUnit(LengthUnit lengthUnit) {
this.lengthUnit = lengthUnit;
}
public LengthUnit getDepthUnit() {
return depthUnit;
}
public void setDepthUnit(LengthUnit depthUnit) {
this.depthUnit = depthUnit;
}
public BearingUnit getBearingUnit() {
return bearingUnit;
}
public void setBearingUnit(BearingUnit bearingUnit) {
this.bearingUnit = bearingUnit;
}
public GradientUnit getGradientUnit() {
return gradientUnit;
}
public void setGradientUnit(GradientUnit gradientUnit) {
this.gradientUnit = gradientUnit;
}
public void addLeg(SurveyLeg leg) {
//Create duplicate of leg to break reference
this.legs.add(leg.clone());
}
public void addLeg(SurveyLeg leg, int position) {
//Create duplicate of leg to break reference
this.legs.add( position, leg.clone() );
}
public int legCount() {
return legs.size();
}
public List getToStnLRUDs() {
return toStnLRUDs;
}
/**
* Assigns a survey station a unique Id for the station name. The survey
* station name is mapped to an integer which is assigned as the station id.
* This can be used for file formats which do not support text station names.
* If the name is a string which represents
* a positive integer number already then that number is returned.
* For non-numeric station names the name is assigned
* a unique negative integer number. Negative numbers are used so they will
* not clash with any positive number station names in the series. The negative
* station ids also indicate which stations have names which are not numeric.
* A map of names to numbers already added to the series is used so that the
* same name is not translated into different numbers if passed in more than
* once. When all legs have been added to a series, the negative numbered
* stations could be assigned new positive number IDs which are not already
* used by other stations in the series.
*
* @param stn The survey station with a name property to be assigned an id
*/
public void setStationIdFromName( SurveyStation stn ) {
int stnId = stnNameToNumber( stn.getName() );
stn.setId( stnId );
}
/**
* @param stnName
* @return a station number linked to the text name for this series
*/
private int stnNameToNumber( String stnName ) {
int stn;
try {
stn = Integer.parseInt( stnName );
}
catch (NumberFormatException nfe) {
//Stn name not an integer, so translate name to a negative number
stn = this.getNumberToRepresentStnName( stnName );
}
return stn;
}
/**
* Returns a negative integer number for a station name string.
*
* The number will be unique to this series for this station name.
* This enables station names to be converted to numbers for file formats
* which do not support strings. The name is mapped to a number in this
* series by looking up the name in a cache for the series. If the name
* is not found in the cache then a new negative number is returned, and
* the new name and number mapping is added to the cache for the series.
*
* @param stnName Name of the survey station to map to a number.
* @return Number which can be used to represent this station.
*/
public int getNumberToRepresentStnName(String stnName) {
int mappedStnNum = 0;
//Look for station name in cache
ListIterator stnCacheIterator = stnRenameCache.listIterator();
int stnIdx = 0;
while ( ( mappedStnNum == 0 ) && ( stnCacheIterator.hasNext() ) ) {
String cachedName = stnCacheIterator.next();
stnIdx--;
if ( stnName.compareToIgnoreCase(cachedName) == 0 ) {
//Found matching stn in cache, so get number from index
mappedStnNum = stnIdx;
}
}
if ( mappedStnNum == 0 ) {
//No match found in cache, so create a new mapping and cache it
stnRenumberSequence--;
mappedStnNum = stnRenumberSequence;
/**
* As the mapped stns are added to the cache, the index in the
* cache will correspond to the mapped number of the stn:
* idx 0 = map -1
* idx 1 = map -2
* etc.
*/
stnRenameCache.add(stnName);
}
return mappedStnNum;
}
public String getMappedStnName(int stn) {
//If number is negative then lookup name in map,
//otherwise just return station number as the name
if ( stn < 0 ) {
return stnRenameCache.get( Math.abs(stn) - 1 );
}
else {
return "" + stn;
}
}
//Apply instrument calibration corrections to all measurements in returned leg
public SurveyLeg getLegCorrected(int index) {
SurveyLeg originalLeg = legs.get(index);
SurveyLeg correctedLeg = originalLeg.clone();
//Over-ride leg length,compass and clino with calibration corrected values
correctedLeg.setLength( originalLeg.getLength(LengthUnit.Metres) - tapeCalibration, LengthUnit.Metres );
double compass = originalLeg.getCompass(BearingUnit.Degrees);
if ( compass >= 0 && compass <= 360 ) {
//Apply calibration only to valid bearings
compass -= (compassCalibration + declination);
}
correctedLeg.setCompass( compass, BearingUnit.Degrees );
double clino = originalLeg.getClino(GradientUnit.Degrees);
if ( clino >= -90 && clino <= 180 ) {
//Apply calibration only to valid clino
clino = (clino - clinoCalibration) * clinoCalScaleFactor;
}
correctedLeg.setClino( clino, GradientUnit.Degrees );
//Don't correct LRUD using tape calibration as these are assumed to be estimated by eye
return correctedLeg;
}
public SurveyLeg getLegRaw(int index) {
return legs.get(index);
}
public void addSeries(SurveySeries series) {
this.innerSeries.add(series);
}
public int innerSeriesCount() {
return innerSeries.size();
}
public SurveySeries getInnerSeries(int index) {
return innerSeries.get(index);
}
public List getInnerSeriesList() {
return innerSeries;
}
public SurveySeries findInnerSeriesByName(String name) {
SurveySeries match = null;
//Look in inner series
ListIterator innerIterator = innerSeries.listIterator();
while ( innerIterator.hasNext() ) {
SurveySeries innerSeries = innerIterator.next();
if ( innerSeries.getSeriesName().equalsIgnoreCase( name ) ) {
//Found series, so return this
match = innerSeries;
break;
}
}
return match;
}
/**
* Look up the child object specified in the series, and return the index of the object
* @param child The child object to get the index of
* @return The index of the object in this series
*/
public int getIndexOfChild(Object child) {
int matchIndex = -1;
//Look for child in inner series
ListIterator innerIterator = innerSeries.listIterator();
int idx = -1;
while ( innerIterator.hasNext() ) {
SurveySeries innerSeries = innerIterator.next();
idx++;
if ( innerSeries.equals( child ) ) {
//Found series, so return this
matchIndex = idx;
break;
}
}
return matchIndex;
}
public void addLink(String series1Path, SurveyStation stn1, String series2Path, SurveyStation stn2 ) {
//Add link to array
SeriesLink link = new SeriesLink(series1Path, stn1, series2Path, stn2 );
links.add(link);
}
public List getLinks() {
return links;
}
public String getSeriesName() {
return seriesName;
}
public void setSeriesName(String seriesName) {
this.seriesName = seriesName;
}
public List getDataOrder() {
//Make a copy to return, as we don't want a pointer to the existing list or parsers
//can change it
List tmpList = new ArrayList();
if (dataOrder != null ) {
tmpList.addAll(dataOrder);
}
return tmpList;
}
public List getDataOrder2() {
//Make a copy to return, as we don't want a pointer to the existing list or parsers
//can change it
List tmpList = new ArrayList();
if (dataOrder2 != null ) {
tmpList.addAll(dataOrder2);
}
return tmpList;
}
/**
* Indicates if series has a data order defined. Method provided to avoid copying data order
* list just to check this from outside the series, as getter clones the list to protect it
* from being edited via a pointer.
* @return True if series has a field data order defined.
*/
public boolean hasDataOrder() {
boolean hasIt = false;
if ( dataOrder != null ) {
hasIt = (dataOrder.size() > 0);
}
return hasIt;
}
/**
* Specifies the order of data items used in the file which was read to generate the series.
* Parsers use this to track the active data ordering (i.e. from to length, bearing, gradient)
* through nested series in files.
* @param dataOrder Field data order for the series
*/
public void setDataOrder(List dataOrder) {
//Make a copy to store, as we don't want a pointer to an existing list
List tmpList = new ArrayList();
tmpList.addAll(dataOrder);
this.dataOrder = tmpList;
}
/**
* Secondary order of data items used in the series. This is used when a series contains a mix
* of normal and diving data to hold the data order for both formats of data.
* @param dataOrder Field data order for the series for alternate data format (e.g. For mixed diving/normal data in series)
*/
public void setDataOrder2(List dataOrder) {
//Make a copy to store, as we don't want a pointer to an existing list
List tmpList = new ArrayList();
tmpList.addAll(dataOrder);
this.dataOrder2 = tmpList;
}
/**
* Provides a string representation of a cave survey series to display in a tree view of the data model
* @return String representation of survey series class
*/
public String toString() {
String text = getSeriesName();
if ( ( text == null ) || ( text.equals("") ) ) {
text = "Survey Series";
}
return text;
}
public double getTapeCalibration(LengthUnit units) {
return UtilityFunctions.lengthFromMetres( tapeCalibration, units );
}
public void setTapeCalibration(double tapeCalibration, LengthUnit units) {
this.tapeCalibration = UtilityFunctions.lengthToMetres(tapeCalibration, units);
}
public double getDeclination() {
return declination;
}
public void setDeclination(double declination) {
this.declination = declination;
}
public double getCompassCalibration(BearingUnit units) {
return UtilityFunctions.bearingFromDegrees(compassCalibration, units) ;
}
public void setCompassCalibration(double compassCalibration, BearingUnit units) {
this.compassCalibration = UtilityFunctions.bearingToDegrees(compassCalibration, units) ;
}
public double getClinoCalibration(GradientUnit units) {
return UtilityFunctions.gradientFromDegrees(clinoCalibration, units) ;
}
public double getClinoScaleFactor() {
return clinoCalScaleFactor;
}
public void setClinoCalibration(double clinoCalibration, GradientUnit units) {
this.clinoCalibration = UtilityFunctions.gradientToDegrees(clinoCalibration, units);
this.clinoCalScaleFactor = 1;
}
public void setClinoCalibration(double clinoCalibration, GradientUnit units, double clinoCalScaleFactor) {
this.clinoCalibration = UtilityFunctions.gradientToDegrees(clinoCalibration, units);;
this.clinoCalScaleFactor = clinoCalScaleFactor;
}
public Date getSurveyDate() {
return surveyDate;
}
public void setSurveyDate(Date surveyDate) {
this.surveyDate = surveyDate;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public void setCalibrationFromAnotherSeries(SurveySeries series) {
setDeclination( series.getDeclination() );
setTapeCalibration( series.getTapeCalibration( LengthUnit.Metres ), LengthUnit.Metres );
setCompassCalibration( series.getCompassCalibration( BearingUnit.Degrees ), BearingUnit.Degrees );
setClinoCalibration( series.getClinoCalibration( GradientUnit.Degrees ), GradientUnit.Degrees, series.getClinoScaleFactor() );
}
public SurveyLeg removeLeg(int index){
return legs.remove(index);
}
public void removeSplaysUsedForLRUD() {
int i = legs.size() - 1;
while ( i >= 0 ) {
SurveyLeg leg = legs.get(i);
if ( leg.isSplay() && leg.getDown( LengthUnit.Metres ) == LRUD_SPECIAL_FLAG ) {
//Splay leg used for LRUD generation
removeLeg(i);
}
else {
i--;
}
}
}
/**
* Examines all splay shots from the start station for each leg and generates LRUD data
* from them for each leg. The algorithm examines all legs which meet at the station, and
* splays which are close to equivalent to any of the legs are not used (to prevent back shots and
* splays up side passages which were then surveyed being used to calculate passage dimensions).
* Up and down dimensions are calculated from the splay with the greatest vertical length
* up or down as appropriate. Splays which are less than 20 degrees from horizontal are
* ignored when determining up and down dimensions. Splays with an angle steeper than 70 degrees
* from horizontal are excluded from the splays used to determine horizontal passage dimensions.
* The passage dimension directions for left and right are calculated along the horizontal
* vector bisecting the angle between the forward survey leg, and the most in-line previous
* leg arriving at the station. The best matching splay is selected from the suitable
* splays by determining which splay gives the greatest distance along the left or right
* dimension vector.
* @param logger Logging class to output information, warning and error messages to
*/
public void generateLRUDFromSplays( Logger logger ) {
List caveLegs = new ArrayList();
List> splayLegGroups = new ArrayList>();
List> previousLegGroups = new ArrayList>();
//Loop through all legs in series to build arrays of splays and non-splay underground legs
ListIterator legsIterator = legs.listIterator();
while ( legsIterator.hasNext() ) {
SurveyLeg leg = legsIterator.next();
//Put all non-surface legs into either the cave legs (non-splays) or splay groups lists
if ( leg.isSurface() == false ) {
if ( leg.isSplay() ) {
//Add to splays group identified by station name
String stnName = leg.getFromStn().getName();
//Look for a group matching this station
int groupIdx = -1;
for ( int i = 0; groupIdx < 0 && i < splayLegGroups.size(); i++ ) {
if ( splayLegGroups.get(i).get(0).getFromStn().getName().compareTo(stnName) == 0 ) {
//Found group, so get index
groupIdx = i;
}
}
if ( groupIdx >= 0 ) {
//Add to this group
splayLegGroups.get(groupIdx).add(leg);
}
else {
//Create new group and add leg to this
List splays = new ArrayList();
splays.add(leg);
splayLegGroups.add(splays);
}
}
else {
//Add to legs array to be processed
caveLegs.add(leg);
}
}
}
//Loop through all legs non-splay legs to build arrays of legs radiating from the station of each leg
for ( int caveLegIdx = 0; caveLegIdx < caveLegs.size(); caveLegIdx++ ) {
SurveyLeg leg = caveLegs.get(caveLegIdx);
String legStartStn = leg.getFromStn().getName();
//Create list to hold legs radiating out from this leg fromStn
List legGrp = new ArrayList();
//Find all other legs which radiate out from the fromStn of this leg
for ( int otherLegIdx = 0; otherLegIdx < caveLegs.size(); otherLegIdx++ ) {
//Ignore ourself
if ( otherLegIdx != caveLegIdx ) {
SurveyLeg chkLeg = caveLegs.get(otherLegIdx);
if ( legStartStn.compareToIgnoreCase( chkLeg.getToStn().getName() ) == 0 ) {
//Add leg to group
legGrp.add(chkLeg);
}
else if ( legStartStn.compareToIgnoreCase( chkLeg.getFromStn().getName() ) == 0 ) {
//Reverse leg so it leads to this station and add leg to group
SurveyLeg revLeg = chkLeg.clone();
revLeg.reverseDirection();
legGrp.add( revLeg );
}
}
}
//Add group to list for this leg
previousLegGroups.add( legGrp );
}
//Loop through splay groups and process all the legs with a from stn matching the from stn of the splays in each group
ListIterator> spGroupsIter = splayLegGroups.listIterator();
List stnsUsed = new ArrayList();
while ( spGroupsIter.hasNext() ) {
List splaysGroup = spGroupsIter.next();
String stnName = splaysGroup.get(0).getFromStn().getName();
ListIterator legsIter = caveLegs.listIterator();
int caveLegIdx = -1;
while ( legsIter.hasNext() ) {
SurveyLeg leg = legsIter.next();
caveLegIdx++;
if ( leg.getFromStn().getName().compareTo(stnName) == 0 ) {
//Generate LRUD data for leg, using the splays starting from that leg from station
generateLRUDForLeg( leg, splaysGroup, previousLegGroups.get(caveLegIdx), logger );
//Store station name of splays group that was used
stnsUsed.add(stnName);
//Remove leg from list now it has been processed
legsIter.remove();
//Remove corresponding previous legs group so lists remain in sync
previousLegGroups.remove(caveLegIdx);
caveLegIdx--;
}
}
}
//Build list of splays groups which were not used, as these will be for terminal
//stations in branches of the series. These splays will only be from the toStn of a leg.
ListIterator> spGroupsIter2 = splayLegGroups.listIterator();
List> unusedSplayGroups = new ArrayList>();
while ( spGroupsIter2.hasNext() ) {
List splaysGroup = spGroupsIter2.next();
String stnName = splaysGroup.get(0).getFromStn().getName();
//Check for this group in the used stations list
boolean foundMatch = false;
ListIterator stnsUsedIter = stnsUsed.listIterator();
while ( stnsUsedIter.hasNext() ) {
String grpName = stnsUsedIter.next();
if ( stnName.compareTo( grpName ) == 0 ) {
//Set flag to indicate group was used
foundMatch = true;
break;
}
}
if ( foundMatch == false ) {
//Add group to unused groups list
unusedSplayGroups.add( splaysGroup );
}
}
//Loop through unused splays groups and find the leg where the toStn for these
//splays occur. Then add the LRUD data for the toStn to the toStnLrud cache.
ListIterator> spGroupsIter3 = unusedSplayGroups.listIterator();
while ( spGroupsIter3.hasNext() ) {
List splaysGroup = spGroupsIter3.next();
String stnName = splaysGroup.get(0).getFromStn().getName();
//Find leg with a matching toStn for these splays
for ( int legsIdx = 0; legsIdx < legs.size(); legsIdx++ ) {
SurveyLeg leg = legs.get( legsIdx );
if ( ( leg.isSplay() == false )
&& ( leg.getToStn() != null )
&& ( leg.getToStn().getName().compareTo(stnName) == 0 ) ) {
//Create a temporary leg to hold the LRUD for this toStn
SurveyLeg tempLrudLeg = new SurveyLeg();
tempLrudLeg.setFromStn( leg.getToStn() );
tempLrudLeg.setLength( -1, LengthUnit.Metres );
tempLrudLeg.setCompass( leg.getCompass(BearingUnit.Degrees), BearingUnit.Degrees );
tempLrudLeg.setClino( leg.getClino(GradientUnit.Degrees), GradientUnit.Degrees );
//Check for any other legs terminating at this station, as if there are any then
//their bearings should be used to average with the bearing for the leg we are creating here.
List otherLegs = new ArrayList();
/* Find any other legs which terminate at this station. There may be more than
* one leg terminating at a station which is not a 'from' station in any leg
* e.g. In a leap-frog survey. So we need to hold the LRUD for the station which
* only appears as a 'to' station, but in more than one leg. If there were 3 legs all
* pointing 'to' a station then the first one in the series will be the one the LRUD data
* is associated with really, but this is not recorded.
*/
for ( int legsIdx2 = legsIdx + 1; legsIdx2 < legs.size(); legsIdx2++ ) {
SurveyLeg chkLeg = legs.get( legsIdx2 );
if ( ( chkLeg.isSplay() == false )
&& ( chkLeg.getToStn() != null )
&& ( chkLeg.getToStn().getName().compareTo(stnName) == 0 ) ) {
//Found another leg to this station, we need to reverse it as
//LRUD function takes group of legs from the station
SurveyLeg revLeg = chkLeg.clone();
revLeg.reverseDirection();
otherLegs.add(revLeg);
}
}
//Generate LRUD data for leg, using the splays starting from that leg from station
generateLRUDForLeg( tempLrudLeg, splaysGroup, otherLegs, logger );
//Create an LRUD object to hold the LRUD data and copy data into it
ToStnLRUD newLrud = new ToStnLRUD();
newLrud.setFromStn( tempLrudLeg.getFromStn() );
newLrud.setLeft( tempLrudLeg.getLeft(LengthUnit.Metres) );
newLrud.setRight( tempLrudLeg.getRight(LengthUnit.Metres) );
newLrud.setUp( tempLrudLeg.getUp(LengthUnit.Metres) );
newLrud.setDown( tempLrudLeg.getDown(LengthUnit.Metres) );
//Add the new LRUD leg in the series
toStnLRUDs.add(newLrud);
//Stop checking series once LRUD data has been accounted for or LRUD data will get
//duplicated in cases where the station is the toStn in more than one leg (e.g. In
//a leap-frog survey)
legsIdx = legs.size();
}
}
}
}
/**
* Generates a Left, Right, Up and Down dimension at the from station for a survey leg
* using the splays recorded at that station, and stores them in the leg.
* @param leg The survey leg to generate LRUD data for (at the From station)
* @param splaysGroup A list of splay legs measured from the From station of the leg
* @param otherLegs A list of survey legs arriving at the same station as the From station of the leg
*/
private void generateLRUDForLeg( SurveyLeg masterLeg, List splayShots, List otherLegs, Logger logger ) {
int bearingTolerance = 3;
List leftShots = new ArrayList();
List rightShots = new ArrayList();
List upShots = new ArrayList();
List downShots = new ArrayList();
//Determine best previous leg (use leg with closest bearing to onward leg)
double bestPrevBearing = 360.0;
int bestPrevLegIdx = -1;
for ( int i = 0; i < otherLegs.size(); i++ ) {
//Looking for bearing closest to master leg bearing
double testBearing = UtilityFunctions.bearingDifferenceDegrees( otherLegs.get(i).getCompass(BearingUnit.Degrees), masterLeg.getCompass(BearingUnit.Degrees) );
if ( testBearing < bestPrevBearing ) {
//Found better match, so store value and index
bestPrevBearing = testBearing;
bestPrevLegIdx = i;
}
}
//If a previous leg was found then calculate average bearing between previous and onward legs
double bearing = masterLeg.getCompass(BearingUnit.Degrees);
if ( bestPrevLegIdx >= 0 ) {
double[] bearings = new double[2];
bearings[0] = masterLeg.getCompass(BearingUnit.Degrees);
bearings[1] = otherLegs.get(bestPrevLegIdx).getCompass(BearingUnit.Degrees);
bearing = UtilityFunctions.averageCompassBearings(bearings);
}
//Loop through all the splays and categorise each shot into groups of L,R,U,D
for ( int i = 0; i < splayShots.size(); i++ ) {
SurveyLeg splayLeg = splayShots.get(i);
//Ignore any splays which are along the path of any legs to/from this station
//This will remove backshots and splays fired up side passages which were then surveyed
//as side passage splays are not going to represent passage dimensions.
boolean backShot = false;
for ( int olegsIdx = -1; olegsIdx < otherLegs.size(); olegsIdx++ ) {
//First check against main leg
SurveyLeg testLeg = masterLeg;
if (olegsIdx > -1) {
//Then loop through other legs reversed (as we need bearing from this station, not to it)
testLeg = otherLegs.get(olegsIdx).clone();
testLeg.reverseDirection();
}
//Check if splay is equivalent to leg shot
double bearingDiff = UtilityFunctions.bearingDifferenceDegrees( testLeg.getCompass(BearingUnit.Degrees), splayLeg.getCompass(BearingUnit.Degrees) );
if ( bearingDiff < bearingTolerance ) {
//Within 3 degrees of leg, so check clino
double clinoDiff = UtilityFunctions.bearingDifferenceDegrees( testLeg.getClino(GradientUnit.Degrees), splayLeg.getClino(GradientUnit.Degrees) );
if ( clinoDiff < bearingTolerance ) {
//Within 3 degrees of leg, so check length
double lengthDiff = Math.abs( testLeg.getLength( LengthUnit.Metres ) - splayLeg.getLength( LengthUnit.Metres ) );
if ( lengthDiff < 0.2 ) {
//Under 20cm difference in length, assume a back shot for a leg
backShot = true;
logger.logMessage("Ignoring splay from " + getSeriesName() + "." + splayLeg.getFromStn().getName() +
" with length of " + splayLeg.getLength( LengthUnit.Metres ) + " as splay too closely matching a leg and so assumed to be a back-shot.");
}
}
}
}
if ( backShot == false ) {
double splayClino = splayLeg.getClino(GradientUnit.Degrees);
if ( splayClino > 20 ) {
//Up shot
upShots.add(splayLeg);
}
else if ( splayClino < -20 ) {
//Down shot
downShots.add(splayLeg);
}
//As well as up or down, some splays may also be the best Left or Right
if ( ( splayClino < 70 ) && ( splayClino > -70 ) ) {
//Left or right shots (not steeper than 70 deg.)
//Determine whether left or right shot
double masterBearing = masterLeg.getCompass(BearingUnit.Degrees);
//Normalise bearings to reference of zero degrees for onward leg
double splayCorrected = splayLeg.getCompass(BearingUnit.Degrees) - masterBearing;
if ( splayCorrected < 0 ) {
splayCorrected += 360;
}
if ( bestPrevLegIdx >= 0 ) {
//Two legs to consider
double prevLegBearing = otherLegs.get(bestPrevLegIdx).getCompass(BearingUnit.Degrees);
//Convert previous leg bearing to bearing from the station for the splay adjusted with respect to onward leg bearing
double prevLegBackBearingCorrected = UtilityFunctions.adjustBearingWithinDegreesRange(
180 + prevLegBearing - masterBearing, 0, 360 );
/* We now have three bearings. The onward leg is adjusted to zero deg.
* The back bearing for the previous leg is adjusted relative to the zero bearing of the onward leg.
* The splay is also adjusted relative to the zero bearing of the onward leg.
* Splays which are aligned with any leg from the station should already have been discarded.
* So now we just need to determine if this splay as a left or right splay.
*/
if ( splayCorrected < prevLegBackBearingCorrected ) {
//Right
rightShots.add(splayLeg);
}
else {
//Left
leftShots.add(splayLeg);
}
}
else {
//Only the one leg to consider as first or last leg of chain of legs in series
//Angle between splay and leg bearings is now in the range 0-360 deg.
if ( ( splayCorrected > bearingTolerance ) || ( splayCorrected > (360 - bearingTolerance) ) ) {
if ( splayCorrected < ( 180 - bearingTolerance) ) {
//Right
rightShots.add(splayLeg);
}
else if ( splayCorrected > ( 180 + bearingTolerance) ) {
//Left
leftShots.add(splayLeg);
}
else {
//Splay is within tolerance of back bearing for leg, so too shallow an angle to use for LRUD calc.
logger.logMessage("Ignoring splay from " + getSeriesName() + "." + splayLeg.getFromStn().getName() +
" with bearing of " + splayLeg.getCompass(BearingUnit.Degrees) + " as bearing is < " + bearingTolerance + " degrees off back bearing of leg.");
}
}
else {
//Splay is within tolerance of bearing for leg, so too shallow an angle to use for LRUD calc.
logger.logMessage("Ignoring splay from " + getSeriesName() + "." + splayLeg.getFromStn().getName() +
" with bearing of " + splayLeg.getCompass(BearingUnit.Degrees) + " as bearing is < " + bearingTolerance + " degrees off bearing of leg.");
}
}
}
}
}
//Set flag for this station to say whether all splays should be output
boolean keepAllSplays = false;
if (( upShots.size() > 1 )
|| ( downShots.size() > 1 )
|| ( leftShots.size() > 1 )
|| ( rightShots.size() > 1 )) {
keepAllSplays = true;
}
//Find best up shot
double bestValue = 0.0;
int bestIdx = -1;
for ( int i = 0; i < upShots.size(); i++ ) {
//Looking for highest clino reading
if ( upShots.get(i).getClino(GradientUnit.Degrees) > bestValue ) {
//Found better match, so store value and index
bestValue = upShots.get(i).getClino(GradientUnit.Degrees);
bestIdx = i;
}
}
//Add up shot if found
if ( bestIdx >= 0 ) {
//Found best up shot
masterLeg.setUp( upShots.get(bestIdx).getVerticalLength(), LengthUnit.Metres );
//Flag splay as used for Up
if ( keepAllSplays == false ) {
upShots.get(bestIdx).setDown( LRUD_SPECIAL_FLAG, LengthUnit.Metres );
}
}
//Find best down shot
bestValue = 0.0;
bestIdx = -1;
for ( int i = 0; i < downShots.size(); i++ ) {
//Looking for lowest clino reading
if ( downShots.get(i).getClino(GradientUnit.Degrees) < bestValue ) {
//Found better match, so store value and index
bestValue = downShots.get(i).getClino(GradientUnit.Degrees);
bestIdx = i;
}
}
//Add down shot if found
if ( bestIdx >= 0 ) {
//Found best down shot
masterLeg.setDown( downShots.get(bestIdx).getVerticalLength(), LengthUnit.Metres );
//Flag splay as used for Down
if ( keepAllSplays == false ) {
downShots.get(bestIdx).setDown( LRUD_SPECIAL_FLAG, LengthUnit.Metres );
}
}
//Find best left shot
bestIdx = findBestSplayForHorizontalDimension(leftShots, bearing, true);
//Add left shot if found
if ( bestIdx >= 0 ) {
//Found best left shot
double horizontalLength = leftShots.get(bestIdx).getHorizontalLength();
double leftOrthoganal = bearing - 90;
if ( leftOrthoganal < 0 ) {
leftOrthoganal += 360;
}
masterLeg.setLeft( extentAlongBearing( leftOrthoganal,
horizontalLength, leftShots.get(bestIdx).getCompass(BearingUnit.Degrees) ), LengthUnit.Metres );
//Flag splay as used for Left
if ( keepAllSplays == false ) {
leftShots.get(bestIdx).setDown( LRUD_SPECIAL_FLAG, LengthUnit.Metres );
}
}
//Find best right shot
bestIdx = findBestSplayForHorizontalDimension(rightShots, bearing, false);
//Add right shot if found
if ( bestIdx >= 0 ) {
//Found best right shot
double horizontalLength = rightShots.get(bestIdx).getHorizontalLength();
double rightOrthoganal = bearing + 90;
if ( rightOrthoganal >= 360 ) {
rightOrthoganal -= 360;
}
masterLeg.setRight( extentAlongBearing( rightOrthoganal,
horizontalLength, rightShots.get(bestIdx).getCompass(BearingUnit.Degrees) ), LengthUnit.Metres );
//Flag splay as used for Right
if ( keepAllSplays == false ) {
rightShots.get(bestIdx).setDown( LRUD_SPECIAL_FLAG, LengthUnit.Metres );
}
}
}
/**
* Determines which of the splays is the best one to use to calculate the horizontal dimension for the
* cave passage at this station
*
* @param splays Array of splay shots to pick the best shot from
* @param forwardBearing Bearing from the station to represent the passage direction
* @param left Flag to indicate if we want to find the best left shot or best right shot
* @return Index of the best matching splay shot from the input array of splays
*/
private int findBestSplayForHorizontalDimension(List splays, double forwardBearing, boolean left) {
//Find best shot (greatest horizontal extent along vector orthogonal to leg bearing)
double bestValue = 0.0;
int bestIdx = -1;
for ( int i = 0; i < splays.size(); i++ ) {
//Calculate corrected compass bearing for splay, normalised to a passage trending due North (zero degrees)
double shotCorrected = splays.get(i).getCompass(BearingUnit.Degrees) - forwardBearing;
if ( shotCorrected < 0 ) {
shotCorrected += 360;
}
//Calculate horizontal extent of leg in direction orthogonal to corrected bearing (left or right)
double orthogonal;
if ( left ) {
orthogonal = 270.0;
}
else {
orthogonal = 90.0;
}
double horizontalLength = splays.get(i).getHorizontalLength();
double orthogonalExtent = extentAlongBearing( orthogonal,
horizontalLength, shotCorrected);
if ( orthogonalExtent > bestValue ) {
//Found better match, so store value and index
bestValue = orthogonalExtent;
bestIdx = i;
}
}
return bestIdx;
}
/**
* Calculates the distance a vector extends along a bearing. Used to work out the distance of the cave wall
* for a left or right passage dimension in a given direction from a station for a horizontal length measured
* along another bearing
* @param bearing The direction we need the distance to the wall for
* @param vectorLength The length of the horizontal measurement we have
* @param vectorBearing The direction of the horizontal measurement we have
* @return The calculated distance to the wall from the station in the direction of the bearing
*/
private double extentAlongBearing( double bearing, double vectorLength,
double vectorBearing) {
double bearingDifference = UtilityFunctions.bearingDifferenceDegrees(bearing, vectorBearing);
double distance = vectorLength * Math.cos(Math.PI * bearingDifference / 180);
return distance;
}
/**
* Reverses the order of all legs in the series, so in a linear chain of stations the last
* station is placed first, and the first station is placed last.
* Was written to Toporobot export, but then was not needed. Leaving the code here but
* commented out as not currently used.
public void reverseSeries() {
//Loop through all legs, moving each one to the start of the series
for (int idx = 0; idx < legs.size(); idx++) {
SurveyLeg leg = legs.remove(idx);
//Reverse leg direction
SurveyStation stn = leg.getFromStn();
leg.setFromStn( leg.getToStn() );
leg.setToStn( stn );
double compass = leg.getCompass();
if ( compass > 180 ) {
compass -= 180;
}
else {
compass += 180;
}
leg.setCompass( compass );
leg.setClino( -leg.getClino() );
//Put leg at start of series
legs.add(0, leg);
}
}
*/
}
CaveConverter_src/src/footleg/cavesurvey/data/model/SurveyStation.java 0000644 0000000 0000000 00000007446 12621447114 025343 0 ustar root root /**
* Copyright (C) 2009-2015 Paul Fretwell - aka 'Footleg' (drfootleg@gmail.com)
*
* This file is part of Cave Converter.
*
* Cave Converter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cave Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cave Converter. If not, see .
*/
package footleg.cavesurvey.data.model;
/**
* Represents a survey station in a cave survey. A station is a specific location in
* the cave which might be marked, or a natural feature which can be described in a
* comment, or it might be a temporary point which is not marked but was just used
* to join two legs during the survey.
*
* @author Footleg
* @version 2015.08.31 (ISO 8601 YYYY.MM.DD)
* @since 1.6 (The Java version used)
*
*/
public class SurveyStation {
/**
* Create station directly specifying an id number
* @param id ID number of the station
*/
public SurveyStation(int id) {
super();
this.id = id;
}
private int id = -1;
private String name = "";
private String comment = "";
//FIXED POSITION
private FixType fixType = FixType.NONE;
private double easting;
private double northing;
private double altitude;
private boolean entrance = false;
public enum FixType {
NONE (0),
GPS (1),
OTHER (2);
FixType( int type ) {
}
}
public boolean isEntrance() {
return ( entrance );
}
public void setEntrance(boolean entrance) {
this.entrance = entrance;
}
public boolean isFixed() {
return ( fixType != FixType.NONE );
}
public FixType getFixType() {
return fixType;
}
public double getEasting() {
return easting;
}
public double getNorthing() {
return northing;
}
public double getAltitude() {
return altitude;
}
/**
* Sets a fixed position for the station.
*
* @param fixType The type of fix (GPS, other)
* @param easting Grid position (in metres) of the fix position
* @param northing Grid position (in metres) of the fix position
* @param altitude Elevation (in metres) of the fix position
*/
public void setFixed(FixType fixType, double easting, double northing, double altitude) {
this.fixType = fixType;
this.easting = easting;
this.northing = northing;
this.altitude = altitude;
}
public void clearFixedStn() {
this.fixType = FixType.NONE;
this.easting = 0;
this.northing = 0;
this.altitude = 0;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
String stnName = this.name;
//Return number as name if no name set
if (stnName.length() == 0) {
stnName = "" + this.id;
}
return stnName;
}
public void setName(String name) {
this.name = name;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public SurveyStation clone() {
SurveyStation clone = new SurveyStation( this.id );
clone.name = this.name;
clone.comment = this.comment;
clone.entrance = this.entrance;
clone.fixType = this.fixType;
clone.easting = this.easting;
clone.northing = this.northing;
clone.altitude = this.altitude;
return clone;
}
}
CaveConverter_src/src/footleg/cavesurvey/data/model/Equate.java 0000644 0000000 0000000 00000006025 12621447114 023720 0 ustar root root /**
* Copyright (C) 2009-2015 Paul Fretwell - aka 'Footleg' (drfootleg@gmail.com)
*
* This file is part of Cave Converter.
*
* Cave Converter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cave Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cave Converter. If not, see .
*/
package footleg.cavesurvey.data.model;
/**
* Class to represent an equate between two survey stations in different series
*
* @author Footleg
* @version 2015.08.31 (ISO 8601 YYYY.MM.DD)
* @since 1.6 (The Java version used)
*
*/
public class Equate {
private String series1;
private String stn1;
private String series2;
private String stn2;
/**
* Create an equate defining the link between two stations in two series
*
* @param seriesPrefix1 Full path to the first series
* @param stnName1 Name of the station in the first series joining to the second series
* @param seriesPrefix2 Full path to the second series
* @param stnName2 Name of the station in the second series joining to the first series
*/
public Equate(String seriesPrefix1, String stnName1, String seriesPrefix2, String stnName2) {
//Need to combine series prefix with station name and then split off
//the station name from full series name
String[] resplitStnRef1 = combineAndResplitFullStnRef( seriesPrefix1, stnName1 );
String[] resplitStnRef2 = combineAndResplitFullStnRef( seriesPrefix2, stnName2 );
this.series1 = resplitStnRef1[0];
this.stn1 = resplitStnRef1[1];
this.series2 = resplitStnRef2[0];
this.stn2 = resplitStnRef2[1];
}
public String getSeries1() {
return series1;
}
public String getStn1() {
return stn1;
}
public String getSeries2() {
return series2;
}
public String getStn2() {
return stn2;
}
private String[] combineAndResplitFullStnRef( String seriesPrefix, String stnName ) {
String[] results = new String[2];
//Combine prefix and station name into fully qualified station reference
String fullStnRef = seriesPrefix + "." + stnName;
//Split series name from station name
int pos = fullStnRef.lastIndexOf('.');
if ( pos > 0 ) {
//Split at this position
results[0] = fullStnRef.substring( 0, pos );
results[1] = fullStnRef.substring( pos + 1 );
}
else {
//No '.' separator in equate, so throw error
throw new RuntimeException( "Equate does not contain a series name and station." );
}
return results;
}
}
CaveConverter_src/src/footleg/cavesurvey/data/model/SurveyLeg.java 0000644 0000000 0000000 00000025475 13034706132 024430 0 ustar root root /**
* Copyright (C) 2009-2017 Paul Fretwell - aka 'Footleg' (drfootleg@gmail.com)
*
* This file is part of Cave Converter.
*
* Cave Converter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cave Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cave Converter. If not, see .
*/
package footleg.cavesurvey.data.model;
import footleg.cavesurvey.converter.CaveConverter.BearingUnit;
import footleg.cavesurvey.converter.CaveConverter.GradientUnit;
import footleg.cavesurvey.converter.CaveConverter.LengthUnit;
import footleg.cavesurvey.tools.UtilityFunctions;
/**
* Represents a leg in a cave survey. A leg is a set of measurements which define the
* location of a point in the cave relative to another point (a survey station). The
* second point defined by the leg can be another survey station or may just be a point
* in the cave on the wall or used to measure the position of a significant feature.
* Legs can be normal (defined in terms of length, bearing and inclination), or diving.
* Diving legs are defined in terms of a length, bearing and change in depth. The change
* in depth can be defined explicitly using the depths of both the from and to stations,
* or in terms of just a change in depth. If the leg is defined as a 'depth change' leg
* then this is stored in the toDepth property (the fromDepth property is set to a
* value of 999999m).
* Data is stored and calculations done in metric units (metres and degrees), but
* conversion to other units on input/output is supported.
*
* @author Footleg
* @version 2017.01.09 (ISO 8601 YYYY.MM.DD)
* @since 1.6 (The Java version used)
*
* @to.do
* TODO Add method to convert diving leg into conventional measurements (length,bearing,gradient)
*/
public class SurveyLeg implements Comparable {
private static final int nullDepth = 999999;
private SurveyStation fromStn;
private SurveyStation toStn;
private double length = -1;
private double fromDepth = nullDepth;
private double toDepth = nullDepth;
private double compass = -1;
private double clino = -99;
private String comment = "";
//LRUD applies to fromStn
private double left = 0.0;
private double right = 0.0;
private double up = 0.0;
private double down = 0.0;
//Leg flags
private boolean splay = false;
private boolean duplicate = false;
private boolean surface = false;
private boolean diving = false;
private boolean nosurvey = false;
/**
* Support sorting by from station name, then to station name
*/
public int compareTo(SurveyLeg anotherLeg) {
SurveyLeg leg = (SurveyLeg)anotherLeg;
int fromTest = this.fromStn.getId() - leg.getFromStn().getId();
if ( fromTest == 0 ) {
//Same from station, so use toStn to sort
return this.toStn.getId() - leg.getToStn().getId();
}
else {
return fromTest;
}
}
/**
* Provides a string representation of a cave survey leg to display in a tree view of the data model
* @return String representation of survey leg class
*/
public String toString() {
String text = getFromStn().getName();
if ( (toStn != null) && ( toStn.getName() != null ) ) {
text += " - " + getToStn().getName();
}
return text;
}
/**
* @return A copy of the survey leg
*/
public SurveyLeg clone() {
SurveyLeg clone = new SurveyLeg();
if (fromStn != null){
clone.fromStn = fromStn.clone();
}
if (toStn != null){
clone.toStn = toStn.clone();
}
clone.length = length;
clone.compass = compass;
clone.clino = clino;
clone.fromDepth = fromDepth;
clone.toDepth = toDepth;
clone.comment = comment;
clone.left = left;
clone.right = right;
clone.up = up;
clone.down = down;
clone.splay = splay;
clone.duplicate = duplicate;
clone.surface = surface;
clone.diving = diving;
clone.nosurvey = nosurvey;
return clone;
}
/**
* Reverses the direction of a survey leg
*/
public void reverseDirection() {
//Reverse leg direction
SurveyStation stn = getFromStn();
setFromStn( getToStn() );
setToStn( stn );
double compass = getCompass(BearingUnit.Degrees);
if ( compass > 180 ) {
compass -= 180;
}
else {
compass += 180;
}
setCompass( compass, BearingUnit.Degrees );
if ( isDiving() ) {
if ( isDepthChangeLeg() ) {
//Depth change, so change sign on toDepth to reverse depth change
toDepth = -toDepth;
}
else {
//Specific depths, so swap them
double tmpDepth = fromDepth;
fromDepth = toDepth;
toDepth = tmpDepth;
}
//Swap clino value avoiding setter so isDiving flag is not unset, just in case it had a value
this.clino = -getClino(GradientUnit.Degrees);
}
else {
setClino( -getClino(GradientUnit.Degrees), GradientUnit.Degrees );
//Reset depths to null depth in case they had values
this.fromDepth = nullDepth;
this.toDepth = nullDepth;
}
}
/**
* Calculates the horizontal length of the leg
* @return calculated horizontal length
*/
public double getHorizontalLength() {
double hLength = Math.abs( length * Math.cos( Math.toRadians( clino ) ) );
return hLength;
}
/**
* Calculates the vertical height of the leg
* @return calculated vertical height
*/
public double getVerticalLength() {
double vLength = Math.abs( length * Math.sin( Math.toRadians( clino ) ) );
return vLength;
}
//Getters and Setters
public SurveyStation getFromStn() {
return fromStn;
}
public void setFromStn(SurveyStation fromStn) {
this.fromStn = fromStn;
}
public SurveyStation getToStn() {
return toStn;
}
public void setToStn(SurveyStation toStn) {
this.toStn = toStn;
}
public double getLength(LengthUnit units) {
return UtilityFunctions.lengthFromMetres( length, units );
}
public void setLength(double length, LengthUnit units) {
this.length = UtilityFunctions.lengthToMetres(length, units);
}
public double getCompass(BearingUnit units) {
return UtilityFunctions.bearingFromDegrees(compass, units);
}
public void setCompass(double compass, BearingUnit units) {
this.compass = UtilityFunctions.bearingToDegrees(compass, units);
}
/**
* TODO Calculate angle for diving legs
* @param units Units to return the value in
* @return Gradient of leg in the specified units
*/
public double getClino(GradientUnit units) {
return UtilityFunctions.gradientFromDegrees(clino, units);
}
/**
* Setting this sets the leg to a non-diving (normal) type leg.
* @param clino Gradient value to set for the leg, in the units specified
* @param units Units of the specified value
*/
public void setClino(double clino, GradientUnit units) {
this.clino = UtilityFunctions.gradientToDegrees(clino, units);
diving = false;
}
public double getLeft(LengthUnit units) {
return UtilityFunctions.lengthFromMetres( left, units );
}
public void setLeft(double left, LengthUnit units) {
this.left = UtilityFunctions.lengthToMetres(left, units);
}
public double getRight(LengthUnit units) {
return UtilityFunctions.lengthFromMetres( right, units );
}
public void setRight(double right, LengthUnit units) {
this.right = UtilityFunctions.lengthToMetres(right, units);
}
public double getUp(LengthUnit units) {
return UtilityFunctions.lengthFromMetres( up, units );
}
public void setUp(double up, LengthUnit units) {
this.up = UtilityFunctions.lengthToMetres(up, units);
}
public double getDown(LengthUnit units) {
return UtilityFunctions.lengthFromMetres( down, units );
}
public void setDown(double down, LengthUnit units) {
this.down = UtilityFunctions.lengthToMetres(down, units);
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public boolean isSplay() {
return splay;
}
public void setSplay(boolean splay) {
this.splay = splay;
if ( splay == true ) {
this.nosurvey = false;
}
}
public boolean isDuplicate() {
return duplicate;
}
public void setDuplicate(boolean duplicate) {
this.duplicate = duplicate;
}
public boolean isSurface() {
return surface;
}
public void setSurface(boolean surface) {
this.surface = surface;
}
/**
* @param units Length units to return the value in
* @return The depth change for the diving leg
*/
public double getDepthChange(LengthUnit units) {
if ( isDepthChangeLeg() ) {
return UtilityFunctions.lengthFromMetres( toDepth, units );
}
else {
return UtilityFunctions.lengthFromMetres( toDepth - fromDepth, units );
}
}
/**
* Setting this sets the leg to a diving type leg.
* @param depthChange The depth change to set
* @param units Units of the specified depth change
*/
public void setDepthChange(double depthChange, LengthUnit units) {
this.fromDepth = nullDepth;
this.toDepth = UtilityFunctions.lengthToMetres(depthChange, units);
this.diving = true;
this.nosurvey = false;
}
/**
* @param units Length units to return the value in
* @return The from Depth for a diving leg
*/
public double getFromDepth(LengthUnit units) {
return UtilityFunctions.lengthFromMetres( fromDepth, units );
}
/**
* @param units Length units to return the value in
* @return The to Depth for a diving leg
*/
public double getToDepth(LengthUnit units) {
return UtilityFunctions.lengthFromMetres( toDepth, units );
}
/**
* Setting this sets the leg to a diving type leg.
* @param fromDepth the depth of the from station
* @param toDepth the depth of the to station
* @param units Units of the specified depth measurements
*/
public void setDepths(double fromDepth, double toDepth, LengthUnit units) {
this.fromDepth = UtilityFunctions.lengthToMetres(fromDepth, units);
this.toDepth = UtilityFunctions.lengthToMetres(toDepth, units);
this.diving = true;
this.nosurvey = false;
}
public boolean isDiving() {
return diving;
}
public boolean isDepthChangeLeg() {
return ( diving && fromDepth == nullDepth );
}
public boolean isNosurvey() {
return nosurvey;
}
public void setNosurvey(boolean nosurvey) {
this.nosurvey = nosurvey;
if ( nosurvey == true) {
this.splay = false;
}
}
}
CaveConverter_src/src/footleg/cavesurvey/data/model/SeriesLink.java 0000644 0000000 0000000 00000003675 12621447114 024554 0 ustar root root /**
* Copyright (C) 2009-2013 Paul Fretwell - aka 'Footleg' (drfootleg@gmail.com)
*
* This file is part of Cave Converter.
*
* Cave Converter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cave Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cave Converter. If not, see .
*/
package footleg.cavesurvey.data.model;
/**
* Represents a link or join between two survey series. The link specifies a series and
* station for each of the two series being joined which are the same point in the cave.
* The series link lives in a series which both the linked series are inside (or the
* same as).
*
* @author Footleg
* @version 2013.01.20 (ISO 8601 YYYY.MM.DD)
* @since 1.6 (The Java version used)
*
*/
public class SeriesLink {
private String series1;
private String series2;
private SurveyStation stn2;
private SurveyStation stn1;
public SeriesLink(String series1, SurveyStation stn1, String series2, SurveyStation stn2) {
this.series1 = series1;
this.series2 = series2;
this.stn1 = stn1;
this.stn2 = stn2;
}
public String getSeries1() {
return series1;
}
public String getSeries2() {
return series2;
}
public SurveyStation getStn2() {
return stn2;
}
public SurveyStation getStn1() {
return stn1;
}
public void setSeries2(String series2) {
this.series2 = series2;
}
}
CaveConverter_src/src/footleg/cavesurvey/data/writer/ 0000755 0000000 0000000 00000000000 13036467352 022051 5 ustar root root CaveConverter_src/src/footleg/cavesurvey/data/writer/CompassWriter.java 0000644 0000000 0000000 00000026277 13034707276 025534 0 ustar root root /**
* Copyright (C) 2009-2017 Paul Fretwell - aka 'Footleg' (drfootleg@gmail.com)
*
* This file is part of Cave Converter.
*
* Cave Converter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cave Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cave Converter. If not, see .
*/
package footleg.cavesurvey.data.writer;
import java.util.ArrayList;
import java.util.List;
import footleg.cavesurvey.converter.CaveConverter;
import footleg.cavesurvey.converter.Logger;
import footleg.cavesurvey.converter.CaveConverter.BearingUnit;
import footleg.cavesurvey.converter.CaveConverter.GradientUnit;
import footleg.cavesurvey.converter.CaveConverter.LengthUnit;
import footleg.cavesurvey.data.model.CaveSurvey;
import footleg.cavesurvey.data.model.SurveyLeg;
import footleg.cavesurvey.data.model.SurveySeries;
/**
* Writer for Compass file format text data.
*
* @author Footleg
* @version 2017.01.09 (ISO 8601 YYYY.MM.DD)
* @since 1.6 (The Java version used)
*/
public class CompassWriter {
private List shortNames = new ArrayList();
private Logger logger;
public CompassWriter( Logger logger ) {
super();
this.logger = logger;
}
/**
* Generates Compass format data from a cave survey
*
* THIS CLASS IS NOT FULLY IMPLEMENTED YET AND WILL NOT GENERATE VALID DATA
*
* @param surveyData The cave survey model to generate Compass data for
* @return Text lines of Compass format data
*/
public List generateCompassData( CaveSurvey surveyData ) {
List outputData = new ArrayList();
String caveName = "cave";
//See if we can determine cave name from series name of first series
SurveySeries series1 = surveyData.get(0);
if ( series1.getSeriesName().length() > 0 ) {
//Check for dot separator
int pos = series1.getSeriesName().indexOf('.');
if ( pos > 0) {
//Take name before first separator
caveName = series1.getSeriesName().substring(0, pos);
}
else {
//Work backwards until first non-numeric character found
for ( int nameIdx = series1.getSeriesName().length() - 1; nameIdx > -1; nameIdx-- ) {
//Check for non-numerical character
int charCode = series1.getSeriesName().charAt(nameIdx);
if ( charCode < 48 || charCode > 57 ) {
//Take name prior to this point
caveName = series1.getSeriesName().substring(0, nameIdx + 1);
nameIdx = 0;
}
}
}
}
//Generate short names for all series
for ( int seriesIdx = 0; seriesIdx < surveyData.size(); seriesIdx++ ) {
String seriesName = surveyData.get(seriesIdx).getSeriesName();
String reducedName = "";
/* //Remove cave name prefix if found
if ( seriesName.substring(0, caveName.length() + 1 ).compareTo( caveName + "." ) == 0 ) {
seriesName = seriesName.substring(caveName.length() + 1);
}
*/
//Look for last part of multi-part names
String nameLastPart = seriesName;
int lstIdx = seriesName.lastIndexOf('.');
if ( lstIdx > -1 && lstIdx < (seriesName.length() - 1) ) {
//Take just last part of name
nameLastPart = seriesName.substring(lstIdx + 1);
}
//Remove illegal characters
for ( int idx = 0; reducedName.length() < 4 && idx < nameLastPart.length(); idx ++ ) {
if ( nameLastPart.charAt(idx) != '.' ) {
reducedName += nameLastPart.charAt(idx);
}
}
int trys = 0;
while ( trys < 110 ) {
trys++;
String testName = " ";
switch (trys) {
case 1:
//Try using first 4 chars of name padded with leading spaces
testName = reducedName;
break;
case 2:
//Try removing vowels
reducedName = "";
for ( int idx = 0; reducedName.length() < 4 && idx < nameLastPart.length(); idx ++ ) {
if ( nameLastPart.charAt(idx) != '.'
&& nameLastPart.charAt(idx) != 'a'
&& nameLastPart.charAt(idx) != 'e'
&& nameLastPart.charAt(idx) != 'i'
&& nameLastPart.charAt(idx) != 'o'
&& nameLastPart.charAt(idx) != 'u'
&& nameLastPart.charAt(idx) != 'A'
&& nameLastPart.charAt(idx) != 'E'
&& nameLastPart.charAt(idx) != 'I'
&& nameLastPart.charAt(idx) != 'O'
&& nameLastPart.charAt(idx) != 'U'
) {
reducedName += nameLastPart.charAt(idx);
}
}
testName += reducedName;
break;
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
//Try adding increment number to end of 3 char name
testName += reducedName.substring(0,3) + ( trys - 2 );
break;
default :
//Try adding increment number to end of 2 char name
testName += reducedName.substring(0,2) + ( trys - 11 );
}
//Trim to 4 chars
testName = testName.substring( testName.length() - 4 );
//Check if name already used
if ( nameAlreadyUsed( testName ) == false ) {
//Use this name
shortNames.add( testName );
trys = 999;
}
}
//Check name was added
if (trys != 999) {
RuntimeException ex = new RuntimeException("Failed to generate unique short name for series name: " + seriesName );
throw ex;
}
}
//Loop through all series
for ( int seriesIdx = 0; seriesIdx < surveyData.size(); seriesIdx++ ) {
SurveySeries series = surveyData.get(seriesIdx);
List legsData = new ArrayList();
List fixedStnsData = new ArrayList();
List passageData = new ArrayList();
//Write header lines for series
outputData.add( caveName );
outputData.add( "SURVEY NAME: " + series.getSeriesName() );
outputData.add( "SURVEY DATE: " + series.getSurveyDate() );
outputData.add( "SURVEY TEAM: ");
outputData.add( " ");
outputData.add( "DECLINATION: " + series.getCompassCalibration(BearingUnit.Degrees) + " FORMAT: DMMDLRUDLADN CORRECTIONS: 0.00 0.00 0.00");
outputData.add( " ");
outputData.add( "FROM TO LENGTH BEARING INC LEFT UP DOWN RIGHT FLAGS COMMENTS");
/*
//Add equates to output
for ( int linkIdx = 0; linkIdx < series.getLinks().size(); linkIdx++ ) {
SeriesLink link = series.getLinks().get(linkIdx);
String equate = "*EQUATE ";
if ( link.getLinkedSeries() == -1 ) {
//Link to series not in current data file, so create 'false' equate placeholder in file
equate = ";" + equate + series.getSeriesName() + "." + link.getLocalStation().getName() + " " +
"CAVE" + "." +
link.getLocalStation();
}
else {
equate += series.getSeriesName() + "." + link.getLocalStation().getName() + " " +
surveyData.get( link.getLinkedSeries() ).getSeriesName() + "." +
link.getLinkedStation().getName();
}
outputData.add( equate );
}
outputData.add( "");
*/
//Start series block
outputData.add( "*BEGIN " + series.getSeriesName() );
//TODO Write date if present
// if ( series.getSurveyDate().length() > 0 ) {
// compassData.add( ";Date " + series.getSurveyDate() );
// }
//Write calibration lines
if ( series.getTapeCalibration(LengthUnit.Metres) != 0 ) {
outputData.add( "*CALIBRATE tape " + series.getTapeCalibration(LengthUnit.Feet) );
}
if ( series.getCompassCalibration(BearingUnit.Degrees) != 0 ) {
outputData.add( "*CALIBRATE declination " + series.getCompassCalibration(BearingUnit.Degrees) );
}
if ( series.getClinoCalibration(GradientUnit.Degrees) != 0 ) {
outputData.add( "*CALIBRATE clino " + series.getClinoCalibration(GradientUnit.Degrees) );
}
outputData.add( "");
//Loop through the series legs writing details of each leg found
for ( int legIdx = 0; legIdx < series.legCount(); legIdx++ ) {
SurveyLeg leg = series.getLegRaw(legIdx);
//Check for valid leg
String fromStn = leg.getFromStn().getName();
String toStn = leg.getToStn().getName();
if ( leg.getLength( LengthUnit.Metres ) > 0 ) {
//Write leg data
legsData.add( fromStn + "\t" + toStn + "\t" +
CaveConverter.padNumber( leg.getLength(LengthUnit.Feet), 2, 5 ) + "\t" +
CaveConverter.padNumber( leg.getCompass(BearingUnit.Degrees), 2, 6 ) + "\t" +
CaveConverter.padNumber( leg.getClino(GradientUnit.Degrees), 2, 6 ) + "\t");
}
else {
//Zero length leg
//Get to stn name if there is one
if ( leg.isSplay() == false ) {
//Valid leg, so write as equate
legsData.add( "*EQUATE " + fromStn + "\t" + toStn );
}
}
//Add FIXed points to fixed stns block for series
// if (leg.hasFixedStn() == true) {
// String fixedStnLine = "*FIX ";
// if ( leg.isFromStnFixed() == true ) {
// fixedStnLine += fromStn;
// }
// else {
// fixedStnLine += getActualStationName( series, leg.getToStn() );
// }
// fixedStnLine += "\t" + leg.getEasting() + "\t" + leg.getNorthing() + "\t" +
// leg.getAltitude();
// fixedStnsData.add( fixedStnLine );
// }
//Add LRUD to passages block for series
if ( leg.getLeft(LengthUnit.Metres) + leg.getRight(LengthUnit.Metres) +
leg.getUp(LengthUnit.Metres) + leg.getDown(LengthUnit.Metres) > 0.0 ) {
passageData.add( fromStn + "\t" + leg.getLeft(LengthUnit.Feet) + "\t" +
leg.getRight(LengthUnit.Feet) + "\t" + leg.getUp(LengthUnit.Feet) + "\t" +
leg.getDown(LengthUnit.Feet) + "\t" );
}
}
//Write fixes data block
if ( fixedStnsData.size() > 0 ) {
outputData.addAll( fixedStnsData );
}
//Write legs data block
outputData.addAll( legsData );
//Write passage data block
if ( passageData.size() > 0 ) {
outputData.add( "");
outputData.add( "*data passage station left right up down");
outputData.addAll( passageData );
}
//Close the series
outputData.add( "*END " + series.getSeriesName() + "\n");
}
//Close the cave name
outputData.add( "*END " + caveName );
return outputData;
}
/**
* Looks up short series name for series from array of names created previously
* @param seriesIdx
* @return short (4 character) name for series
*/
private String getShortSeriesName( int seriesIdx ) {
return shortNames.get(seriesIdx);
}
/**
* Looks for short series name in array of names used
* @param name
* @return true if name has already been used for another series
*/
private boolean nameAlreadyUsed( String name ) {
boolean found = false;
for ( int i = 0; i < shortNames.size(); i++ ) {
if ( shortNames.get(i).compareToIgnoreCase( name ) == 0 ) {
found = true;
i = shortNames.size();
}
}
return found;
}
}
CaveConverter_src/src/footleg/cavesurvey/data/writer/TopoRobotWriter.java 0000644 0000000 0000000 00000052223 13034772722 026042 0 ustar root root /**
* Copyright (C) 2009-2017 Paul Fretwell - aka 'Footleg' (drfootleg@gmail.com)
*
* This file is part of Cave Converter.
*
* Cave Converter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cave Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cave Converter. If not, see .
*/
package footleg.cavesurvey.data.writer;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.ListIterator;
import footleg.cavesurvey.converter.CaveConverter;
import footleg.cavesurvey.converter.Logger;
import footleg.cavesurvey.converter.CaveConverter.BearingUnit;
import footleg.cavesurvey.converter.CaveConverter.GradientUnit;
import footleg.cavesurvey.converter.CaveConverter.LengthUnit;
import footleg.cavesurvey.data.model.CaveSurvey;
import footleg.cavesurvey.data.model.SeriesLink;
import footleg.cavesurvey.data.model.SurveyLeg;
import footleg.cavesurvey.data.model.SurveySeries;
import footleg.cavesurvey.data.model.SurveySeries.ToStnLRUD;
import footleg.cavesurvey.tools.UtilityFunctions;
/**
* Writer for TopoRobot file format text data.
*
* @author Footleg
* @version 2017.01.09 (ISO 8601 YYYY.MM.DD)
* @since 1.6 (The Java version used)
*/
public class TopoRobotWriter {
private List terminalLRUDCache;
private Logger logger;
public TopoRobotWriter( Logger logger ) {
super();
this.logger = logger;
}
/**
* Generates TopoRobot format data from a cave survey
*
* @param surveyData The cave survey model to generate TopoRobot data for
* @param defaultDate The date to put in the data header
* @param outputSplays Flag to switch on/off whether splay legs are included in the output
* @return Text lines of TopoRobot format data
*/
public List generateToporobotData( CaveSurvey surveyData, Date defaultDate, boolean outputSplays ) {
List outputData = new ArrayList();
terminalLRUDCache = new ArrayList();
/**
* Pre-process series data to make it suitable for toporobot export. Toporobot only supports
* numeric station names, and linear chains of survey legs (i.e. No branching in a survey
* series). To convert the data to a suitable structure from a hierarchical nested set of
* potentially branched survey series the following processes are applied to the data.
* 1) Create a new list of survey series where no series contains any inner series. All
* series are named using the fully expanded original hierarchical name for the series.
* All links between series are stored in one of the series involved in the link rather
* than at a higher level outside the series.
* 2) All branches in the resulting series are removed by spliting each series containing
* a branch at the junction.
* 3) Merge series which are joined end to end into a single series to minimise the number
* of linear series in the survey.
* 4) Split series which are not linked by their start stations at the linking station so
* that all series links use only the first station to link to another series.
*
* The result of this is that the naming of series will no longer reflect the original
* naming of series form the input survey, and stations will be renumbered.
*
* TODO This writer has some issues. Some surveys with multiple loops can get split into
* two parts which are not linked to each other. Some links have comments indicating the
* stations are id -1 and some series report they are not linked to any others.
*/
logger.logMessage("Generating Toporobot format data...");
//Convert all series including nested series into one big series with full qualified names in links cache
SurveySeries rawData = convertToSingleSeries(surveyData, outputSplays);
//Convert this to linear series
SurveySeries processedSurveyData = UtilityFunctions.convertToLinearSeries( rawData, logger );
List preferredLinkSeriesCache = new ArrayList();
//Generate formatted date and time strings for headers
String dateFormat = "yy/MM/dd HH:mm:ss";
String dateTime = UtilityFunctions.dateToString(defaultDate, dateFormat);
String todaysDate = dateTime.substring(6, 8) + '/' + dateTime.substring(3, 5) + '/' + dateTime.substring(0, 2);
//Create file header
outputData.add( " -6 1 1 1 1 Cave Name");
outputData.add( " -5 1 1 1 1 0.00 0.00 0.00 1 0");
outputData.add( " -4 1 1 1 1 " + dateTime + " CaveConverter");
outputData.add( " -3 1 1 1 1");
outputData.add( " -2 1 1 1 1 " + todaysDate + " Converted Data 0 0.00 0 1");
outputData.add( " -1 1 1 1 1 360.00 360.00 0.05 1.00 1.00 100.00 0.00");
//Generate data lines from survey data
List links = processedSurveyData.getLinks();
ListIterator seriesItr = processedSurveyData.getInnerSeriesList().listIterator();
int seriesNo = 0; //1 based index of stations, for writing directly into file
while ( seriesItr.hasNext() ) {
SurveySeries series = seriesItr.next();
seriesNo++;
//Generate series header
outputData.add( CaveConverter.padNumber(seriesNo,6) + " -2 1 1 1 Series " + series.getSeriesName() );
//Initialise links data line
String linkLine = CaveConverter.padNumber(seriesNo,6) + " -1 1 1 1";
//Initialise link from ourself to ourself, as that is how toborobot represents unlinked series
int linkSeries1 = seriesNo;
int linkStn1 = 0;
int linkSeries2 = seriesNo;
int linkStn2 = series.legCount();
boolean foundFirstLink = false;
//Now find the links to any series this one starts or ends at a connection with
//Loop through links looking for links to start stn or end stn of this series
for (int i = 0; i < links.size(); i++ ) {
//Check whether this link is to this series
if ( series.getSeriesName() == links.get(i).getSeries1() ) {
//Found link to our series, check if it is to start or end of this series
int linkType = 0; //0=not to this series, 1=to start of this series, 2=to end of this series
if ( series.getLegRaw(0).getFromStn().getId() == links.get(i).getStn1().getId() ) {
linkType = 1;
}
else if ( series.getLegRaw( series.legCount() - 1 ).getToStn().getId() == links.get(i).getStn1().getId() ) {
linkType = 2;
}
if ( linkType > 0 ) {
//Found a link to this series, so determine the index of the linked series
int linkSeriesIdx = -1;
int j = 0;
String series2Name = links.get(i).getSeries2();
while ( linkSeriesIdx < 0 && j < processedSurveyData.getInnerSeriesList().size() ) {
if ( processedSurveyData.getInnerSeriesList().get(j).getSeriesName() == series2Name ) {
linkSeriesIdx = j;
}
j++;
}
if ( linkSeriesIdx >= 0 ) {
//Found the index of the parent series, now need to find the index of the link station
//Stn number should be 1 based index of station in series,
//not the actual value stored in that series
int idx = 0;
int linkStnIdx = -1;
int linkStnId = links.get(i).getStn2().getId();
/*
* Cache series index as preferred for this stn number if not already found.
* This is so that where multiple series all branch off from the same point,
* all the links are to the same one of these series. Otherwise we can end
* up with multiple sets of series linked to the others in the group, but
* the groups not connected to each other. This preferred series cache
* ensures that the first series found is used for all other links to
* series starting at this station.
*/
int preferredLinkSeries = 0 ;
//Look for this stn num in cache
for ( int k = 0; k < preferredLinkSeriesCache.size(); k++ ) {
if ( preferredLinkSeriesCache.get(k)[0] == linkStnId ) {
//Found, so get preferred series from cache
preferredLinkSeries = preferredLinkSeriesCache.get(k)[1];
k = preferredLinkSeriesCache.size();
}
}
//If not found in cache then cache this series as the preferred one
if ( preferredLinkSeries == 0 ) {
int[] rec = new int[2];
rec[0] = linkStnId;
rec[1] = linkSeriesIdx;
preferredLinkSeriesCache.add(rec);
}
//Get link series
SurveySeries linkSeries;
if ( ( preferredLinkSeries > 0 ) && ( preferredLinkSeries != seriesNo ) ) {
//Use preferred series instead of the given link
linkSeries = processedSurveyData.getInnerSeries(preferredLinkSeries);
linkSeriesIdx = preferredLinkSeries;
}
else {
linkSeries = processedSurveyData.getInnerSeries(linkSeriesIdx);
}
while ( linkStnIdx == -1) {
//Check if stn name matches our link name
if ( idx >= linkSeries.legCount() ) {
//Check if link is to final stn
if ( linkSeries.getLegRaw(idx-1).getToStn().getId() == linkStnId ) {
//Found match
linkStnIdx = idx;
}
else {
//No matches, set value to cause loop to exit
linkStnIdx = -2;
}
}
else if ( linkSeries.getLegRaw(idx).getFromStn().getId() == linkStnId ) {
//Found match
linkStnIdx = idx;
}
else {
//Keep looking
idx++;
}
}
//We have now identified the series number and station number for the link
int linkSeriesNum = linkSeriesIdx + 1;
//Set link to start or end station
if ( linkType == 1 ) {
linkSeries1 = linkSeriesNum;
linkStn1 = linkStnIdx;
if ( foundFirstLink == false ) {
//This is first link for this series, so set flag
foundFirstLink = true;
}
else {
//This is 2nd link to this series, so stop looking for more
break;
}
}
else {
linkSeries2 = linkSeriesNum;
linkStn2 = linkStnIdx;
if ( foundFirstLink == false ) {
//This is first link for this series, so set flag
foundFirstLink = true;
}
else {
//This is 2nd link to this series, so stop looking for more
break;
}
}
}
}
}
}
//Add link details to data line: linked series no, linked station index, this series number, first station (0)
linkLine += CaveConverter.padNumber(linkSeries1,8) + CaveConverter.padNumber(linkStn1,8);
linkLine += CaveConverter.padNumber(linkSeries2,8) + CaveConverter.padNumber(linkStn2,8);
//Finish line
linkLine += CaveConverter.padNumber(series.legCount(),8) + " 3 0";
outputData.add( linkLine );
//First line is LRUD for first station
String blankLRUD = " 0.00 0.00 0.00 0.00";
String lrud = blankLRUD;
if ( series.legCount() > 0 ) {
lrud = lrudForLeg( series.getLegCorrected( 0 ) );
}
outputData.add( CaveConverter.padNumber(seriesNo,6) + CaveConverter.padNumber(0,6) + " 1 1 1" +
" 0.00 0.00 0.00" + lrud);
//Loop through legs in series to write out remaining stations
for (int legIdx = 0; legIdx < series.legCount(); legIdx++ ) {
SurveyLeg leg = series.getLegCorrected(legIdx);
//Use lrud from next leg as toporobot puts lrud on 'to' station of each leg
lrud = blankLRUD;
if ( series.legCount() > legIdx + 1 ) {
lrud = lrudForLeg( series.getLegCorrected( legIdx + 1 ) );
}
else if ( series.legCount() == legIdx + 1 ) {
//Last leg in chain, so check for cached LRUD data for toStn
ListIterator legIter = terminalLRUDCache.listIterator();
while (legIter.hasNext() ) {
ToStnLRUD toStnLrudData = legIter.next();
if ( toStnLrudData.getFromStn().getName().compareTo( leg.getToStn().getName() ) == 0 ) {
//Copy terminal station LRUD data to temp leg to convert to data string
SurveyLeg tempLeg = new SurveyLeg();
tempLeg.setLeft( toStnLrudData.getLeft(), LengthUnit.Metres );
tempLeg.setRight( toStnLrudData.getRight(), LengthUnit.Metres );
tempLeg.setUp( toStnLrudData.getUp(), LengthUnit.Metres );
tempLeg.setDown( toStnLrudData.getDown(), LengthUnit.Metres );
lrud = lrudForLeg( tempLeg );
//Remove from iterator and exit loop
legIter.remove();
break;
}
}
}
String dataLine = CaveConverter.padNumber(seriesNo,6) + CaveConverter.padNumber( legIdx+1, 6 ) +
" 1 1 1" +
CaveConverter.padNumber( leg.getLength(LengthUnit.Metres), 2, 8 ) +
CaveConverter.padNumber( leg.getCompass(BearingUnit.Degrees), 2, 8 ) +
CaveConverter.padNumber( leg.getClino(GradientUnit.Degrees), 2, 8 ) + lrud;
outputData.add( dataLine );
}
}
return outputData;
}
/**
* Generate string representation of LRUD data for a survey leg
* @param leg Input leg
* @return String representation of LRUD data
*/
private String lrudForLeg(SurveyLeg leg) {
double down = leg.getDown(LengthUnit.Metres);
if ( down < 0 ) {
down = 0.0;
}
return CaveConverter.padNumber(leg.getLeft(LengthUnit.Metres),2,8) + CaveConverter.padNumber(leg.getRight(LengthUnit.Metres),2,8) +
CaveConverter.padNumber(leg.getUp(LengthUnit.Metres),2,8) + CaveConverter.padNumber(down,2,8);
}
private void addLegsToMasterSeries( SurveySeries series, SurveySeries rawData, String parentSeriesPrefix,
List> linkCache, boolean outputSplays ) {
//Copy legs from this series into master series, translating using links cache when matching
for ( int j = 0; j < series.legCount(); j++ ) {
SurveyLeg leg = series.getLegCorrected(j);
//Ignore splay legs if output splays option is set to false
if ( ( ( outputSplays == false ) && leg.isSplay() ) == false ) {
//Get fully expanded station names for leg
String fromStn = parentSeriesPrefix + "." + series.getSeriesName() + "." +
leg.getFromStn().getName();
String toStn = parentSeriesPrefix + "." + series.getSeriesName() + "." +
leg.getToStn().getName();
/*
* Check for these stn names in links cache and if found, use name of first equivalent station
* so no links are required because all stations will be linked via a common named station
*/
for ( int k = 0; k < linkCache.size(); k++ ) {
List rec = linkCache.get(k);
for ( int m = 1; m < rec.size(); m++ ) {
if ( rec.get(m).compareToIgnoreCase( fromStn ) == 0 ) {
fromStn = rec.get(0);
}
else if ( rec.get(m).compareToIgnoreCase( toStn ) == 0 ) {
toStn = rec.get(0);
}
}
}
//Clone leg and create new stn numbers from stn full names
SurveyLeg newLeg = leg.clone();
newLeg.setFromStn( UtilityFunctions.createStationFromNameForSeries(fromStn, rawData) );
newLeg.setToStn(UtilityFunctions.createStationFromNameForSeries(toStn, rawData) );
//Add to master raw survey series
rawData.addLeg(newLeg);
}
}
//Cache LRUD data from final stations in a series
for ( int j = 0; j < series.getToStnLRUDs().size(); j++ ) {
ToStnLRUD toStnLrudData = series.getToStnLRUDs().get(j);
//Get fully expanded station name for from stn and set as stn name in cache
String fromStn = parentSeriesPrefix + "." + series.getSeriesName() + "." +
toStnLrudData.getFromStn().getName();
toStnLrudData.getFromStn().setName(fromStn);
terminalLRUDCache.add(toStnLrudData);
}
}
private void addLinksToCache(SurveySeries series, String parentSeriesPrefix, List> linkCache ){
String seriesFullName = parentSeriesPrefix + "." + series.getSeriesName();
List links = series.getLinks();
for ( int j = 0; j < links.size(); j++ ) {
//Get fully expanded linked station pair
String linkSeries1 = "";
if ( links.get(j).getSeries1().length() > 0 ) {
linkSeries1 = "." + links.get(j).getSeries1();
}
String stn1 = seriesFullName + linkSeries1 + "." +
links.get(j).getStn1().getName();
String linkSeries2 = "";
if ( links.get(j).getSeries2().length() > 0 ) {
linkSeries2 = "." + links.get(j).getSeries2();
}
String stn2 = seriesFullName + linkSeries2 + "." +
links.get(j).getStn2().getName();
/**
* Need one station name to always take precedence in all links
* so check if either of these stations is already in the cache,
* and if so add other station to the existing entry
*/
boolean newRec = true;
for ( int k = 0; k < linkCache.size(); k++ ) {
List rec = linkCache.get(k);
for ( int m = 0; m < rec.size(); m++ ) {
if ( rec.get(m).equalsIgnoreCase( stn1 ) ) {
newRec = false;
//Add other stn to this list
rec.add( stn2 );
m = rec.size();
}
else if ( rec.get(m).equalsIgnoreCase( stn2 ) ) {
newRec = false;
//Add other stn to this list
rec.add( stn1 );
m = rec.size();
}
}
}
if ( newRec ) {
//Cache this link as a new record
List rec = new ArrayList();
rec.add(stn1);
rec.add(stn2);
linkCache.add(rec);
}
}
}
private void processSeriesLinks(SurveySeries series, String masterSeriesNamePrefix, List> linkCache){
//Process links for this series
logger.logMessage("Processing links from series: " + series.getSeriesName() );
addLinksToCache( series, masterSeriesNamePrefix, linkCache );
//Process any inner series
String seriesNamePrefix = masterSeriesNamePrefix + "." + series.getSeriesName();
ListIterator iterator = series.getInnerSeriesList().listIterator();
while (iterator.hasNext() ) {
SurveySeries innerSeries = iterator.next();
//Process any inner series
processSeriesLinks(innerSeries, seriesNamePrefix, linkCache);
}
}
private void processSeriesLegs(SurveySeries series, SurveySeries masterSeries, String masterSeriesNamePrefix,
List> linkCache, boolean outputSplays ){
//Process legs from this series
logger.logMessage("Processing legs from series: " + series.getSeriesName() );
addLegsToMasterSeries( series, masterSeries, masterSeriesNamePrefix, linkCache, outputSplays );
//Process any inner series
String seriesNamePrefix = masterSeriesNamePrefix + "." + series.getSeriesName();
ListIterator iterator = series.getInnerSeriesList().listIterator();
while (iterator.hasNext() ) {
SurveySeries innerSeries = iterator.next();
//Process any inner series
processSeriesLegs(innerSeries, masterSeries, seriesNamePrefix, linkCache, outputSplays);
}
}
/**
* Creates one master survey series from a cave survey
* @param surveyData The cave survey model to convert to a single series
* @param outputSplays Indicates whether to include splay legs in the file or not
* @return The single survey series representing the cave survey
*/
public SurveySeries convertToSingleSeries( CaveSurvey surveyData, boolean outputSplays ) {
logger.logMessage("Flattening survey series heirarchy...");
SurveySeries rawData = new SurveySeries("root");
List> linkCache = new ArrayList>();
String seriesNamePrefix = rawData.getSeriesName();
//Create cache of links from all series
ListIterator iterator = surveyData.listIterator();
while (iterator.hasNext() ) {
SurveySeries series = iterator.next();
//Process links from this series
processSeriesLinks(series, seriesNamePrefix, linkCache);
}
//Process legs from all series once all series links have been handled
ListIterator iterator2 = surveyData.listIterator();
while (iterator2.hasNext() ) {
SurveySeries series = iterator2.next();
//Process legs from this series
processSeriesLegs(series, rawData, seriesNamePrefix, linkCache, outputSplays);
}
/*
* At this stage the legs are all in one master series, and the station names have been set so
* that any links are implicit due to having mapped all equivalent stations to the same station names.
*/
//Debug dump links cache
for ( int k = 0; k < linkCache.size(); k++ ) {
List rec = linkCache.get(k);
String msg = "Linked";
for ( int m = 0; m < rec.size(); m++ ) {
msg += ":" + rec.get(m);
}
logger.logMessage(msg);
}
return rawData;
}
}
CaveConverter_src/src/footleg/cavesurvey/data/writer/SurvexWriter.java 0000644 0000000 0000000 00000111773 13035166172 025412 0 ustar root root /**
* Copyright (C) 2009-2017 Paul Fretwell - aka 'Footleg' (drfootleg@gmail.com)
*
* This file is part of Cave Converter.
*
* Cave Converter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cave Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cave Converter. If not, see .
*/
package footleg.cavesurvey.data.writer;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import footleg.cavesurvey.converter.CaveConverter;
import footleg.cavesurvey.converter.Logger;
import footleg.cavesurvey.converter.CaveConverter.BearingUnit;
import footleg.cavesurvey.converter.CaveConverter.GradientUnit;
import footleg.cavesurvey.converter.CaveConverter.LengthUnit;
import footleg.cavesurvey.data.model.CaveSurvey;
import footleg.cavesurvey.data.model.SeriesLink;
import footleg.cavesurvey.data.model.SurveyLeg;
import footleg.cavesurvey.data.model.SurveySeries;
import footleg.cavesurvey.data.model.SurveySeries.ToStnLRUD;
import footleg.cavesurvey.data.model.SurveyStation;
import footleg.cavesurvey.tools.UtilityFunctions;
/**
* Writer for Survex file format text data.
*
* @author Footleg
* @version 2017.01.10 (ISO 8601 YYYY.MM.DD)
* @since 1.6 (The Java version used)
*
* @to.do
* TODO Handle nosurvey legs (currently they are converted to equates in error)
* TODO Output entrance flags
* TODO Output fix flags
* TODO Output calibration comments fields
* TODO Output team fields
* TODO Output instrument fields
* TODO Output units
* TODO Indent block contents including nested blocks
* TODO Support option to output multiple files using includes and equates in master file and one series per file in child files
*/
public class SurvexWriter {
private Logger logger;
public SurvexWriter( Logger logger ) {
super();
this.logger = logger;
}
/**
* Supported splay formats for output
*/
public static enum SplayFormats {
None,
Flagged,
Anonymous
}
/**
* Generates Survex format data from a cave survey
*
* @param surveyData The cave survey model to generate Survex data for
* @param splaysOutput Indicate format of splay legs included in the output.
* @return Text lines of Survex format data
*/
public List generateSurvexData( CaveSurvey surveyData, SplayFormats splaysOutput ) {
List outputData = new ArrayList();
logger.logMessage("Generating Survex format data...");
//Create dummy parent series to pass in neutral calibration settings for top level series
SurveySeries parentSeries = new SurveySeries("parent");
//Loop through all series
ListIterator seriesIterator = surveyData.listIterator();
while ( seriesIterator.hasNext() ) {
SurveySeries series = seriesIterator.next();
outputData.addAll( generateSurvexDataSeries( series, parentSeries, splaysOutput ) );
}
return outputData;
}
/**
* Generates Survex format data for a survey series
*
* @param series Survey series to generate Survex format data from
* @param parentSeries Survey series which is the parent of the series being passed in (used for calibrations)
* @return Text lines for a Survex format series data block
*/
private List generateSurvexDataSeries( SurveySeries series, SurveySeries parentSeries, SplayFormats splaysOutput ) {
List outputData = new ArrayList();
List legsData = new ArrayList();
List fixedStnsData = new ArrayList();
//List of passage data blocks. Each block is a child list of the station name and the LRUD data line.
List> passageData = new ArrayList>();
boolean duplicateFlagOn = false;
boolean surfaceFlagOn = false;
boolean splayFlagOn = false;
boolean nosurveyFlagOn = false;
int splayNameSequence = 0;
//Start series block
outputData.add( "*BEGIN " + substIllegalNameChars( series.getSeriesName() ) );
//Write comment if present
if ( series.getComment().length() > 0 ) {
String[] dataItems = UtilityFunctions.parseTripComment( series.getComment() );
for (String line : dataItems) {
outputData.add( ";" + line );
}
outputData.add( "" );
}
//Write date if present
if ( series.getSurveyDate() != null ) {
outputData.add( "*DATE " + UtilityFunctions.dateToString( series.getSurveyDate(), UtilityFunctions.SURVEXDATE_FORMAT) );
}
//Write calibration lines if different to parent series
//TODO Output Units for series and calibrations in these units (at same time as measurements in series units)
if ( series.getDeclination() != parentSeries.getDeclination() ) {
outputData.add( "*CALIBRATE declination " + series.getDeclination() );
}
if ( series.getTapeCalibration(LengthUnit.Metres) != parentSeries.getTapeCalibration(LengthUnit.Metres) ) {
outputData.add( "*CALIBRATE tape " + CaveConverter.padNumber( series.getTapeCalibration(LengthUnit.Metres), 2, 0 ) );
}
if ( series.getCompassCalibration(BearingUnit.Degrees) != parentSeries.getCompassCalibration(BearingUnit.Degrees) ) {
outputData.add( "*CALIBRATE compass " + series.getCompassCalibration(BearingUnit.Degrees) );
}
if ( series.getClinoCalibration(GradientUnit.Degrees) != parentSeries.getClinoCalibration(GradientUnit.Degrees) ) {
outputData.add( "*CALIBRATE clino " + series.getClinoCalibration(GradientUnit.Degrees) );
}
outputData.add( "" );
//Alias - for stations if splays output format is anonymous
if ( splaysOutput == SplayFormats.Anonymous ) {
outputData.add( "*alias station - .." );
outputData.add( "" );
}
//Add equates to output
ListIterator linksIterator = series.getLinks().listIterator();
while ( linksIterator.hasNext() ) {
SeriesLink link = linksIterator.next();
String equate = "*EQUATE ";
String series1Prefix = "";
String series2Prefix = "";
if ( link.getSeries1().length() > 0 ) {
series1Prefix = substIllegalNameChars( link.getSeries1() ) + ".";
}
if ( link.getSeries2().length() > 0 ) {
series2Prefix = substIllegalNameChars( link.getSeries2() ) + ".";
}
equate += series1Prefix + substIllegalNameChars( link.getStn1().getName() ) + " " +
series2Prefix + substIllegalNameChars( link.getStn2().getName() );
outputData.add( equate );
}
outputData.add( "");
//Write data format block if present and different to parent series
boolean skipDataOrderLine = false;
if ( series.hasDataOrder() ) {
if ( parentSeries.hasDataOrder() ) {
if ( UtilityFunctions.compareStringLists( series.getDataOrder(), parentSeries.getDataOrder() ) ) {
if ( series.getDataOrder2().size() == 0 && parentSeries.getDataOrder2().size() == 0 ) {
//Identical to parent and neither is a mix of data formats, so skip redeclaring data order in this series
skipDataOrderLine = true;
}
}
}
}
//Add data order header here if no legs in series, so it can apply to child series.
//(Series with legs will have data order headers written as needed in the leg processing loop later)
if ( ( skipDataOrderLine == false ) && ( series.legCount() == 0 ) && ( series.hasDataOrder() ) ) {
outputData.add( "*" + UtilityFunctions.dataFormatLineForSeries( series, false ) );
}
//Loop through the series legs and LRUD data writing details of each leg found
int psuedoLegCount = series.legCount() + series.getToStnLRUDs().size();
boolean lastLegWasDiving = false;
for ( int psuedoLegIdx = 0; psuedoLegIdx < psuedoLegCount; psuedoLegIdx++ ) {
//Determine if real leg of psuedo leg representing LRUD data on a toStn
SurveyLeg leg;
if ( psuedoLegIdx < series.legCount() ) {
//Real leg
leg = series.getLegRaw(psuedoLegIdx);
//If no data order in series, then skip data order headers unless diving data
if ( ( series.hasDataOrder() == false ) && ( leg.isDiving() == false ) ) {
//No data order in series, so skip it
skipDataOrderLine = true;
}
}
else {
//Need to generate a psudeo leg to hold the LRUD data for this toStn
leg = new SurveyLeg();
ToStnLRUD lrud = series.getToStnLRUDs().get( psuedoLegIdx - series.legCount() );
leg.setFromStn( lrud.getFromStn() );
leg.setLeft( lrud.getLeft(), LengthUnit.Metres );
leg.setRight( lrud.getRight(), LengthUnit.Metres );
leg.setUp( lrud.getUp(), LengthUnit.Metres );
leg.setDown( lrud.getDown(), LengthUnit.Metres );
}
String fromStn = leg.getFromStn().getName();
String fromStnVal = substIllegalNameChars( fromStn );
/* Check for valid leg. We do not want to write output for psuedo legs in a series which
* contain only a 'from station' with LRUD data on it,
* so only write leg data if a 'to station' exists or leg is a splay
*/
if ( ( leg.isSplay() ) || ( leg.getToStn() != null ) ) {
String toStn;
if ( leg.getToStn() == null ) {
//Splay leg without a 'to' station. Create a station name from the leg from station
toStn = leg.getFromStn().getName() + "-" + splayNameSequence++;
}
else {
toStn = leg.getToStn().getName();
}
String toStnVal = substIllegalNameChars( toStn );
if ( leg.isNosurvey() || leg.getLength( LengthUnit.Metres ) > 0 ) {
//Determine whether to write data type header
if ( ( leg.isNosurvey() ) && nosurveyFlagOn == false ) {
legsData.add( "*data nosurvey from to");
nosurveyFlagOn = true;
}
else if ( ( leg.isNosurvey() == false ) && nosurveyFlagOn ) {
legsData.add( "*" + UtilityFunctions.dataFormatLineForSeries( series, leg.isDiving() ) );
nosurveyFlagOn = false;
}
//Determine if any flags need setting
String flagsSetting = "";
//Determine whether to set or clear duplicate flag
if ( ( leg.isDuplicate() ) && duplicateFlagOn == false ) {
flagsSetting += " DUPLICATE";
duplicateFlagOn = true;
}
else if ( ( leg.isDuplicate() == false ) && duplicateFlagOn ) {
flagsSetting += " NOT DUPLICATE";
duplicateFlagOn = false;
}
//Determine whether to set or clear surface flag
if ( ( leg.isSurface() ) && surfaceFlagOn == false ) {
flagsSetting += " SURFACE";
surfaceFlagOn = true;
}
else if ( ( leg.isSurface() == false ) && surfaceFlagOn ) {
flagsSetting += " NOT SURFACE";
surfaceFlagOn = false;
}
if ( splaysOutput == SplayFormats.Flagged ) {
//Determine whether to set or clear splays flag
if ( ( leg.isSplay() ) && splayFlagOn == false ) {
flagsSetting += " SPLAY";
splayFlagOn = true;
}
else if ( ( leg.isSplay() == false ) && splayFlagOn ) {
flagsSetting += " NOT SPLAY";
splayFlagOn = false;
}
}
//Write flags line if any flags changed
if ( flagsSetting.length() > 0 ) {
legsData.add( "*FLAGS" + flagsSetting);
}
if ( ( ( splaysOutput == SplayFormats.None ) && leg.isSplay() ) == false ) {
//Leg or a splay to be written
//Write data order line if first leg, or different format to last leg when secondary data order
boolean needDataOrderHeader = false;
if ( psuedoLegIdx == 0 ) {
//First leg, so write data order line regardless, and set flag for type
if ( skipDataOrderLine == false ) {
needDataOrderHeader = true;
}
}
else {
//Only write data order line if leg type has changed since last leg
if ( series.getDataOrder2().size() > 0 ) {
if ( lastLegWasDiving != leg.isDiving() ) {
needDataOrderHeader = true;
}
}
}
//Set flag ready for next leg
lastLegWasDiving = leg.isDiving();
//Output data order header line if needed before this leg
if ( needDataOrderHeader ) {
legsData.add( "*" + UtilityFunctions.dataFormatLineForSeries( series, leg.isDiving() ) );
}
//Determine data order to write out
List dataOrder = series.getDataOrder();
if ( dataOrder.size() == 0 ) {
//No data order specified, so use Survex default order
dataOrder.add( CaveConverter.DATA_ORDER_CAT_FROMSTN );
dataOrder.add( CaveConverter.DATA_ORDER_CAT_TOSTN );
dataOrder.add( CaveConverter.DATA_ORDER_CAT_LENGTH );
dataOrder.add( CaveConverter.DATA_ORDER_CAT_BEARING );
dataOrder.add( CaveConverter.DATA_ORDER_CAT_CLINO );
}
else {
List dataOrder2 = series.getDataOrder2();
//Determine if correct data order item for normal or diving in case of mixed data series
if ( dataOrder2.size() > 0 ) {
if ( UtilityFunctions.dataOrderIsDiving( dataOrder ) != leg.isDiving() ) {
//Primary data order does not match required type, so use secondary order
dataOrder = dataOrder2;
}
}
}
String legLine = "";
//Write data in data order
for ( int i = 0; i < dataOrder.size(); i++ ) {
String item = dataOrder.get(i);
if ( item.compareTo( CaveConverter.DATA_ORDER_CAT_FROMSTN ) == 0 ) {
legLine += fromStnVal + "\t";
}
else if ( item.compareTo( CaveConverter.DATA_ORDER_CAT_TOSTN ) == 0 ) {
if ( ( splaysOutput == SplayFormats.Anonymous ) && leg.isSplay() ) {
//Anonymous splay, so no name for toStn
legLine += "-\t";
}
else {
legLine += toStnVal + "\t";
}
}
else if ( nosurveyFlagOn == false ) {
if ( item.compareTo( CaveConverter.DATA_ORDER_CAT_LENGTH ) == 0 ) {
legLine += CaveConverter.padNumber( leg.getLength(LengthUnit.Metres), 2, 5 ) + "\t";
}
else if ( item.compareTo( CaveConverter.DATA_ORDER_CAT_BEARING ) == 0 ) {
legLine += CaveConverter.padNumber( leg.getCompass(BearingUnit.Degrees), 2, 6 ) + "\t";
}
else if ( item.compareTo( CaveConverter.DATA_ORDER_CAT_CLINO ) == 0 ) {
legLine += CaveConverter.padNumber( leg.getClino(GradientUnit.Degrees), 2, 6 ) + "\t";
}
else if ( item.compareTo( CaveConverter.DATA_ORDER_CAT_FROMDEPTH ) == 0 ) {
legLine += CaveConverter.padNumber( leg.getFromDepth(LengthUnit.Metres), 2, 5 ) + "\t";
}
else if ( item.compareTo( CaveConverter.DATA_ORDER_CAT_TODEPTH ) == 0 ) {
legLine += CaveConverter.padNumber( leg.getToDepth(LengthUnit.Metres), 2, 5 ) + "\t";
}
else if ( item.compareTo( CaveConverter.DATA_ORDER_CAT_DEPTHCHANGE ) == 0 ) {
legLine += CaveConverter.padNumber( leg.getDepthChange(LengthUnit.Metres), 2, 5 ) + "\t";
}
else if ( item.compareTo( CaveConverter.DATA_ORDER_CAT_IGNOREALL ) == 0 ) {
}
}
}
//Add comment to end of line
if ( leg.getComment().length() > 0 ) {
if ( dataOrder.get( dataOrder.size() - 1 ).compareTo( CaveConverter.DATA_ORDER_CAT_IGNOREALL ) != 0 ) {
//Use comment separator
legLine += ";";
}
legLine += leg.getComment();
}
else {
//Trim whitespace off end of line
legLine = legLine.trim();
}
legsData.add( legLine );
}
}
else {
//Zero length leg
if ( leg.isSplay() == false ) {
//Valid leg, so write as equate
legsData.add( "*EQUATE " + fromStnVal + "\t" + toStnVal );
}
}
//Add FIXed points to fixed stns block for series
SurveyStation stn = leg.getFromStn();
for (int i=0; i < 2; i++) {
//Loop twice, to process from and to stations
if ( stn.isFixed() == true ) {
String fixedStnLine = "*FIX " + substIllegalNameChars( stn.getName() );
fixedStnLine += "\t" + stn.getEasting() + "\t" + stn.getNorthing() + "\t" +
stn.getAltitude();
fixedStnsData.add( fixedStnLine );
}
//After first pass using FromStn, get toStn if there is one for second pass
if ( leg.getToStn() == null ) {
break;
}
stn = leg.getToStn();
}
}
//Process LRUD data for non-splay, non-surface legs, including legs with no LRUD data.
if ( ( leg.isSplay() == false ) && ( leg.isSurface() == false ) ) {
//Start new passage data block if leg does not connect the fromStn containing the LRUD data
//to the start or end of another passage data block in the file
boolean newBlock = false;
int activeBlockIdx = passageData.size() - 1; //Initialise to last block in list
boolean addAtFront = false;
if ( passageData.size() == 0 ) {
//Create first block for first lrud station in series
newBlock = true;
}
else {
//Check if station linked to a station at either end of an existing block
// We are looking for the toStn at either end of an existing block
boolean foundExistingBlock = false;
for (int idx = 0; idx < passageData.size(); idx++ ) {
List block = passageData.get(idx);
if ( leg.getToStn() != null ) {
//Check if this block starts with the toStn for the leg
if ( ( block.size() == 1 ) && ( block.get(0)[0].equals( leg.getToStn().getName() ) ) ) {
//Leg toStn matches only station in block, so leg could go before or after this LRUD
//We need to know which direction the leg for the existing LRUD measurements was in
if ( block.get(0)[2].equals( leg.getFromStn().getName() ) ) {
//This leg follows the one previously added to this block, so add at end
activeBlockIdx = idx;
addAtFront = false;
foundExistingBlock = true;
}
else {
//This leg does NOT follow the one which started this block, so it must go before it
activeBlockIdx = idx;
addAtFront = true;
foundExistingBlock = true;
}
}
else if ( block.get(block.size() - 1)[0].equals( leg.getToStn().getName() ) ) {
//Matching toStn at end, so add LRUD to end of this block
activeBlockIdx = idx;
addAtFront = false;
foundExistingBlock = true;
}
else if ( block.get(0)[0].equals( leg.getToStn().getName() ) ) {
//Matching toStn at start, so add LRUD to start of this block
activeBlockIdx = idx;
addAtFront = true;
foundExistingBlock = true;
}
}
if ( foundExistingBlock == false ) {
//Need to find another leg which has a toStn matching the fromStn of this leg
for (int idx2 = 0; idx2 < series.legCount(); idx2++ ) {
SurveyLeg chkLeg = series.getLegRaw(idx2);
if ( ( chkLeg.getToStn() != null )
&& ( chkLeg.getToStn().getName().equals(leg.getFromStn().getName() ) ) ) {
//Found a leg which precedes the leg we are adding LRUD data from.
//Check if the fromStn of this preceding leg is the last stn in the block
if ( block.get(block.size() - 1)[0].equals( chkLeg.getFromStn().getName() ) ) {
//Matching fromStn for previous leg at end of block, so add LRUD to end of this block
activeBlockIdx = idx;
addAtFront = false;
foundExistingBlock = true;
}
// This last case turns out not to be needed as it is generally better to start a new passage data block in this case.
// else {
// //Check if the preceding leg starts this block
// //(but is not already covered going down this block)
// int blockIdx2 = 1;
// if ( block.size() == 1 ) {
// blockIdx2 = 0;
// }
// if ( ( block.get(0)[0].equals( chkLeg.getFromStn().getName() ) )
// && ( block.get(blockIdx2)[0].equals( chkLeg.getToStn().getName() ) == false ) ) {
// //Matching toStn at start, so add LRUD to start of this block
// activeBlockIdx = idx;
// addAtFront = true;
// foundExistingBlock = true;
// logger.logMessage("Stn " + leg.getFromStn().getName() + " fromStn for leg matched toStn of leg " +
// chkLeg.getFromStn().getName() + "-" + chkLeg.getToStn().getName() +
// " where fromStn was at start of PD block but leg is not covered in this block");
// }
// }
if ( foundExistingBlock ) {
break;
}
}
}
}
if ( foundExistingBlock ) {
break;
}
}
if ( foundExistingBlock == false ) {
//Create new block for station as it is not connected to any other existing block
newBlock = true;
}
}
if (newBlock) {
passageData.add( new ArrayList() );
activeBlockIdx = passageData.size() - 1;
}
String[] lrudLine = createLrudLine( leg );
if ( addAtFront ) {
passageData.get(activeBlockIdx).add(0, lrudLine);
}
else {
passageData.get(activeBlockIdx).add( lrudLine );
}
}
}
//Add missing toStns from legs which close loops
addMissingPassageDataAtLoopClosureStations( series, passageData );
//Replace LRUD values with those from the actual legs matching the pairs of stations
//for the current and next line in each block and remove single station blocks
reprocessPassageDataBlocks( series, passageData );
//Merge passage data blocks where one block ends with the same station as another starts
combinePassageDataBlocks( passageData );
//Write fixes data block
if ( fixedStnsData.size() > 0 ) {
outputData.addAll( fixedStnsData );
}
//Write legs data block
outputData.addAll( legsData );
//Turn off splays flag if on
if ( splayFlagOn ) {
outputData.add( "*FLAGS NOT SPLAY");
splayFlagOn = false;
}
//Write passage data block
if ( passageData.size() > 0 ) {
outputData.add( "");
//outputData.addAll( passageData );
ListIterator> blocksIter = passageData.listIterator();
while ( blocksIter.hasNext() ) {
List block = blocksIter.next();
outputData.add("*data passage station left right up down");
ListIterator blockIter = block.listIterator();
while ( blockIter.hasNext() ) {
String[] line = blockIter.next();
//line[0] is fromStn name, and needs illegal characters substituting
//line[1] is the LRUD numbers, line[2] is the toStn name and is not output
outputData.add( substIllegalNameChars( line[0] ) + "\t" + line[1] );
}
}
}
//Loop through inner series
ListIterator seriesIterator = series.getInnerSeriesList().listIterator();
while ( seriesIterator.hasNext() ) {
SurveySeries innerSeries = seriesIterator.next();
outputData.addAll( generateSurvexDataSeries( innerSeries, series, splaysOutput ) );
}
//Close the series
outputData.add( "*END " + substIllegalNameChars( series.getSeriesName() ) );
outputData.add( "" );
return outputData;
}
/* Replace illegal characters in station or series names with Survex allowed name characters.
* This method should store previous conversions in case ambiguous cases might arise, and
* use a different character to prevent converted stations becoming ambiguous.
* e.g. CF+ and CF' do not want to both be changed to CF_ or CF-, but all occurrences of
* CF+ would want to be converted to the same name. We could also check the series for
* occurrences of the same name as an existing legal station (so if CF_1 already existed,
* we would not want to convert CF+ to CF_1). *
*/
private String substIllegalNameChars( String stnName ) {
String result = "";
for (int i=0; i < stnName.length(); i++){
char c = stnName.charAt(i);
String sub = String.valueOf(c);
if ( c < 48 ) {
//Check for characters with ascii values below '0'
switch (c) {
case 33:
// 33 !
sub = "_ex";
break;
case 34:
// 34 "
sub = "_dq";
break;
case 35:
// 35 #
sub = "_hs";
break;
case 36:
// 36 $
sub = "_dl";
break;
case 37:
// 37 %
sub = "_pc";
break;
case 38:
// 38 &
sub = "_am";
break;
case 39:
// 39 '
sub = "_sq";
break;
case 40:
// 40 (
sub = "_ob";
break;
case 41:
// 41 )
sub = "_cb";
break;
case 42:
// 42 *
sub = "_as";
break;
case 43:
// 43 +
sub = "_pl";
break;
case 44:
// 44 ,
sub = "_cm";
break;
case 47:
// 47 /
sub = "_fs";
break;
}
}
else if ( c > 57 ) {
if ( c < 65 ) {
//Check for characters with ascii values between '9' and 'A'
switch (c) {
case 58:
// 58 :
sub = "_co";
break;
case 59:
// 59 ;
sub = "_sc";
break;
case 60:
// 60 <
sub = "_lt";
break;
case 61:
// 61 =
sub = "_eq";
break;
case 62:
// 62 >
sub = "_gt";
break;
case 63:
// 63 ?
sub = "_qm";
break;
case 64:
// 64 @
sub = "_at";
break;
}
}
else if ( c > 90 ) {
if ( c < 97 ) {
//Check for characters with ascii values between 'Z' and 'a'
switch (c) {
case 91:
// 91 [
sub = "_os";
break;
case 92:
// 92 \
sub = "_bs";
break;
case 93:
// 93 ]
sub = "_cs";
break;
case 94:
// 94 ^
sub = "_ht";
break;
case 95:
// 95 _
//Allowed character
//sub = "_";
break;
case 96:
// 96 `
sub = "_gr";
break;
}
}
else if ( c > 122 ) {
//Check for characters with ascii values between 'z' and '....'
switch (c) {
case 123:
// 123 {
sub = "_oc";
break;
case 124:
// 124 |
sub = "_pi";
break;
case 125:
// 125 }
sub = "_cc";
break;
case 126:
// 126 ~
sub = "_ti";
break;
default:
//All extended ascii characters, replace with ascii code
int code = 0 + c;
sub = "_asc" + code;
}
}
}
}
//TODO More illegal character mapping required
result += sub;
}
return result;
}
/* Replace LRUD values with those from actual leg represented by current and next station in block
* and remove single station blocks. Sometimes at junctions the initial LRUD data put into the block
* is from a leg which is not the correct leg for the next station added to the block. So where a
* leg can be matched to both the station on the data line, and the next station in the block then
* we can update the LRUD data to be from that leg.
*/
private void reprocessPassageDataBlocks( SurveySeries series, List> passageData ) {
//Process all blocks
ListIterator> blocksIter = passageData.listIterator();
while ( blocksIter.hasNext() ) {
List block = blocksIter.next();
//Remove block if only contains a single station as these are obsolete
if ( block.size() < 2 ) {
blocksIter.remove();
}
else {
//Update LRUD data for lines in block
boolean noLrudDataInBlock = true;
//Loop through each pair of stations in the block
for ( int blockIdx = 0; blockIdx < ( block.size() - 1 ); blockIdx++ ) {
String[] line1 = block.get(blockIdx);
String[] line2 = block.get(blockIdx + 1);
//Look for a leg representing this line1
boolean legMatchesPassageData = false;
for ( int legIdx = 0; legIdx < series.legCount(); legIdx++ ) {
SurveyLeg leg = series.getLegRaw(legIdx);
if ( ( leg.isSplay() == false )
&& ( leg.getToStn() != null )
&& ( leg.getLeft(LengthUnit.Metres) + leg.getRight(LengthUnit.Metres) +
leg.getUp(LengthUnit.Metres) + leg.getDown(LengthUnit.Metres) > 0.0 ) ) {
//Leg is a real leg with LRUD data, check for match both ways
if ( ( line1[0].equals( leg.getFromStn().getName() ) )
&& ( line2[0].equals( leg.getToStn().getName() ) ) ) {
legMatchesPassageData = true;
}
//Do not look for reverse leg match because LRUD will be on opposite end of leg to the passage data line we are updating
// else if ( ( line2[0].equals( leg.getFromStn().getName() ) )
// && ( line1[0].equals( leg.getToStn().getName() ) ) ) {
// legMatchesPassageData = true;
// }
}
//If a matching leg was found then regenerate LRUD data for this line using the leg
if ( legMatchesPassageData ) {
//Replace LRUD data for this line with data from this leg
String[] newLine = createLrudLine(leg);
if ( line1[1].equals( newLine[1] ) == false ) {
block.remove(blockIdx);
block.add(blockIdx, newLine);
}
break; //Exit legs for loop
}
}
//Check if the line has any LRUD data if this block has not had any so far
if ( ( noLrudDataInBlock )
&& ( ( line1[1].equals( " 0.00\t 0.00\t 0.00\t 0.00" ) == false )
|| ( line2[1].equals( " 0.00\t 0.00\t 0.00\t 0.00" ) == false ) )
) {
noLrudDataInBlock = false;
}
}
//Remove block if no LRUD data on any station
if ( noLrudDataInBlock ) {
blocksIter.remove();
}
}
}
}
/* Combine any passage data blocks which start with the same station as another block ends with.
*/
private void combinePassageDataBlocks( List> passageData ) {
//Process all blocks
int outerIdx = 0;
while ( outerIdx < passageData.size() ) {
List block = passageData.get( outerIdx );
String lastStn = block.get( block.size() - 1 )[0];
//Iterate through all blocks to see if any start with the last station in this block
for ( int innerIdx1 = 0; innerIdx1 < passageData.size(); innerIdx1++ ) {
List innerBlock = passageData.get( innerIdx1 );
if ( innerBlock.equals( block ) == false ) {
String innerStartStn = innerBlock.get( 0 )[0];
if ( innerStartStn.equals( lastStn ) ) {
boolean removeInner = true;
//Append this inner block to the end of the main iterator block
for ( int innerIdx2 = 0; innerIdx2 < innerBlock.size(); innerIdx2++ ) {
if ( innerIdx2 == 0 ) {
//Don't copy first station, but check it matches LRUD of last item in block we are appending to
if ( block.get( block.size() - 1 )[1].equals( innerBlock.get( 0 )[1] ) == false ) {
logger.logMessage("Unable to merge passage data blocks due to LRUD data mismatch: " +
"Block ending " + block.get( block.size() - 1 )[0] + "=" + block.get( block.size() - 1 )[1] + ", " +
"Block starting " + innerBlock.get( 0 )[0] + "=" + innerBlock.get( 0 )[1]);
removeInner = false;
break;
}
}
else {
block.add( innerBlock.get( innerIdx2 ) );
}
}
if ( removeInner ) {
//Remove the inner block now it has been copied
passageData.remove( innerIdx1 );
outerIdx--;
//Update last station to new end station
lastStn = block.get( block.size() - 1 )[0];
}
}
}
}
outerIdx++;
}
}
/* When a leg closes a loop, we need to add the LRUD for the toStn of the loop closure by finding that station
* with the LRUD data in another leg. In Stomps, the leg 31-25 has LRUD on 31. But there is no terminal stn25.
* Perhaps there should be? Or easier to analyse all passage data blocks here, consolidate blocks which are adjoined
* and then look for any legs where the toStn is not represented and add them by copying data from another leg.
*/
private void addMissingPassageDataAtLoopClosureStations( SurveySeries series, List> passageData ) {
//Check for legs with LRUD data on the toStn which are not represented in blocks
for ( int legIdx = 0; legIdx < series.legCount(); legIdx++ ) {
SurveyLeg leg = series.getLegRaw(legIdx);
if ( ( leg.isSplay() == false ) && ( leg.getLeft(LengthUnit.Metres) + leg.getRight(LengthUnit.Metres) +
leg.getUp(LengthUnit.Metres) + leg.getDown(LengthUnit.Metres) > 0.0 ) ) {
//Check for a leg which has LRUD data on the toStn of this leg
for ( int legIdx2 = 0; legIdx2 < series.legCount(); legIdx2++ ) {
SurveyLeg leg2 = series.getLegRaw(legIdx2);
if ( ( leg.isSplay() == false )
&& ( leg.getToStn() != null )
&& ( leg.getLeft(LengthUnit.Metres) + leg.getRight(LengthUnit.Metres) +
leg.getUp(LengthUnit.Metres) + leg.getDown(LengthUnit.Metres) > 0.0 )
&& ( leg.getToStn().getName().equals( leg2.getFromStn().getName() ) ) ) {
//Check if the leg is represented in a passage data block
boolean foundLegInPassData = false;
ListIterator> blocksIter = passageData.listIterator();
while ( blocksIter.hasNext() ) {
List block = blocksIter.next();
//Look for pair of lines in this block which represent this leg
for ( int blockIdx = 0; blockIdx < ( block.size() - 1 ); blockIdx++ ) {
String[] line1 = block.get(blockIdx);
String[] line2 = block.get(blockIdx + 1);
if ( ( line1[0].equals( leg.getFromStn().getName() ) )
&& ( line2[0].equals( leg.getToStn().getName() ) ) ) {
foundLegInPassData = true;
break; //Exit block for loop
}
else if ( ( line2[0].equals( leg.getFromStn().getName() ) )
&& ( line1[0].equals( leg.getToStn().getName() ) ) ) {
foundLegInPassData = true;
break; //Exit block for loop
}
}
if ( foundLegInPassData ) {
break; //Exit passage data blocks loop
}
}
if ( foundLegInPassData == false ) {
//Leg with toStn LRUD is missing from passage data.
//Check if any passage data block starts or ends with one of the stns from the leg
//so we can add it
ListIterator> blocksIter2 = passageData.listIterator();
while ( blocksIter2.hasNext() ) {
List blockForAdd = blocksIter2.next();
int insertPoint = 0; //0=not found, 1=start, 2=end
if ( blockForAdd.get(blockForAdd.size() - 1)[0].equals( leg.getFromStn().getName() ) ) {
//Leg from station found at end of block
insertPoint = 2;
}
else if ( blockForAdd.get(0)[0].equals( leg.getFromStn().getName() ) ) {
//Leg from station found at start of block
insertPoint = 1;
}
if ( insertPoint > 0 ) {
//Loop through all passage data block lines looking for one which matches the toStn
ListIterator> blocksIter3 = passageData.listIterator();
while ( blocksIter3.hasNext() ) {
List blockToCopyFrom = blocksIter3.next();
boolean lineAdded = false;
ListIterator blockIter = blockToCopyFrom.listIterator();
while ( blockIter.hasNext() ) {
String[] line = blockIter.next();
if ( line[0].equals( leg.getToStn().getName() ) ) {
//Insert suitable LRUD line here
if ( insertPoint == 1) {
//Insert at start
blockForAdd.add(0, line);
}
else {
//Insert at end
blockForAdd.add(line);
}
lineAdded = true;
break; //Exit loop through block looking for line to add
}
}
if ( lineAdded ) {
break; //Exit loop looking for passage data block to add line from
}
}
break; //Exit passage data blocks2 loop
}
}
}
break; //Exit loop leg2 as leg has been processed
}
}
}
}
}
private String[] createLrudLine( SurveyLeg bestLeg ) {
String[] lrudLine = new String[3];
lrudLine[0] = bestLeg.getFromStn().getName();
lrudLine[1] =
CaveConverter.padNumber(bestLeg.getLeft(LengthUnit.Metres),2,5) + "\t" +
CaveConverter.padNumber(bestLeg.getRight(LengthUnit.Metres),2,5) + "\t" +
CaveConverter.padNumber(bestLeg.getUp(LengthUnit.Metres),2,5) + "\t" +
CaveConverter.padNumber(bestLeg.getDown(LengthUnit.Metres),2,5);
if ( bestLeg.getToStn() != null ) {
lrudLine[2] = bestLeg.getToStn().getName();
}
return lrudLine;
}
}
CaveConverter_src/src/footleg/cavesurvey/data/reader/ 0000755 0000000 0000000 00000000000 13036467352 021777 5 ustar root root CaveConverter_src/src/footleg/cavesurvey/data/reader/DxfParser.java 0000644 0000000 0000000 00000126010 13036417264 024535 0 ustar root root /**
* Copyright (C) 2009-2017 Paul Fretwell - aka 'Footleg' (drfootleg@gmail.com)
*
* This file is part of Cave Converter.
*
* Cave Converter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cave Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cave Converter. If not, see .
*/
package footleg.cavesurvey.data.reader;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import footleg.cavesurvey.converter.Logger;
import footleg.cavesurvey.converter.CaveConverter.BearingUnit;
import footleg.cavesurvey.converter.CaveConverter.GradientUnit;
import footleg.cavesurvey.converter.CaveConverter.LengthUnit;
import footleg.cavesurvey.data.model.CaveSurvey;
import footleg.cavesurvey.data.model.SurveyLeg;
import footleg.cavesurvey.data.model.SurveySeries;
import footleg.cavesurvey.data.model.SurveyStation;
import footleg.cavesurvey.data.model.SurveyStation.FixType;
import footleg.cavesurvey.tools.UtilityFunctions;
/**
* Parser for DXF format text data files.
*
* @author Footleg
* @version 2017.01.09 (ISO 8601 YYYY.MM.DD)
* @since 1.6 (The Java version used)
*/
public class DxfParser {
private Date seriesDate;
private Logger logger;
/**
* Class constructor
* @param logger Logging class to output information, warning and error messages to
*/
public DxfParser( Logger logger ) {
super();
this.logger = logger;
}
/**
* Parse Autocad DXF file into the cave data model.
*
* Converts polylines into series with fixed point at start.
*
* @param surveyFileData ListArray of data lines from a DXF file
* @param parseMode An enumeration to indicate whether to parse file for contour polylines, spot heights or survey legs
* @return Cave Survey object
*
* @to.do
* TODO Add option to round to nearest 10cm or 0.5 deg.
*/
public CaveSurvey parseFile( List surveyFileData, int parseMode ) {
int legCount = 0;
int iPolylineCount = 0;
int iVertexCount = 0;
int iPlainlineCount = 0;
//Create new list of survey series to hold data
CaveSurvey surveyData = new CaveSurvey(logger);
SurveySeries outerSeries = null;
//Create arrays to data while processing lines
List> allChains = new ArrayList>();
List allChainLabels = new ArrayList();
List arSurveyChain = new ArrayList();
List arLines = new ArrayList();
List arLabelPoints = new ArrayList();
List arLabels = new ArrayList();
boolean mappedLabelsFound = false;
//Define read state values
final int stateParsingHeader = 0;
final int stateFindPolyline = 1;
final int stateFindVertex = 2;
final int parseModeSurveyLegs = 0;
final int parseModeSurfaceContours = 1;
final int parseModeSpotHeights = 2;
//Initialise read state
int state = stateParsingHeader;
//Local vars
int iPointCount = 0;
String sLineName = "";
double dEastValue = 0;
double dNorthValue = 0;
double dElevValue = 0;
double dLastEastValue = 0;
double dLastNorthValue = 0;
double dLastElevValue = 0;
//Range bounds
//TODO Promote these to parameters which can be specified as options
int iNorthEdge = 999999999;
int iSouthEdge = -999999999;
int iEastEdge = 999999999;
int iWestEdge = -999999999;
int iMinElev = -999999999;
//Write output file header
logger.logMessage("Generating survey data from lines and polylines in DXF data file");
outerSeries = new SurveySeries( "SurveyFromDXF" );
//Loop through all data lines
for ( int i=0; i < surveyFileData.size(); i++ ) {
String dataLine = surveyFileData.get(i);
//Proceed based on state
switch( state ) {
case stateParsingHeader :
//First look for line:
// ENTITIES
//This signifies start of data after header stuff which we ignore.
if ( dataLine.trim().compareToIgnoreCase("ENTITIES") == 0 ) {
//Found end of header section, so change state to searching for new polyline
state = stateFindPolyline;
}
break;
case stateFindPolyline:
if ( dataLine.trim().compareToIgnoreCase("POLYLINE") == 0 ) {
//Found polyline, indicated with header line:
// POLYLINE
//and ending with:
// SEQEND
//Check next 15 lines looking for AcDbEntity start
int iSkipLoop = 1;
do {
dataLine = surveyFileData.get(++i);
}
while ( ( dataLine.trim().compareToIgnoreCase("AcDbEntity") != 0 )
|| ( iSkipLoop > 15 ) );
//If found it then process
if ( dataLine.trim().compareToIgnoreCase("AcDbEntity") == 0 ) {
//(skip 1 line)
dataLine = surveyFileData.get(++i);
//Read entity type from next line:
dataLine = surveyFileData.get(++i);
//if Strings.Left(Trim(dataLine), 4) = "1100" {
//Found a contour line entity (they are all 1100x)
//Increment line counter
iPolylineCount += 1;
//Start new section heading, removing spaces from station names
sLineName = dataLine.replace(" ", "_");
if ( parseMode == parseModeSurfaceContours ) {
sLineName = sLineName + iPolylineCount;
}
//Set state to finding vertices
state = stateFindVertex;
//Reset point counter
iPointCount = 0;
//}
}
}
//loop through all lines, each indicated with header line:
// LINE
//and ending after a fixed number of lines
else if ( dataLine.trim().compareToIgnoreCase("LINE") == 0 ) {
//Found line
iPlainlineCount++;
//Check next 4 lines looking for AcDbEntity start
int iSkipLoop = 0;
do {
dataLine = surveyFileData.get(++i);
iSkipLoop++;
}
while ( (dataLine.trim().compareToIgnoreCase("AcDbEntity") != 0 && dataLine.trim().compareToIgnoreCase("CentreLine") != 0 )
&& ( iSkipLoop < 4 ) );
//Allow anything if 4 lines read
boolean allowAnyLineName = false;
if ( iSkipLoop == 4 ) {
allowAnyLineName = true;
}
//If found it then process
if ( dataLine.trim().compareToIgnoreCase("AcDbEntity") == 0
|| dataLine.trim().compareToIgnoreCase("CentreLine") == 0
|| allowAnyLineName == true ) {
if ( dataLine.trim().compareToIgnoreCase("CentreLine") != 0 && allowAnyLineName == false ) {
//Check next 15 lines looking for AcDbLine start
iSkipLoop = 1;
do {
dataLine = surveyFileData.get(++i);
}
while ( ( dataLine.trim().compareToIgnoreCase("AcDbLine") != 0 )
|| ( iSkipLoop > 15 ) );
}
//If found it then process
if ( dataLine.trim().compareToIgnoreCase("AcDbLine") == 0
|| ( dataLine.trim().compareToIgnoreCase("CentreLine") == 0 )
|| allowAnyLineName ) {
//Check for next line:
//10:
dataLine = surveyFileData.get(++i);
if ( dataLine.trim().compareToIgnoreCase("10") == 0 ) {
//Read Easting
dataLine = surveyFileData.get(++i);
dEastValue = roundedDataValue(dataLine);
//Check for next line:
//20:
dataLine = surveyFileData.get(++i);
if ( dataLine.trim().compareToIgnoreCase("20") == 0 ) {
//Read Northing
dataLine = surveyFileData.get(++i);
dNorthValue = roundedDataValue(dataLine);
//Check for next line:
//30:
dataLine = surveyFileData.get(++i);
if ( dataLine.trim().compareToIgnoreCase("30") == 0 ) {
//Read Elevation
dataLine = surveyFileData.get(++i);
dElevValue = roundedDataValue(dataLine);
//Check for next line:
//11:
dataLine = surveyFileData.get(++i);
if ( dataLine.trim().compareToIgnoreCase("11") == 0 ) {
//Read Easting
dataLine = surveyFileData.get(++i);
dLastEastValue = roundedDataValue(dataLine);
//Check for next line:
//21:
dataLine = surveyFileData.get(++i);
if ( dataLine.trim().compareToIgnoreCase("21") == 0 ) {
//Read Northing
dataLine = surveyFileData.get(++i);
dLastNorthValue = roundedDataValue(dataLine);
//Check for next line:
//31:
dataLine = surveyFileData.get(++i);
if ( dataLine.trim().compareToIgnoreCase("31") == 0 ) {
//Read Elevation
dataLine = surveyFileData.get(++i);
dLastElevValue = roundedDataValue(dataLine);
//Now generate data to output
if ( ( dEastValue >= iWestEdge )
&& ( dEastValue <= iEastEdge )
&& ( dNorthValue <= iNorthEdge )
&& ( dNorthValue >= iSouthEdge )
&& ( dElevValue >= iMinElev )
&& ( dLastEastValue >= iWestEdge )
&& ( dLastEastValue <= iEastEdge )
&& ( dLastNorthValue <= iNorthEdge )
&& ( dLastNorthValue >= iSouthEdge )
&& ( dLastElevValue >= iMinElev )) {
//Increment point counter
iPointCount += 1;
if ( parseMode == parseModeSurveyLegs ) {
//Add points to line array
double[] point = new double[6];
point[0] = dEastValue;
point[1] = dNorthValue;
point[2] = dElevValue;
point[3] = dLastEastValue;
point[4] = dLastNorthValue;
point[5] = dLastElevValue;
arLines.add(point);
}
}
else {
//Record outside specified area
dEastValue = dNorthValue;
}
}
}
}
}
}
}
}
}
}
//loop through all text items, each indicated with header line:
// TEXT
//and ending after a fixed number of lines
else if ( dataLine.trim().compareToIgnoreCase("TEXT") == 0 ) {
//Found TEXT item
iPlainlineCount++;
//Skip next line then check for Label start
dataLine = surveyFileData.get(++i);
dataLine = surveyFileData.get(++i);
if (dataLine.trim().compareToIgnoreCase("Labels") == 0 ) {
//Found label, so read position and name from following lines
//Check for next line:
//10:
dataLine = surveyFileData.get(++i);
if ( dataLine.trim().compareToIgnoreCase("10") == 0 ) {
//Read Easting
dataLine = surveyFileData.get(++i);
dEastValue = roundedDataValue(dataLine);
//Check for next line:
//20:
dataLine = surveyFileData.get(++i);
if ( dataLine.trim().compareToIgnoreCase("20") == 0 ) {
//Read Northing
dataLine = surveyFileData.get(++i);
dNorthValue = roundedDataValue(dataLine);
//Check for next line:
//30:
dataLine = surveyFileData.get(++i);
if ( dataLine.trim().compareToIgnoreCase("30") == 0 ) {
//Read Elevation
dataLine = surveyFileData.get(++i);
dElevValue = roundedDataValue(dataLine);
//Skip 3 lines
for (int iskip = 0; iskip < 3; iskip++) {
dataLine = surveyFileData.get(++i);
}
//Next line should be station name
String possibleStnName = surveyFileData.get(++i);
//skip a line
dataLine = surveyFileData.get(++i);
//Check for next line:
//POINT:
dataLine = surveyFileData.get(++i);
if ( dataLine.trim().compareToIgnoreCase("POINT") == 0 ) {
//skip a line
dataLine = surveyFileData.get(++i);
//Check for next line:
//Stations:
dataLine = surveyFileData.get(++i);
if ( dataLine.trim().compareToIgnoreCase("Stations") == 0 ) {
//Check for next line:
//10:
dataLine = surveyFileData.get(++i);
if ( dataLine.trim().compareToIgnoreCase("10") == 0 ) {
//Read Easting
dataLine = surveyFileData.get(++i);
dLastEastValue = roundedDataValue(dataLine);
//Check for next line:
//20:
dataLine = surveyFileData.get(++i);
if ( dataLine.trim().compareToIgnoreCase("20") == 0 ) {
//Read Northing
dataLine = surveyFileData.get(++i);
dLastNorthValue = roundedDataValue(dataLine);
//Check for next line:
//30:
dataLine = surveyFileData.get(++i);
if ( dataLine.trim().compareToIgnoreCase("30") == 0 ) {
//Read Elevation
dataLine = surveyFileData.get(++i);
dLastElevValue = roundedDataValue(dataLine);
//Now generate data to output
if ( ( dEastValue >= iWestEdge )
&& ( dEastValue <= iEastEdge )
&& ( dNorthValue <= iNorthEdge )
&& ( dNorthValue >= iSouthEdge )
&& ( dElevValue >= iMinElev )
&& ( dLastEastValue >= iWestEdge )
&& ( dLastEastValue <= iEastEdge )
&& ( dLastNorthValue <= iNorthEdge )
&& ( dLastNorthValue >= iSouthEdge )
&& ( dLastElevValue >= iMinElev )) {
//Check station point and label point matched
if ( ( dEastValue == dLastEastValue )
&& ( dNorthValue == dLastNorthValue )
&& ( dElevValue == dLastElevValue )) {
//Station label identified
if ( parseMode == parseModeSurveyLegs ) {
//Add label and point to lists
double[] point = new double[3];
point[0] = dEastValue;
point[1] = dNorthValue;
point[2] = dElevValue;
arLabels.add( possibleStnName );
arLabelPoints.add( point );
}
}
}
else {
//Record outside specified area
dEastValue = dNorthValue;
}
}
}
}
}
}
}
}
}
}
}
break;
case stateFindVertex:
//Within each polyline, look for vertices indicated by lines:
// VERTEX
if ( dataLine.trim().compareToIgnoreCase("VERTEX") == 0 ) {
//Reset flag
boolean bValidRecord = false;
//Increment count
iVertexCount += 1;
//Check next 15 lines looking for AcDbEntity start
int iSkipLoop = 1;
do {
dataLine = surveyFileData.get(++i);
}
while ( ( dataLine.trim().compareToIgnoreCase("AcDbEntity") != 0 )
|| ( iSkipLoop > 15 ) );
if ( dataLine.trim().compareToIgnoreCase("AcDbEntity") == 0 ) {
//Check next 15 lines looking for AcDbVertex start
iSkipLoop = 1;
do {
dataLine = surveyFileData.get(++i);
}
while ( ( dataLine.trim().compareToIgnoreCase("AcDbVertex") != 0 )
|| ( iSkipLoop > 15 ) );
if ( dataLine.trim().compareToIgnoreCase("AcDbVertex") == 0 ) {
//Check next 15 lines looking for AcDb3dPolylineVertex start
iSkipLoop = 1;
do {
dataLine = surveyFileData.get(++i);
}
while ( ( dataLine.trim().compareToIgnoreCase("AcDb3dPolylineVertex") != 0 )
|| ( iSkipLoop > 15 ) );
if ( dataLine.trim().compareToIgnoreCase("AcDb3dPolylineVertex") == 0 ) {
//Check for next line:
//10:
dataLine = surveyFileData.get(++i);
if ( dataLine.trim().compareToIgnoreCase("10") == 0 ) {
//Read Easting
dataLine = surveyFileData.get(++i);
dEastValue = roundedDataValue(dataLine);
//Check for next line:
//20:
dataLine = surveyFileData.get(++i);
if ( dataLine.trim().compareToIgnoreCase("20") == 0 ) {
//Read Northing
dataLine = surveyFileData.get(++i);
dNorthValue = roundedDataValue(dataLine);
//Check for next line:
//30:
dataLine = surveyFileData.get(++i);
if ( dataLine.trim().compareToIgnoreCase("30") == 0 ) {
//Read Elevation
dataLine = surveyFileData.get(++i);
dElevValue = roundedDataValue(dataLine);
//Now generate data to output
if ( ( dEastValue >= iWestEdge )
&& ( dEastValue <= iEastEdge )
&& ( dNorthValue <= iNorthEdge )
&& ( dNorthValue >= iSouthEdge )
&& ( dElevValue >= iMinElev ) ) {
//Increment point counter
iPointCount += 1;
if ( parseMode == parseModeSurveyLegs ) {
//Add point to chain
double[] point = new double[3];
point[0] = dEastValue;
point[1] = dNorthValue;
point[2] = dElevValue;
arSurveyChain.add(point);
}
else {
//Generate station name
String sStnName = "S" + iPolylineCount + "P" + iPointCount;
//Reset station position string
String sPointPosition = "";
if ( iPointCount == 1 ) {
//First point, so fix it
sPointPosition = "*fix " + sStnName + " " +
dEastValue + " " +
dNorthValue + " " +
dElevValue;
if ( parseMode == parseModeSurfaceContours ) {
//Write line header
logger.logMessage("*begin " + sLineName);
}
else {
//} point, so write differences for leg
sPointPosition = " " + (dEastValue - dLastEastValue) + " " +
(dNorthValue - dLastNorthValue) + " " +
(dElevValue - dLastElevValue);
}
}
if ( parseMode == parseModeSpotHeights ) {
logger.logMessage( String.valueOf(Math.round(dEastValue) - iEastEdge) );
logger.logMessage( String.valueOf( (iNorthEdge - iSouthEdge) - ( Math.round(dNorthValue) - iSouthEdge) ) );
logger.logMessage( String.valueOf(dElevValue) );
}
else {
//Write position and station number
logger.logMessage(sPointPosition);
logger.logMessage(sStnName);
}
}
//Update last positions
dLastEastValue = dEastValue;
dLastNorthValue = dNorthValue;
dLastElevValue = dElevValue;
}
else {
//Record outside specified area
dEastValue = dNorthValue;
}
bValidRecord = true;
}
}
}
}
}
}
//Check for invalid records
if ( bValidRecord == false ) {
//Invalid record
logger.logMessage("Bad Line");
//Set state back to searching for next vertex
state = stateFindPolyline;
}
}
//Find either next VERTEX or SEQEND
else if ( dataLine.trim().compareToIgnoreCase("SEQEND") == 0 ) {
//End of polyline
//Write final station name if any points were written
if ( parseMode == parseModeSurveyLegs ) {
//Check that line name is unique
String sUniqueName = sLineName;
int k = 1;
boolean bUnique = false;
while ( bUnique == false ) {
//Search existing chains for matching name
bUnique = true;
for ( int j = 0; j < outerSeries.innerSeriesCount(); j++ ) {
//Check for matching series
if ( outerSeries.getInnerSeries(j).getSeriesName().compareTo( sUniqueName ) == 0 ) {
//Not unique, so add number to end
sUniqueName = sLineName + k;
k++;
bUnique = false;
//Exit loop
j = outerSeries.innerSeriesCount();
}
}
}
//If no label points, then pass the unique name from the line in via the labels array
if ( arLabelPoints.size() == 0 ) {
arLabels.add( 0, sUniqueName );
}
//Map points in chain to labels
String[] stnLabels = mapLabelsToChainPoints(arSurveyChain, arLabelPoints, arLabels);
//Add new series to list for this polyline
outerSeries.addSeries( makeSeriesFromPolyline( arSurveyChain, sUniqueName, stnLabels ) );
legCount += (arSurveyChain.size() - 1);
//Store chain and labels for this chain for linking to other chains later
allChains.add(arSurveyChain);
allChainLabels.add(stnLabels);
//Reset flag if station labels are null
if ( stnLabels[0] != null ) {
mappedLabelsFound = true;
}
//Reset series data array
arSurveyChain = new ArrayList();
}
else if ( parseMode == parseModeSurfaceContours ) {
//End section if any points were in target area
if ( iPointCount > 0 ) {
logger.logMessage("*end " + sLineName);
}
}
//Set state back to searching for next vertex
state = stateFindPolyline;
}
break;
default:
}
}
//Attempt to join up lines into connected polylines
int newlines = 0;
while ( arLines.size() > 0 ) {
//Get first line out
double[] line = arLines.remove(0);
//Start new polyline
arSurveyChain = new ArrayList();
//Add points to chain
double[] firstPoint = new double[3];
firstPoint[0] = line[0];
firstPoint[1] = line[1];
firstPoint[2] = line[2];
arSurveyChain.add(firstPoint);
double[] lastPoint = new double[3];
lastPoint[0] = line[3];
lastPoint[1] = line[4];
lastPoint[2] = line[5];
arSurveyChain.add(lastPoint);
//Search for adjoining lines
boolean added = true;
while ( added == true ) {
added = false;
for ( int linesIdx = 0; linesIdx < arLines.size(); linesIdx++ ) {
boolean match = false;
line = arLines.get(linesIdx);
if ( firstPoint[0] == line[0]
&& firstPoint[1] == line[1]
&& firstPoint[2] == line[2] ) {
//Add new first point to start of polyline
firstPoint = new double[3];
firstPoint[0] = line[3];
firstPoint[1] = line[4];
firstPoint[2] = line[5];
arSurveyChain.add(0, firstPoint);
match = true;
}
else if ( firstPoint[0] == line[3]
&& firstPoint[1] == line[4]
&& firstPoint[2] == line[5] ) {
//Add new first point to start of polyline
firstPoint = new double[3];
firstPoint[0] = line[0];
firstPoint[1] = line[1];
firstPoint[2] = line[2];
arSurveyChain.add(0, firstPoint);
match = true;
}
else if ( lastPoint[0] == line[0]
&& lastPoint[1] == line[1]
&& lastPoint[2] == line[2] ) {
//Add new last point to end of polyline
lastPoint = new double[3];
lastPoint[0] = line[3];
lastPoint[1] = line[4];
lastPoint[2] = line[5];
arSurveyChain.add(lastPoint);
match = true;
}
else if ( lastPoint[0] == line[3]
&& lastPoint[1] == line[4]
&& lastPoint[2] == line[5] ) {
//Add new last point to end of polyline
lastPoint = new double[3];
lastPoint[0] = line[0];
lastPoint[1] = line[1];
lastPoint[2] = line[2];
arSurveyChain.add(lastPoint);
match = true;
}
if ( match == true ) {
//Remove line and decrement index so next item is picked up
arLines.remove(linesIdx);
linesIdx--;
added = true;
}
}
}
//Map points in chain to labels
String[] stnLabels = mapLabelsToChainPoints(arSurveyChain, arLabelPoints, arLabels);
if ( arLabels.size() > 0 ) {
mappedLabelsFound = true;
}
//Add chain to array and create series
newlines++;
String sSeriesName = "SeriesFromLines" + newlines;
SurveySeries newSeries = makeSeriesFromPolyline( arSurveyChain, sSeriesName, stnLabels );
//Add series if it contains any legs (polylines where all points share the same location do not generate any legs)
if ( newSeries.legCount() > 0 ) {
allChains.add(arSurveyChain);
allChainLabels.add(stnLabels);
outerSeries.addSeries( newSeries );
legCount += (arSurveyChain.size() - 1);
}
}
if ( mappedLabelsFound ) {
//DXF has mapped labels for all chains. Rebuild series based on full station names.
SurveySeries rebuiltOuterSeries = new SurveySeries("SurveyFromDXFExportedFrom3D");
for ( int seriesIdx = 0; seriesIdx < outerSeries.innerSeriesCount(); seriesIdx++ ) {
//Get chain corresponding to this series
List srcChain = allChains.get(seriesIdx);
//Search through all legs in the series and put each into a new series matching the station name prefix
SurveySeries inputSeries = outerSeries.getInnerSeries( seriesIdx );
for ( int legIdx = 0; legIdx < inputSeries.legCount(); legIdx++ ) {
SurveyLeg inputLeg = inputSeries.getLegRaw( legIdx );
//Find series for from stn in this leg
String fromStnName = inputLeg.getFromStn().getName();
int matchingFromSeries = findMatchingSeriesForStnName( rebuiltOuterSeries, fromStnName );
if ( matchingFromSeries > -1 ) {
String toStnName = inputLeg.getToStn().getName();
int matchingToSeries = findMatchingSeriesForStnName( rebuiltOuterSeries, toStnName );
int legNewSeries = matchingFromSeries;
if ( matchingToSeries != matchingFromSeries ) {
//fromStn series does not match toStn series, so we need to determine which series this leg belongs to
boolean stnNameSwapped = false;
//Check if toStn has more than one matching point in the all points array
List matchingToPoints = getListOfMatchingPoints( srcChain.get( legIdx + 1 ), arLabelPoints );
if ( matchingToPoints.size() > 1 ) {
//Look for matching point label which does match the from station series
String fromStnPrefix = getNamePrefix( fromStnName );
if ( fromStnPrefix.length() > 0 ) {
String toStnPrefix = getNamePrefix( toStnName );
if ( toStnPrefix.length() > 0 ) {
for ( int matchingPointsIdx = 0; matchingPointsIdx < matchingToPoints.size(); matchingPointsIdx++ ) {
String testLabel = arLabels.get( matchingToPoints.get(matchingPointsIdx) );
String testLabelPrefix = getNamePrefix( testLabel );
if ( testLabelPrefix.equals(fromStnPrefix) ) {
//Replace the toStnName with the matching point which does match the from station prefix
inputLeg.getToStn().setName( testLabel );
//Add an equate for the alternate names being swapped for this stn
SurveyStation linkStn1 = new SurveyStation(0);
linkStn1.setName( getShortenedName( testLabel ) );
SurveyStation linkStn2 = new SurveyStation(0);
linkStn2.setName( getShortenedName( toStnName ) );
//TODO Only add link if not already present
rebuiltOuterSeries.addLink(rebuiltOuterSeries.getInnerSeries(matchingFromSeries).getSeriesName(),linkStn1,
rebuiltOuterSeries.getInnerSeries(matchingToSeries).getSeriesName(), linkStn2);
stnNameSwapped = true;
break;
}
}
}
}
}
if ( stnNameSwapped == false ) {
//No matching point for toStn was in the same series as the fromStn, so we need to find
//a matching label for the fromStn which is in the toStn series
//Check if fromStn has more than one matching point in the all points array
List matchingFromPoints = getListOfMatchingPoints( srcChain.get( legIdx ), arLabelPoints );
if ( matchingFromPoints.size() > 1 ) {
//Look for matching point label which does match the from station series
String fromStnPrefix = getNamePrefix( fromStnName );
if ( fromStnPrefix.length() > 0 ) {
String toStnPrefix = getNamePrefix( toStnName );
if ( toStnPrefix.length() > 0 ) {
for ( int matchingPointsIdx = 0; matchingPointsIdx < matchingFromPoints.size(); matchingPointsIdx++ ) {
String testLabel = arLabels.get( matchingFromPoints.get(matchingPointsIdx) );
String testLabelPrefix = getNamePrefix( testLabel );
if ( testLabelPrefix.equals(toStnPrefix) ) {
//Replace the fromStnName with the matching point which does match the to station prefix
inputLeg.getFromStn().setName( testLabel );
//Add an equate for the alternate names being swapped for this stn
SurveyStation linkStn1 = new SurveyStation(0);
linkStn1.setName( getShortenedName( fromStnName ) );
SurveyStation linkStn2 = new SurveyStation(0);
linkStn2.setName( getShortenedName( testLabel ) );
//TODO Only add link if not already present
rebuiltOuterSeries.addLink(rebuiltOuterSeries.getInnerSeries(matchingFromSeries).getSeriesName(),linkStn1,
rebuiltOuterSeries.getInnerSeries(matchingToSeries).getSeriesName(), linkStn2);
legNewSeries = matchingToSeries;
break;
}
}
}
}
}
}
}
//Remove the prefixes from both stations in each leg
String shortenedFromName = getShortenedName( inputLeg.getFromStn().getName() );
String shortenedToName = getShortenedName( inputLeg.getToStn().getName() ) ;
inputLeg.getFromStn().setName( shortenedFromName );
inputLeg.getToStn().setName( shortenedToName );
//Put leg into matching series in rebuilt survey after all changes to the station names
rebuiltOuterSeries.getInnerSeries( legNewSeries ).addLeg( inputLeg );
}
}
}
//Replace original with rebuilt series set
outerSeries = rebuiltOuterSeries;
}
else {
//Search for connected stations for any station in each polyline
for ( int seriesIdx = 0; seriesIdx < outerSeries.innerSeriesCount(); seriesIdx++ ) {
//Get chain corresponding to this series
List srcChain = allChains.get(seriesIdx);
String[] srcChainLabels = allChainLabels.get(seriesIdx);
for ( int point1Idx = 0; point1Idx < srcChain.size(); point1Idx++ ) {
//Get fixed position for this station in chain
double fixX = srcChain.get(point1Idx)[0];
double fixY = srcChain.get(point1Idx)[1];
double fixZ = srcChain.get(point1Idx)[2];
//Check all chains except self for a matching point
for ( int chainIdx = 0; chainIdx < allChains.size(); chainIdx++ ) {
List chain = allChains.get(chainIdx);
String[] chainLabels = allChainLabels.get(chainIdx);
for ( int pointIdx = 0; pointIdx < chain.size(); pointIdx++ ) {
if ( ( fixX == chain.get(pointIdx)[0] )
&& ( fixY == chain.get(pointIdx)[1] )
&& ( fixZ == chain.get(pointIdx)[2] ) ) {
//Found matching point
boolean addEquate = true;
if (chainIdx == seriesIdx ) {
//Matching point is in same series, so only add equate
//when matching station occurs after test station to avoid adding
//equate twice or equating stations onto themselves
if ( pointIdx <= point1Idx) {
addEquate = false;
}
}
if ( addEquate == true ) {
//Replace fixed point in leg with equate
SurveyStation linkStn1 = new SurveyStation(point1Idx);
if ( srcChainLabels[point1Idx] != null ) {
linkStn1.setName( srcChainLabels[point1Idx] );
}
SurveyStation linkStn2 = new SurveyStation(pointIdx);
if ( chainLabels[pointIdx] != null ) {
linkStn2.setName( chainLabels[pointIdx] );
}
outerSeries.addLink(outerSeries.getInnerSeries(seriesIdx).getSeriesName(), linkStn1,
outerSeries.getInnerSeries(chainIdx).getSeriesName(), linkStn2);
}
//Clear fixed points apart from first one in first series
if ( seriesIdx > 0 ) {
SurveyStation stn = outerSeries.getInnerSeries(seriesIdx).getLegRaw(0).getFromStn();
// double easting = stn.getEasting();
// double northing = stn.getNorthing();
// double altitude = stn.getAltitude();
// stn.setFixed(FixType.NONE, easting, northing, altitude);
stn.clearFixedStn();
}
}
}
}
}
}
}
//Put generated data series into survey
surveyData.add( outerSeries );
//Debug dump
UtilityFunctions.logSurveyDebugData(surveyData, logger);
//Completed file parsing
logger.logMessage("Processed " + legCount + " survey legs in " +
outerSeries.innerSeriesCount() + " series.");
logger.logMessage("Found:");
logger.logMessage("Polylines: " + iPolylineCount + " containing " + iVertexCount + " line segments.");
logger.logMessage("Lines: " + iPlainlineCount);
logger.logMessage("Total line segments: " + (iPlainlineCount + iVertexCount));
return surveyData;
}
/**
* Search for an inner series matching the name prefix on this station, and return it. If
* not found then create it and return it.
*
* @param series Series containing an inner series to match by name to the stn name
* @param stnName Stn name to match
* @return Matching inner series index
*/
private int findMatchingSeriesForStnName( SurveySeries series, String stnName ) {
int matchingIdx = -1;
//Determine prefix from station name
String stnPrefix = getNamePrefix( stnName );
if ( stnPrefix.length() > 0 ) {
for (int i = 0; i < series.innerSeriesCount(); i++ ) {
String seriesName = series.getInnerSeries( i ).getSeriesName();
if ( stnPrefix.equalsIgnoreCase( seriesName ) ) {
matchingIdx = i;
break;
}
}
//If not match found then create a new series for this name
if ( matchingIdx == -1 ) {
SurveySeries newSeries = new SurveySeries( stnPrefix );
series.addSeries( newSeries );
matchingIdx = series.innerSeriesCount() - 1;
}
}
return matchingIdx;
}
/**
* Takes a survey chain array, label points array and station labels array, and generates an array
* of labels for all the points in the chain. The index of any point in the chain will be the same
* a the index of the corresponding label in the output array.
* @param arSurveyChain Chain of points representing a survey chain (linear chain of legs)
* @param arLabelPoints Array of points which correspond the labels in the labels array
* @param stnLabels Array of labels corresponding to the points in the label points array
* @return Array of labels corresponding to the points in the survey chain
*/
private String[] mapLabelsToChainPoints( List arSurveyChain, List arLabelPoints,
List stnLabels ) {
//Create an array of labels and set them to the matching points
String[] labels = new String[arSurveyChain.size()];
for ( int i = 0; i < arSurveyChain.size(); i++ ) {
double[] chainPoint = arSurveyChain.get(i);
for ( int j = 0; j < arLabelPoints.size(); j++ ) {
//Check if point matches chain point
if ( ( chainPoint[0] == arLabelPoints.get(j)[0] )
&& ( chainPoint[1] == arLabelPoints.get(j)[1] )
&& ( chainPoint[2] == arLabelPoints.get(j)[2] ) ) {
labels[i] = stnLabels.get(j);
break;
}
}
}
return labels;
}
/**
* Converts a series of points in a polyline with absolute coordinates into a series
* of connected survey legs,calculating tape, compass and clino data.
* @param List List of all points in polyline as x,y,z coordinates
* @return SurveySeries of all the legs represented by the polyline. Fixed by position of first stn.
*/
private SurveySeries makeSeriesFromPolyline( List arSurveyChain, String seriesName, String[] labels ) {
//Create new series from this chain
SurveySeries series = new SurveySeries( seriesName );
//Set date
series.setSurveyDate( seriesDate );
//Loop through all points in chain
for ( int i = 1; i < arSurveyChain.size(); i++ ) {
SurveyLeg leg = new SurveyLeg();
SurveyStation fromStn = new SurveyStation( i - 1 );
SurveyStation toStn = new SurveyStation( i );
double[] startPoint = arSurveyChain.get( i - 1 );
double[] endPoint = arSurveyChain.get(i);
if ( i == 1 ) {
//First leg, so fix first station
fromStn.setFixed(FixType.OTHER, startPoint[0], startPoint[1], startPoint[2]);
}
//Calculate leg data
double x = endPoint[0] - startPoint[0];
double y = endPoint[1] - startPoint[1];
double z = endPoint[2] - startPoint[2];
//Only add point if in a different position to previous point in polyline (or series is only two points forming an equate)
if ( x != 0 || y != 0 || z != 0 || arSurveyChain.size() == 2 ) {
double hori = Math.pow( Math.pow( x, 2 ) + Math.pow( y, 2 ), 0.5 );
double tape = Math.pow( Math.pow( hori, 2 ) + Math.pow( z, 2 ), 0.5 );
double compass = bearing(x, y);
double clino = bearing(z, hori);
if ( clino > 90 ) {
clino = clino - 360;
}
//Set stn names from labels array if we have them
if (labels[i-1] != null) {
fromStn.setName( labels[i-1] );
}
if (labels[i] != null) {
toStn.setName( labels[i] );
}
leg.setFromStn( fromStn );
leg.setToStn( toStn );
leg.setLength( tape, LengthUnit.Metres );
leg.setCompass( compass, BearingUnit.Degrees );
leg.setClino( clino, GradientUnit.Degrees );
series.addLeg(leg);
}
}
// CleanSeries(series);
return series;
}
/**
* bearing
*
* Returns bearing for angle described by x and y lengths
*/
private double bearing(double x, double y) {
double res = 0;
if ( x == 0 ) {
if ( y < 0 ) {
res = 180;
}
else {
res = 0;
}
}
else if ( x < 0 ) {
if ( y == 0 ) {
res = 270;
}
else if ( y < 0 ) {
//180-270
res = 180 + (Math.atan(x / y) * 180 / Math.PI);
}
else {
//270-360
res = 360 + (Math.atan(x / y) * 180 / Math.PI);
}
}
else {
if ( y == 0 ) {
res = 90;
}
else if ( y < 0 ) {
//90-180
res = 180 + (Math.atan(x / y) * 180 / Math.PI);
}
else {
//0-90
res = (Math.atan(x / y) * 180 / Math.PI);
}
}
return res;
}
private double roundedDataValue( String dataLine ) {
double roundedVal = ( (double)Math.round( Double.valueOf(dataLine) * 10000 ) ) / 10000;
return roundedVal;
}
/**
* Clean up names in the survey series
* @param series
*/
private void CleanSeries( SurveySeries series ) {
//Count occurrences of each unique prefix on station names
List stnNamePrefixes = new ArrayList();
List stnNamePrefixCounts = new ArrayList();
for ( int i = 0; i < series.legCount(); i++ ) {
SurveyLeg leg = series.getLegRaw( i );
String name = leg.getFromStn().getName();
if ( name.contains(".") ) {
String prefix = name.substring( 0, name.lastIndexOf('.') );
boolean found = false;
for ( int j = 0; j < stnNamePrefixes.size(); j++ ) {
if ( prefix.equals( stnNamePrefixes.get(j) ) ) {
found = true;
int count = stnNamePrefixCounts.get(j);
count++;
stnNamePrefixCounts.set(j, count);
break;
}
}
if ( found == false ) {
stnNamePrefixes.add( prefix );
stnNamePrefixCounts.add(1);
}
}
name = leg.getToStn().getName();
if ( name.contains(".") ) {
String prefix = name.substring( 0, name.lastIndexOf('.') );
boolean found = false;
for ( int j = 0; j < stnNamePrefixes.size(); j++ ) {
if ( prefix.equals( stnNamePrefixes.get(j) ) ) {
found = true;
int count = stnNamePrefixCounts.get(j);
count++;
stnNamePrefixCounts.set(j, count);
break;
}
}
if ( found == false ) {
stnNamePrefixes.add( prefix );
stnNamePrefixCounts.add(1);
}
}
}
if ( stnNamePrefixes.size() > 0 ) {
//Find most common prefix then remove it from all station names and use as series name
int mostCommonPrefixIdx = 0;
int highestOccurence = 0;
for ( int j = 0; j < stnNamePrefixCounts.size(); j++ ) {
if ( stnNamePrefixCounts.get(j) > highestOccurence ) {
mostCommonPrefixIdx = j;
highestOccurence = stnNamePrefixCounts.get(j);
}
}
//Rename series using most common prefix
series.setSeriesName( stnNamePrefixes.get( mostCommonPrefixIdx ) );
}
}
/**
* Determines the prefix for a station name (all the string up to the last dot separator)
* @param name Station name to examine
* @return Prefix from the name, or an empty string if no dit separator in name.
*/
private String getNamePrefix( String name ) {
int lastDotPos = name.lastIndexOf('.');
String prefix = "";
if ( lastDotPos > -1 ) {
prefix = name.substring( 0, lastDotPos );
}
return prefix;
}
/**
* Determines the station name after the prefix (all the string after the last dot separator)
* @param name Station name to examine
* @return Shortened station name
*/
private String getShortenedName( String name ) {
int lastDotPos = name.lastIndexOf('.');
String shortName = name;
if ( lastDotPos > -1 ) {
shortName = name.substring( lastDotPos + 1 );
}
return shortName;
}
/**
* Returns a list of the indices in the label points array of all points matching the position of a given point
* @param matchingPoint The point to match
* @param labelPoints Array of points to search for matching points in
* @return ArrayList of the indices of all points at the same coordinates as the specified point
*/
private List getListOfMatchingPoints(double[] matchingPoint, List labelPoints) {
List matchingPoints = new ArrayList();
for ( int allPointsIdx = 0; allPointsIdx < labelPoints.size(); allPointsIdx++ ) {
double[] testPoint = labelPoints.get( allPointsIdx );
if ( ( matchingPoint[0] == testPoint[0])
&& ( matchingPoint[1] == testPoint[1])
&& ( matchingPoint[2] == testPoint[2]) ) {
//Add index of matching point to matches array
matchingPoints.add( allPointsIdx );
}
}
return matchingPoints;
}
}
CaveConverter_src/src/footleg/cavesurvey/data/reader/SurvexParser.java 0000644 0000000 0000000 00000104544 13036474244 025321 0 ustar root root /**
* Copyright (C) 2009-2017 Paul Fretwell - aka 'Footleg' (drfootleg@gmail.com)
*
* This file is part of Cave Converter.
*
* Cave Converter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cave Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cave Converter. If not, see .
*/
package footleg.cavesurvey.data.reader;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import footleg.cavesurvey.converter.CaveConverter;
import footleg.cavesurvey.converter.Logger;
import footleg.cavesurvey.converter.CaveConverter.BearingUnit;
import footleg.cavesurvey.converter.CaveConverter.GradientUnit;
import footleg.cavesurvey.converter.CaveConverter.LengthUnit;
import footleg.cavesurvey.data.model.CaveSurvey;
import footleg.cavesurvey.data.model.Equate;
import footleg.cavesurvey.data.model.SurveyLeg;
import footleg.cavesurvey.data.model.SurveySeries;
import footleg.cavesurvey.tools.UtilityFunctions;
/**
* Parser for Survex format text data files.
*
* @author Footleg
* @version 2017.01.12 (ISO 8601 YYYY.MM.DD)
* @since 1.6 (The Java version used)
*
* @to.do
* TODO Parse entrance flags
* TODO Parse fix flags
* TODO Parse calibration comments fields
* TODO Parse team fields
* TODO Parse instrument fields
*/
public class SurvexParser {
private Logger logger;
/**
* Class constructor
* @param logger Logging class to output information, warning and error messages to
*/
public SurvexParser( Logger logger ) {
super();
this.logger = logger;
}
private class DataBlockProperties {
public DataBlockProperties(boolean duplicateFlag, boolean splayFlag, boolean surfaceFlag, boolean noSurvey) {
super();
this.duplicateFlag = duplicateFlag;
this.splayFlag = splayFlag;
this.surfaceFlag = surfaceFlag;
this.noSurvey = noSurvey;
}
public DataBlockProperties(DataBlockProperties parentBlock) {
super();
this.duplicateFlag = parentBlock.duplicateFlag;
this.splayFlag = parentBlock.splayFlag;
this.surfaceFlag = parentBlock.surfaceFlag;
this.noSurvey = parentBlock.noSurvey;
this.parentBlock = parentBlock;
}
private boolean duplicateFlag = false;
private boolean splayFlag = false;
private boolean surfaceFlag = false;
private boolean noSurvey = false;
private DataBlockProperties parentBlock;
}
/**
* Parse survex format data into the cave data model
*
* @param surveyFileData ListArray of data lines from a survex file
* @param multifileRefs ArrayList of strings indicating the original file and line number for each data line in the
* surveyFileData ArrayList passed in. Used to report errors in logger for original survey data files when parsing multi-file Survex projects.
* @return Cave Survey object
* @throws ParseException Exception raised when information in a survey data file is not supported or valid for the format
*/
public CaveSurvey parseFile( List surveyFileData, List multifileRefs ) throws ParseException {
/**
* Read state codes:
* 0=starting new file
* 1=inside begin/end block
* 2=LRUD block
*/
int state = 0;
//Create cave survey object to hold data
CaveSurvey allSeries = new CaveSurvey(logger);
//Create stack to hold open series while processing data lines
List seriesStack = new ArrayList();
//Create stack to hold open series names while processing data lines
List nameStack = new ArrayList();
//Create list for equates
List equates = new ArrayList();
//Create a series instance to use as a pointer to the active series data is being read from
SurveySeries liveSeries = null;
//Create list to hold data order for active data block
List dataOrder = new ArrayList();
//Variable to hold values for parent block when inside an anonymous block
DataBlockProperties blockProps = new DataBlockProperties( false, false, false, false );
//Loop through all data lines
for ( int fileDataIdx=0; fileDataIdx < surveyFileData.size(); fileDataIdx++ ) {
//Get data line, file line number and line reference for the next data line from the survey data array list
int lineNo = fileDataIdx + 1;
String dataLine = surveyFileData.get(fileDataIdx);
String lineRef = "" + lineNo;
if ( multifileRefs != null ) {
lineRef = multifileRefs.get( fileDataIdx );
}
//Discard text after comment character
//TODO Keep comments and add into data model for legs, series and file heading
int commentPos = dataLine.indexOf(';');
if ( commentPos > -1 ) {
dataLine = dataLine.substring(0, commentPos);
}
//Trim whitespace off line ends
dataLine = dataLine.trim();
//Skip blank lines
if ( dataLine.length() > 0 ) {
//Check line for commands
if ( dataLine.charAt(0) == '*' ) {
//Process line into individual items trimming off initial '*'
String[] data = UtilityFunctions.cleanAndSplitDataLine( dataLine.substring(1) );
//Get command keyword
String cmd = data[0];
//Check for expected commands
if ( cmd.compareToIgnoreCase("BEGIN") == 0 ) {
//Start of new block
state = 1;
if (data.length == 2) {
//Create series
liveSeries = new SurveySeries(data[1]);
//Add name to stack
nameStack.add(data[1]);
//Get calibrations from last series and apply to new child series (if present)
double tapeCal = 0.0;
double compassCal = 0.0;
double clinoCal = 0.0;
double declinationCal = 0.0;
//Check if a parent series exists
if (seriesStack.size() > 0) {
SurveySeries series = seriesStack.get( seriesStack.size() - 1 );
tapeCal = series.getTapeCalibration(LengthUnit.Metres);
compassCal = series.getCompassCalibration(BearingUnit.Degrees);
clinoCal = series.getClinoCalibration(GradientUnit.Degrees);
declinationCal = series.getDeclination();
dataOrder = series.getDataOrder();
}
//Put new series onto stack
seriesStack.add(liveSeries);
//Apply calibrations from parent series.
//These will get overwritten if this series has it's own calibrations.
liveSeries.setTapeCalibration(tapeCal, LengthUnit.Metres);
liveSeries.setCompassCalibration(compassCal, BearingUnit.Degrees);
liveSeries.setClinoCalibration(clinoCal, GradientUnit.Degrees);
liveSeries.setDeclination(declinationCal);
liveSeries.setDataOrder(dataOrder);
//Copy flag settings into new child series and store parent settings to retrieve at series end
DataBlockProperties newBlock = new DataBlockProperties( blockProps );
//Make new properties block the current one
blockProps = newBlock;
}
else if (data.length < 2) {
//Anonymous block begin. Put properties from last block into new block as parent
DataBlockProperties newBlock = new DataBlockProperties( blockProps );
//then swap new block to current
blockProps = newBlock;
// throw new ParseException( UtilityFunctions.formatFileParserMsg("BEGIN/END blocks without names are not supported.", lineRef ), lineNo );
}
else {
//Do not support begin/end blocks names with white space
throw new ParseException( UtilityFunctions.formatFileParserMsg("BEGIN/END blocks names containing spaces are not supported.", lineRef ), lineNo );
}
}
else if ( cmd.compareToIgnoreCase("END") == 0 ) {
//End block
if ( data.length == 1 ) {
//Anonymous block ended, so just restore parent block flags
blockProps = blockProps.parentBlock;
}
else {
//Named block ended
String blockEndName = data[1];
//Check end matches end of section name
String currentBlockName = nameStack.get( nameStack.size() - 1 );
if ( currentBlockName.compareToIgnoreCase(blockEndName) == 0 ) {
//Found matching end block, so close series
//Remove live series from stack, as it is closed
SurveySeries endedSeries = seriesStack.remove( seriesStack.size() - 1 );
nameStack.remove( nameStack.size() - 1 );
//Restore block properties from parent series block
blockProps = blockProps.parentBlock;
if ( seriesStack.size() > 0) {
//Series is inside another, so make that live and add finished series to it
liveSeries = seriesStack.get( seriesStack.size() - 1 );
liveSeries.addSeries( endedSeries );
//Return state to 1
state = 1;
}
else {
//No other series on stack, so add to main cave survey
allSeries.add( endedSeries );
//Clear reference to live series
liveSeries = null;
//Return state to 0
state = 0;
}
}
else {
//Names of begin end blocks do not match
throw new ParseException( UtilityFunctions.formatFileParserMsg("Names of begin end blocks do not match. Begin=" +
currentBlockName + " End=" + blockEndName + ".", lineRef ), lineNo );
}
}
}
else if ( cmd.compareToIgnoreCase("EQUATE") == 0 ) {
//Get two parts of the equate, expanding to full nested series names
String fullSeriesPrefix = fullNestedSeriesName(nameStack);
Equate equate = new Equate( fullSeriesPrefix, data[1], fullSeriesPrefix, data[2] );
//Add to cache
equates.add(equate);
}
else if ( cmd.compareToIgnoreCase("DATA") == 0 ) {
//Reset nosurvey flag
blockProps.noSurvey = false;
//Check data command type
if ( data[1].compareToIgnoreCase("PASSAGE") == 0 ) {
//LRUD data block
state = 2;
}
else if ( data[1].compareToIgnoreCase("NORMAL") == 0 ) {
state = 1;
//Check data order
//TODO Add support for backcompass and backclino in normal data
logger.logMessage( "Found normal data header at line " + lineNo + ". Checking format." );
//Build array of expected categories here, and then loop through items picking off those that match until done.
//We will then know if all were found and the format is valid.
dataOrder.clear();
List allOrderKeys = new ArrayList();
allOrderKeys.add( CaveConverter.DATA_ORDER_CAT_FROMSTN );
allOrderKeys.add( CaveConverter.DATA_ORDER_CAT_TOSTN );
allOrderKeys.add( CaveConverter.DATA_ORDER_CAT_LENGTH );
allOrderKeys.add( CaveConverter.DATA_ORDER_CAT_BEARING );
allOrderKeys.add( CaveConverter.DATA_ORDER_CAT_CLINO );
allOrderKeys.add( CaveConverter.DATA_ORDER_CAT_IGNOREALL );
for (int dataIdx = 2; dataIdx < data.length; dataIdx++) {
//Get next item (including translating alternative terms)
String item = data[dataIdx].toUpperCase();
if ( item.compareTo("TAPE") == 0 ) {
item = CaveConverter.DATA_ORDER_CAT_LENGTH;
}
else if ( item.compareTo("COMPASS") == 0 ) {
item = CaveConverter.DATA_ORDER_CAT_BEARING;
}
else if ( item.compareTo("CLINO") == 0 ) {
item = CaveConverter.DATA_ORDER_CAT_CLINO;
}
//Look for item in remaining unused keys list
int foundIdx = allOrderKeys.indexOf( item );
if ( foundIdx > -1 ) {
//Add this item to dataOrder list, and remove from unused keys list
dataOrder.add( item );
allOrderKeys.remove( foundIdx );
}
else {
//Item not found, so unsupported data normal block
throw new ParseException( UtilityFunctions.formatFileParserMsg("Unsupported survex normal data order. Term '" + item + "' is not supported.", lineRef ), lineNo );
}
}
//Add to live series, as primary unless that is already set to diving,
//in which case set as secondary
List checkDataOrder = liveSeries.getDataOrder();
if ( checkDataOrder.size() > 0
&& ( UtilityFunctions.dataOrderIsDiving( checkDataOrder ) ) ) {
liveSeries.setDataOrder2(dataOrder);
}
else {
liveSeries.setDataOrder(dataOrder);
}
}
else if ( data[1].compareToIgnoreCase("DIVING") == 0 ) {
state = 1;
//Check data order
//TODO Add support for backcompass and backclino in diving data
logger.logMessage( "Found diving data header at line " + lineNo + ". Checking format." );
//Build array of expected categories here, and then loop through items picking off those that match until done.
//We will then know if all were found and the format is valid.
dataOrder.clear();
List allOrderKeys = new ArrayList();
allOrderKeys.add( CaveConverter.DATA_ORDER_CAT_FROMSTN );
allOrderKeys.add( CaveConverter.DATA_ORDER_CAT_TOSTN );
allOrderKeys.add( CaveConverter.DATA_ORDER_CAT_LENGTH );
allOrderKeys.add( CaveConverter.DATA_ORDER_CAT_BEARING );
allOrderKeys.add( CaveConverter.DATA_ORDER_CAT_FROMDEPTH );
allOrderKeys.add( CaveConverter.DATA_ORDER_CAT_TODEPTH );
allOrderKeys.add( CaveConverter.DATA_ORDER_CAT_DEPTHCHANGE );
allOrderKeys.add( CaveConverter.DATA_ORDER_CAT_IGNOREALL );
for (int dataIdx = 2; dataIdx < data.length; dataIdx++) {
//Get next item (including translating alternative terms)
String item = data[dataIdx].toUpperCase();
if ( item.compareTo("TAPE") == 0 ) {
item = CaveConverter.DATA_ORDER_CAT_LENGTH;
}
else if ( item.compareTo("COMPASS") == 0 ) {
item = CaveConverter.DATA_ORDER_CAT_BEARING;
}
//Look for item in remaining unused keys list
int foundIdx = allOrderKeys.indexOf( item );
if ( foundIdx > -1 ) {
//Add this item to dataOrder list, and remove from unused keys list
dataOrder.add( item );
allOrderKeys.remove( foundIdx );
//If a diving depth term, then remove alternative form
if ( item.compareTo(CaveConverter.DATA_ORDER_CAT_DEPTHCHANGE) == 0 ) {
//Remove from and to depth items from list of remaining valid terms
int altIdx = allOrderKeys.indexOf( CaveConverter.DATA_ORDER_CAT_FROMDEPTH );
if ( altIdx > -1 ) {
allOrderKeys.remove( altIdx );
}
altIdx = allOrderKeys.indexOf( CaveConverter.DATA_ORDER_CAT_TODEPTH );
if ( altIdx > -1 ) {
allOrderKeys.remove( altIdx );
}
}
else if ( item.compareTo(CaveConverter.DATA_ORDER_CAT_FROMDEPTH) == 0 ) {
//Remove depth change item from list of remaining valid terms
int altIdx = allOrderKeys.indexOf( CaveConverter.DATA_ORDER_CAT_DEPTHCHANGE );
if ( altIdx > -1 ) {
allOrderKeys.remove( altIdx );
}
//Check toDepth comes after fromDepth as parser only supports this ordering of depths in diving data
altIdx = allOrderKeys.indexOf( CaveConverter.DATA_ORDER_CAT_TODEPTH );
if ( altIdx == -1 ) {
throw new ParseException( UtilityFunctions.formatFileParserMsg("Unsupported survex diving data order. todepth before fromdepth is not supported.", lineRef ), lineNo );
}
}
}
else {
//Item not found, so unsupported data normal block
throw new ParseException( UtilityFunctions.formatFileParserMsg("Unsupported survex diving data order. Term '" + item + "' is not supported.", lineRef ), lineNo );
}
}
//Add to live series, as primary unless that is already normal,
//in which case set as secondary
List checkDataOrder = liveSeries.getDataOrder();
if ( checkDataOrder.size() > 0
&& ( UtilityFunctions.dataOrderIsDiving( checkDataOrder ) == false ) ) {
liveSeries.setDataOrder2(dataOrder);
}
else {
liveSeries.setDataOrder(dataOrder);
}
}
else if ( data[1].compareToIgnoreCase("NOSURVEY") == 0 ) {
state = 1;
//Check data order
//TODO Add support for nosurvey station format
logger.logMessage( "Found nosurvey data header at line " + lineNo + ". Checking format." );
//Build array of expected categories here, and then loop through items picking off those that match until done.
//We will then know if all were found and the format is valid.
dataOrder.clear();
List allOrderKeys = new ArrayList();
allOrderKeys.add( CaveConverter.DATA_ORDER_CAT_FROMSTN );
allOrderKeys.add( CaveConverter.DATA_ORDER_CAT_TOSTN );
for (int dataIdx = 2; dataIdx < data.length; dataIdx++) {
//Get next item
String item = data[dataIdx].toUpperCase();
//Look for item in remaining unused keys list
int foundIdx = allOrderKeys.indexOf( item );
if ( foundIdx > -1 ) {
//Add this item to dataOrder list, and remove from unused keys list
dataOrder.add( item );
allOrderKeys.remove( foundIdx );
}
else {
//Item not found, so unsupported data normal block
throw new ParseException( UtilityFunctions.formatFileParserMsg("Unsupported survex nosurvey data order. Term '" + item + "' is not supported.", lineRef ), lineNo );
}
}
//Set nosurvey flag
blockProps.noSurvey = true;
}
else {
//Other data settings not currently supported (assumes file use default order)
throw new ParseException( UtilityFunctions.formatFileParserMsg("Unsupported survex data command: " + data[1], lineRef ), lineNo );
}
}
else if ( cmd.compareToIgnoreCase("CALIBRATE") == 0 ) {
//Process calibration command
if (data.length == 3) {
String type = data[1];
double value = Double.parseDouble( data[2] );
if ( type.compareToIgnoreCase("tape") == 0 ) {
//Set tape calibration in active series
liveSeries.setTapeCalibration(value, liveSeries.getLengthUnit() );
}
else if ( type.compareToIgnoreCase("declination") == 0 ) {
//Set declination calibration in active series
liveSeries.setDeclination(value);
}
else if ( type.compareToIgnoreCase("compass") == 0 ) {
//Set compass calibration in active series
liveSeries.setCompassCalibration(value, liveSeries.getBearingUnit() );
}
else if ( type.compareToIgnoreCase("clino") == 0 ) {
//Set compass calibration in active series
liveSeries.setClinoCalibration(value, liveSeries.getGradientUnit() );
}
//TODO Add support for calibration scale factors
}
else {
//Invalid calibration lie
throw new ParseException( UtilityFunctions.formatFileParserMsg("CALIBRATE command did not contain a single instrument type plus value.", lineRef ), lineNo );
}
}
else if ( cmd.compareToIgnoreCase("DATE") == 0 ) {
//Process date
if (data.length > 1) {
Date value = UtilityFunctions.stringToDate(data[1], "yyyy.MM.dd");
liveSeries.setSurveyDate(value);
}
else {
logger.logMessage( "DATE command without further data skipped for line: " + dataLine);
}
}
else if ( cmd.compareToIgnoreCase("UNITS") == 0 ) {
//Determine unit being set
if ( data.length > 2 ) {
//Find unit in line
int unitIdx = 0;
for (int i = 2; i < data.length; i++) {
if ( ( data[i].compareToIgnoreCase("METRES") == 0 )
|| ( data[i].compareToIgnoreCase("METERS") == 0 )
|| ( data[i].compareToIgnoreCase("METRIC") == 0 )
|| ( data[i].compareToIgnoreCase("YARDS") == 0 )
|| ( data[i].compareToIgnoreCase("FEET") == 0 )
|| ( data[i].compareToIgnoreCase("DEGS") == 0 )
|| ( data[i].compareToIgnoreCase("DEGREES") == 0 )
|| ( data[i].compareToIgnoreCase("GRADS") == 0 )
|| ( data[i].compareToIgnoreCase("MILS") == 0 )
|| ( data[i].compareToIgnoreCase("MINUTES") == 0 )
|| ( data[i].compareToIgnoreCase("PERCENT") == 0 )
|| ( data[i].compareToIgnoreCase("PERCENTAGE") == 0 ) ) {
//Found unit, store index and then look back through items to set for this unit
unitIdx = i;
break;
}
}
if (unitIdx > 0) {
for (int i = 1; i < unitIdx; i++) {
if ( data[i].compareToIgnoreCase("TAPE") == 0 || data[i].compareToIgnoreCase("LENGTH") == 0 ) {
//TAPE/LENGTH, BACKTAPE/BACKLENGTH, COUNTER/COUNT, DEPTH, DX/EASTING, DY/NORTHING, DZ/ALTITUDE in YARDS|FEET|METRIC|METRES|METERS (default: METRES)
if ( data[unitIdx].compareToIgnoreCase("METRES") == 0 || data[unitIdx].compareToIgnoreCase("METERS") == 0 || data[unitIdx].compareToIgnoreCase("METRIC") == 0 ) {
liveSeries.setLengthUnit( LengthUnit.Metres );
}
else if ( data[unitIdx].compareToIgnoreCase("FEET") == 0 ) {
liveSeries.setLengthUnit( LengthUnit.Feet );
}
else if ( data[unitIdx].compareToIgnoreCase("YARDS") == 0 ) {
liveSeries.setLengthUnit( LengthUnit.Yards );
}
else {
throw new ParseException( UtilityFunctions.formatFileParserMsg("Unsupported length unit '" + data[unitIdx] + "'.", lineRef ), lineNo );
}
}
else if ( data[i].compareToIgnoreCase("DEPTH") == 0 ) {
//TAPE/LENGTH, BACKTAPE/BACKLENGTH, COUNTER/COUNT, DEPTH, DX/EASTING, DY/NORTHING, DZ/ALTITUDE in YARDS|FEET|METRIC|METRES|METERS (default: METRES)
if ( data[unitIdx].compareToIgnoreCase("METRES") == 0 || data[unitIdx].compareToIgnoreCase("METERS") == 0 || data[unitIdx].compareToIgnoreCase("METRIC") == 0 ) {
liveSeries.setDepthUnit( LengthUnit.Metres );
}
else if ( data[unitIdx].compareToIgnoreCase("FEET") == 0 ) {
liveSeries.setDepthUnit( LengthUnit.Feet );
}
else if ( data[unitIdx].compareToIgnoreCase("YARDS") == 0 ) {
liveSeries.setDepthUnit( LengthUnit.Yards );
}
else {
throw new ParseException( UtilityFunctions.formatFileParserMsg("Unsupported depth unit '" + data[unitIdx] + "'.", lineRef ), lineNo );
}
}
else if ( data[i].compareToIgnoreCase("COMPASS") == 0 || data[i].compareToIgnoreCase("BEARING") == 0 ) {
//COMPASS/BEARING, BACKCOMPASS/BACKBEARING, DECLINATION in DEG|DEGREES|GRADS|MILS|MINUTES (default: DEGREES)
if ( data[unitIdx].compareToIgnoreCase("DEGS") == 0 || data[unitIdx].compareToIgnoreCase("DEGREES") == 0 ) {
liveSeries.setBearingUnit( BearingUnit.Degrees );
}
else if ( data[unitIdx].compareToIgnoreCase("GRADS") == 0 || data[unitIdx].compareToIgnoreCase("MILS") == 0 ) {
liveSeries.setBearingUnit( BearingUnit.Grads );
}
else if ( data[unitIdx].compareToIgnoreCase("MINUTES") == 0 ) {
liveSeries.setBearingUnit( BearingUnit.Minutes );
}
else {
throw new ParseException( UtilityFunctions.formatFileParserMsg("Unsupported bearing unit '" + data[unitIdx] + "'.", lineRef ), lineNo );
}
}
else if ( data[i].compareToIgnoreCase("CLINO") == 0 || data[i].compareToIgnoreCase("GRADIENT") == 0 ) {
//CLINO/GRADIENT, BACKCLINO/BACKGRADIENT in DEG|DEGREES|GRADS|MILS|PERCENT|PERCENTAGE (default: DEGREES)
if ( data[unitIdx].compareToIgnoreCase("DEGS") == 0 || data[unitIdx].compareToIgnoreCase("DEGREES") == 0 ) {
liveSeries.setGradientUnit( GradientUnit.Degrees );
}
else if ( data[unitIdx].compareToIgnoreCase("GRADS") == 0 || data[unitIdx].compareToIgnoreCase("MILS") == 0 ) {
liveSeries.setGradientUnit( GradientUnit.Grads );
}
else if ( data[unitIdx].compareToIgnoreCase("PERCENT") == 0 || data[unitIdx].compareToIgnoreCase("PERCENTAGE") == 0 ) {
liveSeries.setGradientUnit( GradientUnit.Percent );
}
else if ( data[unitIdx].compareToIgnoreCase("MINUTES") == 0 ) {
liveSeries.setGradientUnit( GradientUnit.Minutes );
}
else {
throw new ParseException( UtilityFunctions.formatFileParserMsg("Unsupported gradient unit '" + data[unitIdx] + "'.", lineRef ), lineNo );
}
}
else {
throw new ParseException( UtilityFunctions.formatFileParserMsg("Unsupported unit type '" + data[i] + "'.", lineRef ), lineNo );
}
}
}
else {
throw new ParseException( UtilityFunctions.formatFileParserMsg("UNITS command did not contain a category of measurement plus value.", lineRef ), lineNo );
}
}
}
else if ( cmd.compareToIgnoreCase("FLAGS") == 0 ) {
//Process flags
boolean notPrefixed = false;
for (int iFlags = 1; iFlags < data.length; iFlags++) {
//Read all flags settings to determine what is being turned on or off
if (data[iFlags].compareToIgnoreCase("NOT") == 0 ) {
notPrefixed = true;
}
else if (data[iFlags].compareToIgnoreCase("DUPLICATE") == 0 ) {
blockProps.duplicateFlag = (notPrefixed == false);
notPrefixed = false;
}
else if (data[iFlags].compareToIgnoreCase("SPLAY") == 0 ) {
blockProps.splayFlag = (notPrefixed == false);
notPrefixed = false;
}
else if (data[iFlags].compareToIgnoreCase("SURFACE") == 0 ) {
blockProps.surfaceFlag = (notPrefixed == false);
notPrefixed = false;
}
else {
//Reset notPrefixed flag if any other value
notPrefixed = false;
}
}
}
else {
//Ignore other commands inside begin end block
//TODO Add support for FIX stations
//TODO Add support for ENTRANCE stations
//TODO Add support for topofil clino calibration scale factor
logger.logMessage("Unsupported Survex command ignored: " + cmd);
}
}
else {
//Data line
//logger.logMessage("Data line " + CaveConverter.padNumber(lineNo, 4) + ": " + dataLine);
if ( liveSeries != null ) {
//Process line into individual items
String[] data = UtilityFunctions.cleanAndSplitDataLine(dataLine);
switch (state) {
case 1:
//Create new survey leg
SurveyLeg leg = new SurveyLeg();
double fromDepth = 0;
//Create record from the items
int index = 0;
while ( index < data.length ) {
String item = data[index];
//Check for end of line
if ( item.charAt(0) == ';' ) {
//Comments, so ignore rest of line
//TODO Add support for leg comments
index = data.length;
}
else {
//Check data order was determined
if ( dataOrder.size() == 0 ) {
//No data order specified, so set to default
dataOrder.add( CaveConverter.DATA_ORDER_CAT_FROMSTN );
dataOrder.add( CaveConverter.DATA_ORDER_CAT_TOSTN );
dataOrder.add( CaveConverter.DATA_ORDER_CAT_LENGTH );
dataOrder.add( CaveConverter.DATA_ORDER_CAT_BEARING );
dataOrder.add( CaveConverter.DATA_ORDER_CAT_CLINO );
}
else if ( blockProps.noSurvey == false && dataOrder.size() <5 ) {
throw new ParseException( UtilityFunctions.formatFileParserMsg("Last data order command did not contain enough items for a survey leg.", lineRef ), lineNo );
}
else if ( ( dataOrder.size() < index + 1 )
&& ( dataOrder.get( dataOrder.size() - 1 ).equals( CaveConverter.DATA_ORDER_CAT_IGNOREALL ) ) == false ) {
throw new ParseException( UtilityFunctions.formatFileParserMsg("Last data order command did not contain enough items for data line.", lineRef ), lineNo );
}
//Put item into appropriate value
String itemCat = CaveConverter.DATA_ORDER_CAT_IGNOREALL;
if ( index < dataOrder.size() ) {
itemCat = dataOrder.get( index );
}
if ( itemCat.equals( CaveConverter.DATA_ORDER_CAT_FROMSTN ) ) {
//TODO Add support for retaining station name when not a number
leg.setFromStn( UtilityFunctions.createStationFromNameForSeries( data[index], liveSeries ) );
//Set nosurvey legs flag for nosurvey leg when setting fromStn
if ( blockProps.noSurvey ) {
leg.setNosurvey( true );
}
}
else if ( itemCat.equals( CaveConverter.DATA_ORDER_CAT_TOSTN ) ) {
//TODO Add support for retaining station name when not a number
leg.setToStn( UtilityFunctions.createStationFromNameForSeries( data[index], liveSeries ) );
}
else if ( itemCat.equals( CaveConverter.DATA_ORDER_CAT_LENGTH ) ) {
double length = Double.parseDouble( data[index] );
if ( length < 0 ) {
logger.logMessage("Warning: Negative leg length (" + data[index] +
") read from Survex file at line " + lineNo + ".");
}
leg.setLength( length, liveSeries.getLengthUnit() );
}
else if ( itemCat.equals( CaveConverter.DATA_ORDER_CAT_BEARING ) ) {
if ( data[index].compareTo("-") == 0 ) {
leg.setCompass( 0, BearingUnit.Degrees );
}
else {
leg.setCompass( Double.valueOf( data[index] ), liveSeries.getBearingUnit() );
}
}
else if ( itemCat.equals( CaveConverter.DATA_ORDER_CAT_CLINO ) ) {
//Trim data item after ';' if present
String val = data[index];
int commentCharPos = val.indexOf(';');
if ( commentCharPos > 0 ) {
val = val.substring(0, commentCharPos);
}
double straightDown = -90;
double straightUp = 90;
double level = 0;
if ( val.compareToIgnoreCase("-V") == 0 ) {
leg.setClino( straightDown, GradientUnit.Degrees );
}
else if ( val.compareToIgnoreCase("down") == 0 ) {
leg.setClino( straightDown, GradientUnit.Degrees );
}
else if ( val.compareToIgnoreCase("d") == 0 ) {
leg.setClino( straightDown, GradientUnit.Degrees );
}
else if ( val.compareToIgnoreCase("+V") == 0 ) {
leg.setClino( straightUp, GradientUnit.Degrees );
}
else if ( val.compareToIgnoreCase("up") == 0 ) {
leg.setClino( straightUp, GradientUnit.Degrees );
}
else if ( val.compareToIgnoreCase("u") == 0 ) {
leg.setClino( straightUp, GradientUnit.Degrees );
}
else if ( val.compareToIgnoreCase("-") == 0 ) {
leg.setClino( level, GradientUnit.Degrees );
}
else if ( val.compareToIgnoreCase("level") == 0 ) {
leg.setClino( level, GradientUnit.Degrees );
}
else {
leg.setClino( Double.valueOf( val ), liveSeries.getGradientUnit() );
}
}
else if ( itemCat.equals( CaveConverter.DATA_ORDER_CAT_FROMDEPTH ) ) {
//Store fromDepth, it will be added to leg when toDepth is read (parser only supports fromDepth before toDepth in data ordering)
fromDepth = Double.parseDouble( data[index] );
}
else if ( itemCat.equals( CaveConverter.DATA_ORDER_CAT_TODEPTH ) ) {
double toDepth = Double.parseDouble( data[index] );
leg.setDepths( fromDepth, toDepth, liveSeries.getDepthUnit() );
}
else if ( itemCat.equals( CaveConverter.DATA_ORDER_CAT_DEPTHCHANGE ) ) {
double depthChange = Double.parseDouble( data[index] );
leg.setDepthChange( depthChange, liveSeries.getDepthUnit() );
}
else if ( itemCat.equals( CaveConverter.DATA_ORDER_CAT_IGNOREALL ) ) {
//TODO Comment on line end?
}
}
index++;
}
//Check leg was found
if ( blockProps.noSurvey || leg.getLength( LengthUnit.Metres ) > -1 ) {
//Set flags for leg
leg.setDuplicate(blockProps.duplicateFlag);
leg.setSplay(blockProps.splayFlag);
leg.setSurface(blockProps.surfaceFlag);
//Add leg to series
liveSeries.addLeg(leg);
}
break;
case 2:
//Add data to LRUD cache
/**
* TODO Need to store all the LRUD lines in groups to match with
* the legs once the series is complete. Need to match legs to lrud
* from two consecutive lines to be sure the LRUD is for that leg, and
* not another leg from the same station. Create the LRUD group in
* the command parsing switch, as that is where we know a new LRUD group has
* been started.
*/
break;
}
}
else {
//Data line outside of series
throw new ParseException( UtilityFunctions.formatFileParserMsg("Data line found outside of any begin/end block.", lineRef ), lineNo );
}
}
}
}
//Process equates
UtilityFunctions.processEquates(equates, allSeries);
//Debug dump
UtilityFunctions.logSurveyDebugData(allSeries, logger);
//Completed file parsing
return allSeries;
}
//Get series names in stack to generate full series name
private String fullNestedSeriesName(List stack) {
String name = "";
Iterator stackIterator = stack.listIterator();
while ( stackIterator.hasNext() ) {
name += "." + stackIterator.next();
}
//Remove initial dot
return name.substring(1);
}
}
CaveConverter_src/src/footleg/cavesurvey/data/reader/PocketTopoParser.java 0000644 0000000 0000000 00000065342 13036417264 026115 0 ustar root root /**
* Copyright (C) 2009-2017 Paul Fretwell - aka 'Footleg' (drfootleg@gmail.com)
*
* This file is part of Cave Converter.
*
* Cave Converter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cave Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cave Converter. If not, see .
*/
package footleg.cavesurvey.data.reader;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import footleg.cavesurvey.converter.Logger;
import footleg.cavesurvey.converter.CaveConverter.BearingUnit;
import footleg.cavesurvey.converter.CaveConverter.GradientUnit;
import footleg.cavesurvey.converter.CaveConverter.LengthUnit;
import footleg.cavesurvey.data.model.CaveSurvey;
import footleg.cavesurvey.data.model.Equate;
import footleg.cavesurvey.data.model.SurveyLeg;
import footleg.cavesurvey.data.model.SurveySeries;
import footleg.cavesurvey.data.model.SurveyStation;
import footleg.cavesurvey.tools.UtilityFunctions;
/**
* Parser for PocketTopo Text export files.
*
* @author Footleg
* @version 2017.01.09 (ISO 8601 YYYY.MM.DD)
* @since 1.6 (The Java version used)
*/
public class PocketTopoParser {
private Logger logger;
//Constants used to indicate what type of data is encoded on a data line in the file being parsed
private final int LINE_TYPE_LEG = 1;
private final int LINE_TYPE_SPLAY = 2;
private final int LINE_TYPE_EQUATE = 3;
private final int LINE_TYPE_COMMENT = 4;
/**
* Class constructor
* @param logger Logging class to output information, warning and error messages to
*/
public PocketTopoParser( Logger logger ) {
super();
this.logger = logger;
}
/**
* Class to hold data parsed from a line in a PocketTopo exported text data file
*/
private class TopoDataLine {
// start stepping through the array from the beginning
private String fromStn = "";
private String toStn = "";
private double tape = 0;
private double compass = 0;
private double clino = 0;
private String trip = "";
private String comment = "";
private int lineType = -1;
public String getFromStn() {
return fromStn;
}
public void setFromStn(String fromStn) {
this.fromStn = fromStn;
}
public String getToStn() {
return toStn;
}
public void setToStn(String toStn) {
this.toStn = toStn;
}
public double getTape() {
return tape;
}
public void setTape(double tape) {
this.tape = tape;
}
public double getCompass() {
return compass;
}
public void setCompass(double compass) {
this.compass = compass;
}
public double getClino() {
return clino;
}
public void setClino(double clino) {
this.clino = clino;
}
public String getTrip() {
return trip;
}
public void setTrip(String trip) {
this.trip = trip;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public int getLineType() {
return lineType;
}
public void setLineType(int lineType) {
this.lineType = lineType;
}
}
/**
* Parse PocketTopo exported text data file into the cave data model
*
* @param surveyFileData ListArray of data lines from a PocketTopo text file
* @return Cave Survey object
* @throws ParseException Exception raised when information in a survey data file is not supported or valid for the format
*/
public CaveSurvey parseFile( List surveyFileData ) throws ParseException {
//Create new list of survey series to hold data
CaveSurvey surveyData = new CaveSurvey(logger);
SurveySeries outerSeries = null;
String caveName = "";
//Create cache for trip settings (contains list of arrays each
//containing 3 strings for the id, date and magnetic declination)
List trips = new ArrayList();
//Create list for equates
List equates = new ArrayList();
//Initialise read state
int state = 0;
//Create arrays to cache shots while processing each leg
List legShots = new ArrayList();
List splayShots = new ArrayList();
//Cache vars for last leg processed
String lastShotStart = "";
int lastToStn = -1;
int lastSeriesNo = -1;
String lastTripCode = "";
boolean newLeg = false; //Gets set to true when a new leg is started
//Create vars for active series refs
SurveySeries series = null;
int activeSeries = -1;
//Loop through all data lines
for ( int lineIdx = 0; lineIdx <= surveyFileData.size(); lineIdx++ ) {
int lineNo = lineIdx + 1;
//Need to allow final pass through loop after all lines processed
String dataLine = null;
if ( lineIdx < surveyFileData.size() ) {
dataLine = surveyFileData.get(lineIdx);
}
//Skip blank lines but not null line as this indicates end of file
//and data cached from previous lines needs to be output
if ( ( dataLine == null ) || ( dataLine.length() > 0 ) ) {
//Process data
switch (state) {
case 0: //Header line
//My Cave (m, 360)
//Check for triple space separator with bracket
int pos = dataLine.indexOf(" (");
if ( pos > 0 ) {
String part1 = dataLine.substring(0, pos);
String part2 = dataLine.substring(pos + 3);
if ( part2.compareTo("(m, 360)") != 0 ) {
//Not a valid header
ParseException ex = new ParseException("Invalid file header. Unsupported units: " + part2, lineNo);
throw ex;
}
else {
//Take cave name from header, and create a new series for the cave
caveName = part1.replace(' ', '_');
outerSeries = new SurveySeries( caveName );
surveyData.add( outerSeries );
logger.logMessage("Cave name: " + caveName);
}
}
state = 1;
break;
case 1: //Trip settings in Header
if ( dataLine.substring(0, 1).compareTo("[") == 0 ){
//Trip settings
List tripData = UtilityFunctions.parseDataStringIntoDataItems(dataLine);
//Copy into string array as rest of code uses a simple string array to store trip details
String[] trip = new String[tripData.size()];
if ( tripData.size() >= 3 ) {
if ( tripData.size() <= 4 ) {
//Write first 3 items into trip data record
for (int i = 0; i < tripData.size(); i++) {
trip[i] = tripData.get(i);
}
}
else {
//Invalid trip line (more than 4 items)
ParseException ex = new ParseException("Invalid file header. Trip data contain more than 4 items in line: " + dataLine, lineNo );
throw ex;
}
}
else {
//Invalid trip line (less than 3 items)
ParseException ex = new ParseException("Invalid file header. Trip data does not contain at least 3 items in line: " + dataLine, lineNo );
throw ex;
}
//Remove colon from trip code
if ( trip[0].charAt( trip[0].length() - 1 ) == ':' ) {
trip[0] = trip[0].substring( 0, trip[0].length() - 1 );
}
else {
//Invalid trip code
ParseException ex = new ParseException("Invalid file header. Unexpected trip code: " + trip[0] + " in line: " + dataLine, lineNo );
throw ex;
}
trips.add(trip);
logger.logMessage("Trip settings: " + trip[0] + " " + trip[1] + " " + trip[2]);
break;
}
else {
//Start of legs, so don't break, just update state
state = 2;
}
case 2: //Data lines
//Create temporary survey leg to parse the data line into
SurveyLeg shot = new SurveyLeg();
int seriesNo = -1;
String trip = "";
TopoDataLine data = null;
//Process data line if not at end of file
if ( dataLine != null ) {
// logger.logMessage("Data line " + CaveConverter.padNumber(lineNo, 4) + ": " + dataLine);
//Parse items into Topoline object
data = parseDataLine( UtilityFunctions.parseDataStringIntoDataItems( dataLine ) );
//Skip blank or all whitespace lines
if ( data.getLineType() > 0 ) {
//Add fields in common to all types of data line
int[] seriesStn = splitSeriesFromStn( data.getFromStn() );
seriesNo = seriesStn[0];
shot.setFromStn( new SurveyStation( seriesStn[1] ) );
shot.setLength( data.getTape(), LengthUnit.Metres );
shot.setCompass( data.getCompass(), BearingUnit.Degrees );
shot.setClino( data.getClino(), GradientUnit.Degrees );
shot.setComment( data.getComment() );
trip = data.getTrip();
if ( ( data.getLineType() == LINE_TYPE_LEG ) || ( data.getLineType() == LINE_TYPE_EQUATE ) ) {
//Survey Leg or equate also have a To Stn
int[] seriesStn2 = splitSeriesFromStn( data.getToStn() );
shot.setToStn( new SurveyStation( seriesStn2[1] ) );
//Check whether both stations are in same series
if ( seriesNo != seriesStn2[0] ) {
if ( data.getLineType() != LINE_TYPE_EQUATE ) {
//Leg changes series. Only allowed for equates.
ParseException ex = new ParseException("Legs linking different series must be zero length.", lineNo );
throw ex;
}
else {
//Series equate
Equate equate = new Equate( outerSeries.getSeriesName() + "." + seriesNo, shot.getFromStn().getName(),
outerSeries.getSeriesName() + "." + seriesStn2[0], shot.getToStn().getName() );
//Add to cache
equates.add(equate);
}
}
else if ( data.getLineType() == LINE_TYPE_EQUATE ) {
//Equate within series
Equate equate = new Equate( outerSeries.getSeriesName() + "." + seriesNo, shot.getFromStn().getName(),
outerSeries.getSeriesName() + "." + seriesStn2[0], shot.getToStn().getName() );
//Add to cache
equates.add(equate);
}
}
else {
//Splays do not have a to station, but the code relies on their being
//a dummy station, so create one with id = -1
shot.setToStn( new SurveyStation( -1 ) );
}
}
}
else {
//No more data, so just cached data to output from previous lines
//Initialise leg to dummy data so conditions below enable cached data to be processed
data = parseDataLine( UtilityFunctions.parseDataStringIntoDataItems( "DUMMY DUMMY 42.0 0.0 0.0 [1]" ) );
shot.setLength( data.getTape(), LengthUnit.Metres ); //Set dummy value so cached legs get processed
}
//Process shot if not an equate
if ( shot.getLength(LengthUnit.Metres) > 0 ) {
/**
* Check if this data is part of same leg already being processed.
* If the start station is different to the last one, or there is
* a to station which is different to the last to station then it
* is a new leg.
*/
if ( lastShotStart.length() == 0 ) {
//First shot in file, so store start station
lastShotStart = data.getFromStn();
}
else if ( data.getFromStn().compareTo( lastShotStart ) != 0 ) {
//Start of new leg (start stn is different to last data line)
newLeg = true;
}
else if ( ( shot.getToStn().getId() != -1 )
&& ( ( lastToStn != -1 ) )
&& ( shot.getToStn().getId() != lastToStn ) ) {
//Start of new leg (end stn is different to to stn on last data line where last data line had a to stn)
newLeg = true;
}
else if ( ( lastToStn != -1 ) && ( shot.getToStn().getId() == -1 ) ) {
//New leg (no to stn on current data line, but last line had one)
newLeg = true;
}
if ( newLeg ) {
//Starting new leg, so process data for last one before
//storing this data
//Create new leg to store store processed data
SurveyLeg masterLeg = new SurveyLeg();
if ( legShots.size() > 0 ) {
//Set stations using first leg
masterLeg.setFromStn( legShots.get(0).getFromStn() );
masterLeg.setToStn( legShots.get(0).getToStn() );
//Calculate averages for leg shots
double tape = 0.0;
double clino = 0.0;
String comments = "";
//Use utility function to average bearings so values either side of north average correctly
double[] bearings = new double[legShots.size()];
for ( int i = 0; i < legShots.size(); i++ ) {
tape += legShots.get(i).getLength(LengthUnit.Metres);
bearings[i] = legShots.get(i).getCompass(BearingUnit.Degrees);
clino += legShots.get(i).getClino(GradientUnit.Degrees);
//Append comments from multiple shots into one comment
if ( legShots.get(i).getComment().length() > 0 ) {
if ( comments.length() > 0 ) {
comments += "; " + legShots.get(i).getComment();
}
else {
comments = legShots.get(i).getComment();
}
}
}
double aveCompass = UtilityFunctions.averageCompassBearings(bearings);
//Update master leg
masterLeg.setLength( tape / legShots.size(), LengthUnit.Metres );
masterLeg.setCompass( aveCompass, BearingUnit.Degrees );
masterLeg.setClino( clino / legShots.size(), GradientUnit.Degrees );
masterLeg.setComment( comments );
}
//Determine if this leg belongs to the active series
if ( activeSeries != lastSeriesNo ) {
//Different series, see if this series already exists
boolean gotSeries = false;
for ( int j = 0; j < outerSeries.innerSeriesCount(); j++ ) {
//Check for matching series
if ( outerSeries.getInnerSeries(j).getSeriesName().compareTo( "" + lastSeriesNo ) == 0 ) {
//Found match, so get reference to this one
series = outerSeries.getInnerSeries(j);
gotSeries = true;
//Exit loop
j = outerSeries.innerSeriesCount();
}
}
if ( gotSeries == false ) {
//No series found, so need to create one
series = new SurveySeries( "" + lastSeriesNo );
//Find compass calibration for this new series
for ( int j = 0; j < trips.size(); j++ ) {
if ( lastTripCode.compareTo( trips.get(j)[0] ) == 0 ) {
//Use calibration for this trip
series.setDeclination( Double.valueOf( trips.get(j)[2] ) );
//Set date for trip (will be string of format yyyy/mm/dd)
String tripDateString = trips.get(j)[1];
Date tripDate = UtilityFunctions.stringToDate(tripDateString, "yyyy/MM/dd");
series.setSurveyDate( tripDate );
//Add comment to series if a trip comment exists
if ( trips.get(j).length == 4 ) {
series.setComment(trips.get(j)[3]);
}
}
}
//Add new series to list
outerSeries.addSeries( series );
}
//Set active series number to match new active series
activeSeries = lastSeriesNo;
}
//Process Splays for this leg
int stnSplayCount = 0;
String lastSplayFromStn = "";
for ( int i = 0; i < splayShots.size(); i++ ) {
//Need to increment splay count for number of splays already added to series from this station
//unless we already know this due to last splay being from the same station
if ( ( i == 0 ) || ( lastSplayFromStn.equals( splayShots.get(i).getFromStn().getName() ) == false ) ) {
//Count previous splays matching this fromStn in series
stnSplayCount = 0;
for ( int legIdx = 0; legIdx < series.legCount(); legIdx++ ) {
SurveyLeg chkLeg = series.getLegRaw( legIdx );
if ( ( chkLeg.isSplay() )
&& ( chkLeg.getFromStn().getName().equals( splayShots.get(i).getFromStn().getName() ) ) ) {
stnSplayCount++;
}
}
}
//Add shot as a splay
stnSplayCount++;
addSplayShot(series, splayShots.get(i), stnSplayCount);
}
//Add master leg to series unless a dummy leg (will have negative length)
if ( masterLeg.getLength(LengthUnit.Metres) >= 0 ) {
series.addLeg( masterLeg );
}
//Clear caches
legShots = new ArrayList();
splayShots = new ArrayList();
//Update stn names for this new leg
lastShotStart = data.getFromStn();
//Reset flags
newLeg = false;
}
//Process current leg unless a dummy end of file line
if ( dataLine != null ) {
//Determine whether a splay shot or a leg shot
if ( shot.getToStn().getId() == -1 ) {
//Passage dimension shot
splayShots.add(shot);
}
else {
//Survey Leg, add to list
legShots.add(shot);
}
//Cache series no for this leg to be used next loop
lastSeriesNo = seriesNo;
lastTripCode = trip;
//Store to stn for comparison on next pass
lastToStn = shot.getToStn().getId();
}
}
break;
default:
}
}
}
//Process equates
UtilityFunctions.processEquates(equates, surveyData);
//Debug dump
UtilityFunctions.logSurveyDebugData(surveyData, logger);
//Completed file parsing
// logger.logMessage("Processed " + legCount + " survey legs in " +
// surveyData.size() + " series.");
return surveyData;
}
/**
* Splits a series.station name string into the component integers
*
* @param dataIn String consisting of a numeric series name and numeric station name separated with a dot
* @return An array of two integers, the series number and the station number
*/
private int[] splitSeriesFromStn( String dataIn ) {
//Split a series.station into the component integers
int[] result = new int[2];
int pos = dataIn.indexOf('.');
if ( pos > 0 ) {
result[0] = Integer.valueOf( dataIn.substring( 0, pos ) );
result[1] = Integer.valueOf( dataIn.substring( pos + 1 ) );
}
else {
//Invalid series+station name
RuntimeException ex = new RuntimeException("Invalid dot separated series and station name : " + dataIn);
throw ex;
}
return result;
}
/**
* Determines what type of data a line of the file is representing
*/
private TopoDataLine parseDataLine( List dataItems ) {
//Equate lines do not always have trip numbers ( [1] )
//Lines may be and equate with an optional trip and optional comment
//e.g. 1.0 1.1 0.000 0.00 0.00
//e.g. 1.10 3.0 0.000 0.00 0.00 [1]
//e.g. 1.0 1.1 0.000 0.00 0.00 "Equate with a comment."
//e.g. 1.10 3.0 0.000 0.00 0.00 [1] "Equate with a comment."
//Lines can define a station with no other data, or just a comment
//e.g. 1.0 0.00 0.00 0.00
//e.g. 1.0 0.000 0.00 0.00 "Just a comment."
//Lines can represent legs, with or without comments
//e.g. 1.1 1.2 2.050 260.84 -56.40 [1]
//e.g. 1.0 1.2 5.234 3.18 -5.07 [1] "1.2=rawl plug on rear wall of blockhouse "
//Lines can represent splay shots, with or without comments
//e.g. 1.1 0.670 344.92 4.15 [1]
//e.g. 1.1 0.670 344.92 4.15 [1] "Splay with a comment."
TopoDataLine result = new TopoDataLine();
//Check line is valid (has at least 4 items)
if ( dataItems.size() > 4 ) {
//There will be either 4 or 5 data items in any line, followed by optional trip and comment items
int tripIdx = -1;
int commentIdx = -1;
String lastItem = dataItems.get( dataItems.size() - 1 );
String lastButOneItem = dataItems.get( dataItems.size() - 2 );
//Check if last item is a trip
if ( itemIsTrip( lastItem ) ) {
//Last item is trip
tripIdx = dataItems.size() - 1;
}
else if ( itemIsTrip( lastButOneItem ) ) {
//Last item is trip
tripIdx = dataItems.size() - 2;
//Last item in line has to be a comment
commentIdx = dataItems.size() - 1;
}
else {
//No trip, but is last item a comment?
if ( dataItems.size() > 5 ) {
//Last item in line has to be a comment
commentIdx = dataItems.size() - 1;
}
else if ( dataItems.size() == 5 ) {
//Last item in line is either a comment or a zero value
if ( itemIsZeroValue( lastItem ) == false ) {
//Last item in line has to be a comment
commentIdx = dataItems.size() - 1;
}
}
}
//All items have a from station
result.setFromStn( dataItems.get(0) );
if ( ( tripIdx == 4 ) || ( commentIdx == 4 ) ) {
//If trip or comment idx=4 then line is a splay leg
result.setTape( Double.valueOf( dataItems.get(1) ) );
result.setCompass( Double.valueOf( dataItems.get(2) ) );
result.setClino( Double.valueOf( dataItems.get(3) ) );
//Check whether line is really a splay, null data, or just a comment
if ( ( result.getTape() == 0.0 )
&& ( result.getCompass() == 0.0 )
&& ( result.getClino() == 0.0 ) ) {
if ( commentIdx > 0 ) {
result.setLineType( LINE_TYPE_COMMENT );
}
else {
//No data on line apart from from stn. Null line.
result.setLineType( 0 );
}
}
else {
result.setLineType( LINE_TYPE_SPLAY );
}
}
else if ( ( tripIdx == 5 ) || ( commentIdx == 5 ) || ( ( tripIdx == -1 ) && ( commentIdx == -1 ) ) ) {
//If trip or comment idx=5, or there is neither a trip or comment then line is a survey leg
result.setToStn( dataItems.get(1) );
result.setTape( Double.valueOf( dataItems.get(2) ) );
result.setCompass( Double.valueOf( dataItems.get(3) ) );
result.setClino( Double.valueOf( dataItems.get(4) ) );
//Check whether line is an equate or a leg
if ( ( result.getTape() == 0.0 )
&& ( result.getCompass() == 0.0 )
&& ( result.getClino() == 0.0 ) ) {
result.setLineType( LINE_TYPE_EQUATE );
}
else {
result.setLineType( LINE_TYPE_LEG );
}
}
//Add trip and comment if present
if ( tripIdx > 0 ) {
result.setTrip( dataItems.get( tripIdx ) );
}
if ( commentIdx > 0 ) {
result.setComment( dataItems.get( commentIdx ) );
}
}
return result;
}
private boolean itemIsTrip( String dataItem ) {
boolean isTrip = false;
if ( ( dataItem.length() > 2 ) && ( dataItem.length() < 4 )
&& ( dataItem.charAt( 0 ) == '[' ) && ( dataItem.charAt( dataItem.length() - 1 ) == ']' ) ) {
//Found trip item
isTrip = true;
}
return isTrip;
}
private boolean itemIsZeroValue( String dataItem ) {
boolean isZero = false;
//Check items starts with '0.0'
if ( dataItem.length() > 2 ) {
if ( ( dataItem.charAt(0) == '0' ) && ( dataItem.charAt(1) == '.' ) && ( dataItem.charAt(2) == '0' ) ) {
//Potentially a zero value string
isZero = true;
//Loop through remaining characters checking they are all zeros
for (int i = 3; i < dataItem.length(); i++) {
if ( dataItem.charAt(i) != '0' ) {
//Item is not a zero value
isZero = false;
i = dataItem.length();
}
}
}
}
else {
//Allow '0' and '00'
//Potentially a zero value string
isZero = true;
//Loop through remaining characters checking they are all zeros
for (int i = 0; i < dataItem.length(); i++) {
if ( dataItem.charAt(i) != '0' ) {
//Item is not a zero value
isZero = false;
i = dataItem.length();
}
}
}
return isZero;
}
/*
* processSplaysArray
*
* Processes an array of splay shots. Will return an LRUD dimension or
private double processSplaysArray( SurveyLeg splays[], int bestIdx, SurveySeries series ) {
for ( int i = 0; i < upShots.size(); i++ ) {
if ( i == bestIdx ) {
//Found best up shot
masterLeg.setUp( upShots.get(i).getLength() );
}
else {
//Other shots, add as splay
SurveyLeg splayShot = new SurveyLeg();
splayShot.setFromStn( upShots.get(i).getFromStn() );
String toStn = upShots.get(i).getFromStn() + new Character( (char)stnLetterCode++ ).toString();
splayShot.setToStn( CaveConverter.stnNameToNumber( toStn, series ) );
splayShot.setLength( upShots.get(i).getLength() );
splayShot.setCompass( upShots.get(i).getCompass() );
splayShot.setClino( upShots.get(i).getClino() );
splayShot.setSplay( true );
series.addLeg( splayShot );
legCount++;
}
}
}
*/
private void addSplayShot( SurveySeries series, SurveyLeg shot, int splayCount ){
//Generate station name suffix for splay (97 = letter 'a')
int increment = splayCount - 1; //Zero based
int stnLetterCode = 97; //97 = letter 'a'
String stnSuffix = "";
if ( splayCount < 27 ) {
stnSuffix = new Character( (char)(stnLetterCode + increment) ).toString();
}
else {
int primaryCount = (increment - 26) / 26;
int secondaryCount = (increment % 26);
stnSuffix = new Character( (char)(stnLetterCode + primaryCount) ).toString();
stnSuffix += new Character( (char)(stnLetterCode + secondaryCount) ).toString();
}
//Add shot as a splay
//TODO Stns names should be screened against legal character list, wherever it is that they are set
SurveyLeg splayShot = shot.clone();
String toStn = shot.getFromStn().getName() + stnSuffix;
splayShot.setToStn( UtilityFunctions.createStationFromNameForSeries( toStn, series ) );
splayShot.setLength( shot.getLength(LengthUnit.Metres), LengthUnit.Metres );
splayShot.setCompass( shot.getCompass(BearingUnit.Degrees), BearingUnit.Degrees );
splayShot.setClino( shot.getClino(GradientUnit.Degrees), GradientUnit.Degrees );
splayShot.setSplay( true );
series.addLeg( splayShot );
}
}
CaveConverter_src/src/footleg/cavesurvey/data/reader/CompassParser.java 0000644 0000000 0000000 00000050533 13036417266 025431 0 ustar root root /**
* Copyright (C) 2009-2017 Paul Fretwell - aka 'Footleg' (drfootleg@gmail.com)
*
* This file is part of Cave Converter.
*
* Cave Converter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cave Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cave Converter. If not, see .
*/
package footleg.cavesurvey.data.reader;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import footleg.cavesurvey.converter.CaveConverter.BearingUnit;
import footleg.cavesurvey.converter.CaveConverter.GradientUnit;
import footleg.cavesurvey.converter.CaveConverter.LengthUnit;
import footleg.cavesurvey.converter.Logger;
import footleg.cavesurvey.data.model.CaveSurvey;
import footleg.cavesurvey.data.model.Equate;
import footleg.cavesurvey.data.model.SurveyLeg;
import footleg.cavesurvey.data.model.SurveySeries;
import footleg.cavesurvey.tools.UtilityFunctions;
/**
* Parser for Compass format text data files.
*
* @author Footleg
* @version 2017.01.09 (ISO 8601 YYYY.MM.DD)
* @since 1.6 (The Java version used)
*
*/
public class CompassParser {
private Logger logger;
/**
* Class constructor
* @param logger Logging class to output information, warning and error messages to
*/
public CompassParser( Logger logger ) {
super();
this.logger = logger;
}
/**
* Parse Compass format data into the cave data model
*
* @param surveyFileData ListArray of data lines from a Compass file
* @return Cave Survey object
* @throws ParseException Exception raised when information in a survey data file is not supported or valid for the format
*/
public CaveSurvey parseFile( List surveyFileData ) throws ParseException {
/**
* Read state codes:
* 0=starting new file
* 1=header lines
* 2=calibration data
* 3=data line header
* 4=data lines
*/
int state = 0;
//Create cave survey object to hold data
CaveSurvey allSeries = new CaveSurvey(logger);
//Create a series instance to use as a pointer to the active series data is being read from
SurveySeries liveSeries = null;
//Declare flags to store status of legs being read
boolean duplicateFlag = false;
boolean ignoreLeg = false;
boolean splayFlag = false;
boolean surfaceFlag = false;
boolean backBearings = false;
//Loop through all data lines
for ( int i=0; i < surveyFileData.size(); i++ ) {
int lineNo = i + 1;
String dataLine = surveyFileData.get(i);
//Trim whitespace off line ends (unless a single form feed character)
if (dataLine.equalsIgnoreCase("\f") == false ) {
dataLine = dataLine.trim();
}
//Skip blank lines
if ( dataLine.length() > 0 ) {
switch (state) {
case 0:
//First line of series should be cave name
String caveName = dataLine.replace(' ', '_');
//Create series for cave name
liveSeries = new SurveySeries( caveName );
//Add to cave survey
allSeries.add( liveSeries );
state++;
break;
case 1:
//Header lines
caveName = dataLine.replace(' ', '_');
if ( caveName.compareTo( allSeries.get(0).getSeriesName() ) == 0 ) {
//Repeat of cave name, which we can ignore
}
else if ( dataLine.substring(0, 12).compareTo( "SURVEY NAME:" ) == 0 ) {
//Create series for this survey name
String seriesName = dataLine.substring(12).trim();
liveSeries = new SurveySeries( seriesName );
//Next line: Data and Comment
i++;
lineNo = i + 1;
String surveyDataComment = surveyFileData.get(i).trim();
int commentPos = surveyDataComment.indexOf("COMMENT:");
if ( surveyDataComment.substring(0, 12).compareTo( "SURVEY DATE:" ) == 0 ) {
//Check if comment was present on line
int dateEnd = commentPos;
if ( commentPos == -1 ) {
//No comment, so set commentPos to end of line
commentPos = surveyDataComment.length();
dateEnd = commentPos;
}
else {
//Set comment start position to after COMMENT:
commentPos = commentPos + 8;
}
if ( commentPos > 18 ) {
//Separate date and comment
String dateString = surveyDataComment.substring(12,dateEnd).trim();
String comment = surveyDataComment.substring( commentPos ).trim();
//Parse date
Date value = UtilityFunctions.stringToDate(dateString, "M d yy");
//Store date and comment in series
liveSeries.setSurveyDate(value);
liveSeries.setComment(comment);
//Survey team on next pair of lines
i++;
lineNo = i + 1;
String surveyTeam1 = surveyFileData.get(i).trim();
if ( surveyTeam1.substring(0, 12).compareTo( "SURVEY TEAM:" ) == 0 ) {
i++;
lineNo = i + 1;
String surveyTeam2 = surveyFileData.get(i).trim();
//TODO Store team details in survey series
//Header section done
state++;
}
else {
throw new ParseException( UtilityFunctions.formatFileParserMsg("Did not find expected 'SURVEY TEAM:' at start of line.", lineNo ), lineNo );
}
}
else {
throw new ParseException( UtilityFunctions.formatFileParserMsg("Did not find expected 'COMMENT:' or valid length date string on line.", lineNo ), lineNo );
}
}
else {
throw new ParseException( UtilityFunctions.formatFileParserMsg("Did not find expected 'SURVEY DATE:' at start of line.", lineNo ), lineNo );
}
}
else {
throw new ParseException( UtilityFunctions.formatFileParserMsg("Did not find expected 'SURVEY NAME:' line.", lineNo ), lineNo );
}
break;
case 2:
//calibration data
if ( dataLine.substring(0, 12).compareTo( "DECLINATION:" ) == 0 ) {
int formatPos = dataLine.indexOf("FORMAT:");
int formatEndPos = dataLine.length();
int corrections1Pos = dataLine.indexOf("CORRECTIONS:");
int corrections1EndPos = dataLine.length();
int corrections2Pos = dataLine.indexOf("CORRECTIONS2:");
int corrections2EndPos = dataLine.length();
int declinationEndPos = dataLine.length();
//Determine end positions of data items
if ( corrections2Pos > 12 ) {
corrections1EndPos = corrections2Pos;
if ( corrections1Pos > 12 ) {
formatEndPos = corrections1Pos;
}
else {
formatEndPos = corrections2Pos;
}
}
else {
if ( corrections1Pos > 12 ) {
formatEndPos = corrections1Pos;
}
}
//Process optional file format code
if ( formatPos > 12 ) {
declinationEndPos = formatPos;
String formatCode = dataLine.substring(formatPos+7,formatEndPos).trim();
//TODO Set series default units (once supported in series class)
//TODO Set default data order for series (once supported in series class)
//TODO Handle LRUD on toStns (currently assumes they are for fromStns)
}
//Process declination
String declination = dataLine.substring(12,declinationEndPos).trim();
liveSeries.setDeclination( - Double.parseDouble( declination ) );
//Process optional corrections
if ( corrections1Pos > 12 ) {
String corrections = dataLine.substring(corrections1Pos+12,corrections1EndPos).trim();
String[] data = UtilityFunctions.cleanAndSplitDataLine(corrections);
liveSeries.setCompassCalibration( - Double.parseDouble( data[0] ), BearingUnit.Degrees );
liveSeries.setClinoCalibration( - Double.parseDouble( data[1] ), GradientUnit.Degrees );
liveSeries.setTapeCalibration( - Double.parseDouble( data[2] ), LengthUnit.Feet );
}
//Process optional corrections2
if ( corrections2Pos > 12 ) {
String corrections2 = dataLine.substring(corrections2Pos+13,corrections2EndPos).trim();
String[] data = UtilityFunctions.cleanAndSplitDataLine(corrections2);
//TODO Handle corrections for second set of instruments used for back bearings
}
//Declination line done
state++;
}
else {
throw new ParseException( UtilityFunctions.formatFileParserMsg("Did not find expected 'DECLINATION:' line.", lineNo ), lineNo );
}
break;
case 3:
//data line header
//"FROM TO LENGTH BEARING INC LEFT UP DOWN RIGHT FLAGS COMMENTS"
//"FROM TO LENGTH BEARING DIP LEFT UP DOWN RIGHT AZM2 INC2 FLAGS COMMENTS"
//TODO Support different ordering of LRUD fields in Compass files
String[] headerData = UtilityFunctions.cleanAndSplitDataLine(dataLine);
if ( ( headerData[0].compareTo("FROM") == 0 )
&& ( headerData[1].compareTo("TO") == 0 )
&& ( headerData[2].compareTo("LENGTH") == 0 )
&& ( headerData[3].compareTo("BEARING") == 0 )
&& ( ( headerData[4].compareTo("INC") == 0 ) || ( headerData[4].compareTo("DIP") == 0 ) ) ) {
if ( ( headerData[9].compareTo("AZM2") == 0 )
&& ( headerData[11].compareTo("FLAGS") == 0 )
&& ( headerData[12].compareTo("COMMENTS") == 0 ) ) {
//Header for data with back bearings
backBearings = true;
//Ready to read data lines
state++;
}
else if ( ( headerData[9].compareTo("FLAGS") == 0 )
&& ( headerData[10].compareTo("COMMENTS") == 0 ) ) {
//Header for data without back bearings
backBearings = false;
//Ready to read data lines
state++;
}
if ( ( headerData[5].compareTo("LEFT") == 0 )
&& ( headerData[6].compareTo("UP") == 0 )
&& ( headerData[7].compareTo("DOWN") == 0 )
&& ( headerData[8].compareTo("RIGHT") == 0 ) ) {
//Header for data with back bearings
}
else {
throw new ParseException( UtilityFunctions.formatFileParserMsg(
"LRUD data heading indicates unsupported order. Currently only LEFT UP DOWN RIGHT field order is supported.", lineNo ), lineNo );
}
}
else {
throw new ParseException( UtilityFunctions.formatFileParserMsg("Did not find expected data heading line.", lineNo ), lineNo );
}
break;
case 4:
//data lines
//" 1 2 85.00 45.00 0.00 10.00 3.00 0.00 10.00 #|L#"
//" 7 8 17.00 201.00 0.00 0.00 5.00 0.00 4.00 tributary enters here on left "
//Check for end of series (form feed character)
if (dataLine.equalsIgnoreCase("\f") ) {
//Close series to cave name series and start searching for next one
allSeries.get(0).addSeries( liveSeries );
state = 1;
}
else {
//Process data line
String[] data = UtilityFunctions.cleanAndSplitDataLine(dataLine);
SurveyLeg leg = new SurveyLeg();
//Create record from the items
int index = 0;
while ( index < data.length ) {
String item = data[index];
//Put item into appropriate value
switch ( index ) {
case 0:
//TODO Add support for retaining station name when not a number
leg.setFromStn( UtilityFunctions.createStationFromNameForSeries( item, liveSeries ) );
break;
case 1:
//TODO Add support for retaining station name when not a number
leg.setToStn( UtilityFunctions.createStationFromNameForSeries( item, liveSeries ) );
break;
case 2:
leg.setLength( Double.parseDouble( item ), LengthUnit.Feet );
break;
case 3:
if ( item.compareTo("-") == 0 ) {
leg.setCompass( 0, BearingUnit.Degrees );
}
else {
leg.setCompass( Double.valueOf( item ), BearingUnit.Degrees );
}
break;
case 4:
String val = item;
//TODO Does compass support all these Survex like clino values?
double straightDown = -90;
double straightUp = 90;
double level = 0;
if ( val.compareToIgnoreCase("-V") == 0 ) {
leg.setClino( straightDown, GradientUnit.Degrees );
}
else if ( val.compareToIgnoreCase("down") == 0 ) {
leg.setClino( straightDown, GradientUnit.Degrees );
}
else if ( val.compareToIgnoreCase("d") == 0 ) {
leg.setClino( straightDown, GradientUnit.Degrees );
}
else if ( val.compareToIgnoreCase("+V") == 0 ) {
leg.setClino( straightUp, GradientUnit.Degrees );
}
else if ( val.compareToIgnoreCase("up") == 0 ) {
leg.setClino( straightUp, GradientUnit.Degrees );
}
else if ( val.compareToIgnoreCase("u") == 0 ) {
leg.setClino( straightUp, GradientUnit.Degrees );
}
else if ( val.compareToIgnoreCase("-") == 0 ) {
leg.setClino( level, GradientUnit.Degrees );
}
else if ( val.compareToIgnoreCase("level") == 0 ) {
leg.setClino( level, GradientUnit.Degrees );
}
else {
leg.setClino( Double.valueOf(val), GradientUnit.Degrees );
}
break;
case 5:
case 6:
case 7:
case 8:
//"FROM TO LENGTH BEARING INC LEFT UP DOWN RIGHT FLAGS COMMENTS"
//Ignore LRUD values which are -9999.00
if ( ( item.equals("-9999.00") == false ) && ( item.equals("-9.90") == false ) ) {
if (index == 5) {
leg.setLeft( Double.parseDouble( item ), LengthUnit.Feet );
}
else if (index == 6) {
leg.setUp( Double.parseDouble( item ), LengthUnit.Feet );
}
else if (index == 7) {
leg.setDown( Double.parseDouble( item ), LengthUnit.Feet );
}
else if (index == 8) {
leg.setRight( Double.parseDouble( item ), LengthUnit.Feet );
}
}
break;
case 9:
if ( backBearings ) {
//TODO Support for back bearings compass reading
String backBearing = item;
}
break;
case 10:
if ( backBearings ) {
//TODO Support for back bearings clino reading
String backClino = item;
}
break;
}
index++;
}
//Process flags and comments off end of string from fixed position
//(as comment will have been split into multiple data array items)
String commentFlagsData = "";
//Reinitialise all flags for each leg
duplicateFlag = false;
ignoreLeg = false;
splayFlag = false;
surfaceFlag = false;
//Determine if any flags or comments data exists on this line
int dataEndPos = surveyFileData.get(i).length();
if ( backBearings ) {
if ( data.length > 11 ) {
String firstItem = data[11];
dataEndPos = surveyFileData.get(i).indexOf(firstItem);
}
}
else {
if ( data.length > 9 ) {
String firstItem = data[9];
dataEndPos = surveyFileData.get(i).indexOf(firstItem);
}
}
if ( surveyFileData.get(i).length() > dataEndPos ) {
commentFlagsData = surveyFileData.get(i).substring( dataEndPos );
}
//Initialise comment to entire flags/comments data item
String comment = commentFlagsData;
int commentStartPos = 0;
if ( commentFlagsData.startsWith("#|") ) {
commentStartPos = commentFlagsData.indexOf("#", 2) + 1;
}
if ( commentStartPos > 3 ) {
//Process flags
String flags = commentFlagsData.substring(2, commentStartPos - 1);
//Handle 'L' which indicates duplicate
if ( flags.indexOf("L") > -1 ) {
duplicateFlag = true;
}
//Handle 'X' which indicates duplicate
if ( flags.indexOf("X") > -1 ) {
ignoreLeg = true;
}
//TODO Store flags 'P' and 'C' which mean do not plot, and do not apply loop closure
//Set comment to the text after the flags end character
comment = commentFlagsData.substring(commentStartPos).trim();
}
//Set leg comment
leg.setComment(comment);
//Check leg was found
if ( leg.getLength(LengthUnit.Metres) > -1 ) {
//Set flags for leg
leg.setDuplicate(duplicateFlag);
leg.setSplay(splayFlag);
leg.setSurface(surfaceFlag);
//Check if ignore leg flag was set
if ( ignoreLeg == false ) {
//Add leg to series
liveSeries.addLeg(leg);
}
}
}
break;
}
}
}
//Generate equates by matching station names
List equates = new ArrayList();
//Loop through all series
for ( int idx1 = 0; idx1 < allSeries.get(0).getInnerSeriesList().size(); idx1++ ) {
SurveySeries series = allSeries.get(0).getInnerSeries(idx1);
logger.logMessage("Searching for equivalent stations in series " + (idx1 + 1) + " of " +
allSeries.get(0).getInnerSeriesList().size() + "...");
//Loop through all station names in series
for ( int idxLeg = 0; idxLeg < series.legCount(); idxLeg++ ) {
for ( int idxStn = 0; idxStn < 2; idxStn++ ) {
String stn1Name = "";
if ( idxStn == 0 ) {
stn1Name = series.getLegRaw(idxLeg).getFromStn().getName();
}
else {
stn1Name = series.getLegRaw(idxLeg).getToStn().getName();
}
//Check for matching station name in all following series
for ( int idx2 = idx1 + 1; idx2 < allSeries.get(0).getInnerSeriesList().size(); idx2++ ) {
SurveySeries series2 = allSeries.get(0).getInnerSeries(idx2);
//Loop through all station names in series
for ( int idxLeg2 = 0; idxLeg2 < series2.legCount(); idxLeg2++ ) {
for ( int idxStn2 = 0; idxStn2 < 2; idxStn2++ ) {
String stn2Name = "";
if ( idxStn2 == 0 ) {
stn2Name = series2.getLegRaw(idxLeg2).getFromStn().getName();
}
else {
stn2Name = series2.getLegRaw(idxLeg2).getToStn().getName();
}
//Check for matching station name in all following series
if ( stn1Name.compareTo(stn2Name) == 0 ) {
//Matching stations found
String series1Name = allSeries.get(0).getSeriesName() + "." + series.getSeriesName();
String series2Name = allSeries.get(0).getSeriesName() + "." + series2.getSeriesName();
//Check if equate already added
boolean foundNewEquate = true;
for ( int idxEquates = 0; idxEquates < equates.size(); idxEquates++ ) {
Equate testEquate = equates.get(idxEquates);
if ( ( series1Name.compareTo(testEquate.getSeries1()) == 0 )
&& ( series2Name.compareTo(testEquate.getSeries2()) == 0 )
&& ( stn1Name.compareTo(testEquate.getStn1()) == 0 )
&& ( stn2Name.compareTo(testEquate.getStn2()) == 0 ) ) {
foundNewEquate = false;
}
else if ( ( series1Name.compareTo(testEquate.getSeries2()) == 0 )
&& ( series2Name.compareTo(testEquate.getSeries1()) == 0 )
&& ( stn1Name.compareTo(testEquate.getStn2()) == 0 )
&& ( stn2Name.compareTo(testEquate.getStn1()) == 0 ) ) {
foundNewEquate = false;
}
}
if ( foundNewEquate ) {
//Create and add equate
Equate newEquate = new Equate( series1Name, stn1Name, series2Name, stn2Name);
equates.add(newEquate);
}
}
}
}
}
}
}
}
//Process equates
UtilityFunctions.processEquates(equates, allSeries);
//Debug dump
UtilityFunctions.logSurveyDebugData(allSeries, logger);
//Completed file parsing
return allSeries;
}
}
CaveConverter_src/src/footleg/cavesurvey/gui/ 0000755 0000000 0000000 00000000000 13036467352 020410 5 ustar root root CaveConverter_src/src/footleg/cavesurvey/gui/app.properties 0000644 0000000 0000000 00000000273 13035163056 023301 0 ustar root root # build properties
application.vendor = Footleg
application.title = Cave Converter
build.version = dev-debug
character.set=UTF8
font.typeface=courier
font.size=12
application.skin=System
CaveConverter_src/src/footleg/cavesurvey/gui/swing/ 0000755 0000000 0000000 00000000000 13036467352 021537 5 ustar root root CaveConverter_src/src/footleg/cavesurvey/gui/swing/CaveModelVisualiser.java 0000644 0000000 0000000 00000026526 13034712616 026315 0 ustar root root /**
* Copyright (C) 2009-2017 Paul Fretwell - aka 'Footleg' (drfootleg@gmail.com)
*
* This file is part of Cave Converter.
*
* Cave Converter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cave Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cave Converter. If not, see .
*/
package footleg.cavesurvey.gui.swing;
import java.awt.Dimension;
import java.awt.Font;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import footleg.cavesurvey.converter.CaveConverter;
import footleg.cavesurvey.converter.CaveConverter.BearingUnit;
import footleg.cavesurvey.converter.CaveConverter.GradientUnit;
import footleg.cavesurvey.converter.CaveConverter.LengthUnit;
import footleg.cavesurvey.converter.CaveConverter.SurveyDataOutputFormats;
import footleg.cavesurvey.converter.Logger;
import footleg.cavesurvey.data.model.CaveSurvey;
import footleg.cavesurvey.data.model.SurveyLeg;
import footleg.cavesurvey.data.model.SurveySeries;
import footleg.cavesurvey.data.writer.SurvexWriter;
import footleg.cavesurvey.data.writer.TopoRobotWriter;
import footleg.cavesurvey.tools.SurveyProcessing;
/**
* Component to display the cave data model
*
* @author Footleg
* @version 2017.01.09 (ISO 8601 YYYY.MM.DD)
* @since 1.7 (The Java version used)
*
* @to.do
* TODO Add proper visualisation of data model
*/
@SuppressWarnings("serial")
public class CaveModelVisualiser extends JSplitPane {
public class SelectionListener implements TreeSelectionListener {
public void valueChanged(TreeSelectionEvent se) {
JTree jTree = (JTree) se.getSource();
Object selectedNode = (Object) jTree.getLastSelectedPathComponent();
boolean enableSeriesProcessing = false;
if (selectedNode instanceof CaveSurvey) {
enableSeriesProcessing = false;
CaveSurvey survey = (CaveSurvey) selectedNode;
taCaveLog.append( ReportCaveSurveyStats( survey ) );
}
else if (selectedNode instanceof SurveySeries ) {
enableSeriesProcessing = true;
SurveySeries selectedSeries = (SurveySeries) selectedNode;
taCaveLog.setText( ReportSeriesStats(selectedSeries) );
}
else if (selectedNode instanceof SurveyLeg) {
SurveyLeg selectedLeg = (SurveyLeg) selectedNode;
taCaveLog.setText( ReportLegStats(selectedLeg) );
}
//Enable or disable survey series actions
parent.setSurveySeriesActionsEnabled( enableSeriesProcessing );
}
}
private MainForm parent;
private JTextArea taCaveLog;
private CaveSurvey caveModel;
private JTree tree;
private Logger logger;
/**
* Create TextArea scrollable tab instance and display model summary. This is a very basic start for a
* full visualiser for the cave data model.
* @param parentForm Object reference to the parent form owning this item (so actions can be accessed)
* @param model The cave survey model to build a visualisation of
* @param font The font to use in the text area pane
*/
public CaveModelVisualiser(MainForm parentForm, CaveSurvey model, Font font) {
super();
caveModel = model;
parent = parentForm;
logger = parentForm.getLogger();
//Create scrollable text area for right pane
taCaveLog = new JTextArea();
taCaveLog.setFont( font );
JScrollPane spCaveLog = new JScrollPane (taCaveLog, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
spCaveLog.setViewportView(taCaveLog);
//Create tree for left pane, using the CaveSurvey class as the TreeModel
tree = new JTree( caveModel );
tree.addTreeSelectionListener( new SelectionListener() );
JScrollPane spSurveyTree = new JScrollPane (tree, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
//Configure split pane
spCaveLog.setPreferredSize(new Dimension(250, 400));
spSurveyTree.setPreferredSize(new Dimension(250, 400));
this.setTopComponent( spSurveyTree );
this.setBottomComponent( spCaveLog );
renderCaveModel();
}
public CaveSurvey getCaveModel() {
return caveModel;
}
/**
* To pass a new cave model to an existing visualiser tab
* @param caveModel The cave survey model to build a visualisation of
*/
public void setCaveModel(CaveSurvey caveModel) {
this.caveModel = caveModel;
//Pass the new model to the jTree
tree.setModel( getCaveModel() );
renderCaveModel();
}
/**
* @return the split pane
*/
public JSplitPane getTabPaneComponent() {
return this;
}
/**
* Generates a visualisation of the cave data (currently a bare bones text summary, but intended to be developed).
*/
private void renderCaveModel() {
taCaveLog.setText("This is a bare bones implementation of the visualisation of the cave model." + CaveConverter.newline +
"Currently it just reports some stats to let you know your data was loaded successfully." + CaveConverter.newline +
"This visualisation will improve with later releases to enable you to view your cave model" + CaveConverter.newline +
"more effectively." + CaveConverter.newline +
"-----" + CaveConverter.newline + CaveConverter.newline );
taCaveLog.append( ReportCaveSurveyStats( caveModel ) );
}
/**
* Generates LRUD data for stations from splay legs in each series
*/
public void generateLRUDFromSplays() {
//Generate LRUD if model contains data
if ( ( caveModel != null ) && ( caveModel.isEmpty() == false ) ) {
caveModel.generateLRUDfromSplays();
taCaveLog.append( "LRUD data generated from splays in cave model." + CaveConverter.newline);
}
else {
taCaveLog.append( "No data in cave model, so no LRUD generation is possible." + CaveConverter.newline);
}
}
/**
* Generates new survey series from the names of the stations in the selected series
*/
public void generateSeriesFromFullPathStationNames() {
//Generate LRUD if model contains data
if ( ( caveModel != null ) && ( caveModel.isEmpty() == false ) ) {
//Get selected series
SurveySeries selectedSeries = getSelectedSeries();
//Process series
if ( SurveyProcessing.generateSeriesFromFullPathStationNames(selectedSeries) ) {;
//Reload model
CaveSurvey model = caveModel;
setCaveModel( model );
}
else {
taCaveLog.append( "No changes were made to the series." + CaveConverter.newline);
}
}
else {
taCaveLog.append( "No data in cave model, so no processing is possible." + CaveConverter.newline);
}
}
/**
* Returns data file content for a specified survey format for the current cave model
* @param outputFormat The format to export
* @return Data file content for the model in the specified format
*/
public List exportModel( CaveConverter.SurveyDataOutputFormats outputFormat ) {
List outputData = null;
if ( outputFormat == SurveyDataOutputFormats.Survex ) {
//Set options flag for splays (default to true)
SurvexWriter.SplayFormats outputSplays = SurvexWriter.SplayFormats.Flagged;
//Generate Survex format data
SurvexWriter writer = new SurvexWriter(logger);
outputData = writer.generateSurvexData( caveModel, outputSplays );
}
else if ( outputFormat == SurveyDataOutputFormats.Toporobot ) {
//Set options flag for splays (default to false)
boolean outputSplays = false;
//Set date to todays datetime
Calendar cal = Calendar.getInstance(TimeZone.getDefault());
Date today = cal.getTime();
//Generate Toporobot format data
TopoRobotWriter writer = new TopoRobotWriter(logger);
outputData = writer.generateToporobotData( caveModel, today, outputSplays );
}
return outputData;
}
/**
* Generates a summary report for the survey
* @param survey
* @return Report text
*/
private String ReportCaveSurveyStats(CaveSurvey survey) {
String report = "Cave Survey: ";
if ( survey == null ) {
report = "No Survey set!";
}
else {
report += survey.getSurveyName() + CaveConverter.newline;
if ( caveModel.size() == 0 ) {
report += "No data was loaded into the cave model!" + CaveConverter.newline;
}
else {
report += "Cave model contains " + caveModel.size() + " top level series." + CaveConverter.newline;
for (int i = 0; i < caveModel.size(); i++ ) {
report += ReportSeriesStats( caveModel.get(i) );
}
}
}
return report;
}
/**
* Generates a summary report for the survey series
* @param series
* @return Report text
*/
private String ReportSeriesStats(SurveySeries series) {
String report = "Series: " + series.getSeriesName() + CaveConverter.newline;
report += "Date: " + series.getSurveyDate() + CaveConverter.newline;
report += "Tape units: " + series.getLengthUnit() + CaveConverter.newline;
report += "Declination: " + series.getDeclination() + CaveConverter.newline;
if ( series.getTapeCalibration(LengthUnit.Metres) != 0 ) {
report += "Tape calibration: " + series.getTapeCalibration( series.getLengthUnit() ) + CaveConverter.newline;
}
if ( series.getCompassCalibration(BearingUnit.Degrees) != 0 ) {
report += "Compass calibration: " + series.getCompassCalibration( series.getBearingUnit() ) + CaveConverter.newline;
}
if ( series.getClinoCalibration(GradientUnit.Degrees) != 0 ) {
report += "Clino calibration: " + series.getClinoCalibration( series.getGradientUnit() ) + CaveConverter.newline;
}
if ( series.legCount() > 0 ) {
report += "Contains " + series.legCount() + " legs. ";
}
if ( series.innerSeriesCount() > 0 ) {
report += "(Contains " + series.innerSeriesCount() + " child series).";
}
report += CaveConverter.newline;
//Recursively log details of inner series
for (int i = 0; i < series.innerSeriesCount(); i++ ) {
report += ReportSeriesStats( series.getInnerSeries(i) );
}
return report;
}
/**
* Generates a summary report for the survey leg
* @param leg
* @return Report text
*/
private String ReportLegStats(SurveyLeg leg) {
String report = leg.toString();
return report;
}
/**
* Gets the selected series in the tree (if multiple nodes are selected then it gets
* the first one which was selected)
* @return The selected series, or null if selected node is not a SurveySeries
*/
private SurveySeries getSelectedSeries() {
SurveySeries selectedSeries = null;
Object selectedNode = tree.getSelectionPath().getLastPathComponent();
if (selectedNode instanceof SurveySeries ) {
selectedSeries = (SurveySeries) selectedNode;
}
return selectedSeries;
}
}
CaveConverter_src/src/footleg/cavesurvey/gui/swing/MainForm.java 0000644 0000000 0000000 00000111502 13036425356 024110 0 ustar root root /**
* Copyright (C) 2015-2017 Paul Fretwell - aka 'Footleg' (drfootleg@gmail.com)
*
* This file is part of Cave Converter.
*
* Cave Converter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cave Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cave Converter. If not, see .
*/
package footleg.cavesurvey.gui.swing;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JToolBar;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.plaf.metal.DefaultMetalTheme;
import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.plaf.metal.OceanTheme;
import footleg.cavesurvey.converter.CaveConverter;
import footleg.cavesurvey.converter.CaveConverter.SurveyDataOutputFormats;
import footleg.cavesurvey.converter.DialogMsgLogger;
import footleg.cavesurvey.data.model.CaveSurvey;
import footleg.cavesurvey.data.reader.CompassParser;
import footleg.cavesurvey.data.reader.DxfParser;
import footleg.cavesurvey.data.reader.PocketTopoParser;
import footleg.cavesurvey.data.reader.SurvexParser;
/**
* GUI application built on top of CaveConverter library.
*
* @author Footleg
* @version 2017.01.10 (ISO 8601 YYYY.MM.DD)
* @since 1.7 (The Java version used)
*
* @to.do
* TODO Add option to output anonymous splay stations
* TODO Add selection of file character set encoding to preferences
* TODO In case of unknown file format on reading, ask user to select the survey format of the file
* TODO Prevent window position from preferences being beyond screen size when screen resolution is changed
* TODO Add selection of font in preferences
* TODO Add selection of look and feel in preferences
*/
@SuppressWarnings("serial")
public class MainForm extends JFrame {
/**
* Private class to manage loading, saving and to hold user preferences
* @author Footleg
*/
private class AppPrefs {
private static final String HEIGHT_PROPERTY = "window.height";
private static final String WIDTH_PROPERTY = "window.width";
private static final String SCREENPOSX_PROPERTY = "window.posx";
private static final String SCREENPOSY_PROPERTY = "window.posy";
private static final String FONT_PROPERTY = "font.typeface";
private static final String FONTSIZE_PROPERTY = "font.size";
private static final String CHARSET_PROPERTY = "character.set";
private static final String LOOKANDFEEL = "application.skin";
private Point position;
private Dimension size;
private String charset;
private String fontTypeface;
private int fontSize;
private String lookAndFeel;
/**
* Initialise preferences data to defaults and attempt to load user preferences from properties file
*/
public AppPrefs() {
//Set defaults in case app preferences cannot be read from file
position = new Point(-1,-1);
size = new Dimension(600,400);
charset = "UTF8";
fontTypeface = "courier";
fontSize = 12;
lookAndFeel = "System";
//Read user preferences from file (if properties file exists)
try {
File file = new File(".");
URL[] urls = {file.toURI().toURL()};
ClassLoader loader = new URLClassLoader(urls);
ResourceBundle userPrefs = ResourceBundle.getBundle("app", Locale.getDefault(), loader);
//Set prefs from properties read from file
position.setLocation( Integer.parseInt( userPrefs.getString( SCREENPOSX_PROPERTY ) ),
Integer.parseInt( userPrefs.getString( SCREENPOSY_PROPERTY ) ));
size.setSize( Integer.parseInt( userPrefs.getString( WIDTH_PROPERTY ) ),
Integer.parseInt( userPrefs.getString( HEIGHT_PROPERTY ) ));
charset = userPrefs.getString( CHARSET_PROPERTY );
fontTypeface = userPrefs.getString( FONT_PROPERTY );
fontSize = Integer.valueOf( userPrefs.getString( FONTSIZE_PROPERTY ) );
lookAndFeel = userPrefs.getString( LOOKANDFEEL );
}
catch (Exception e) {
//Ignore errors in reading prefs
// JOptionPane.showMessageDialog(null, "Error reading prefs: " + e.getMessage(),
// "Error", JOptionPane.ERROR_MESSAGE);
}
}
public Point getPosition() {
return position;
}
public void setPosition(Point position) {
this.position = position;
}
public Dimension getSize() {
return size;
}
public void setSize(int width, int height) {
size.setSize(width, height);
}
public String getCharset() {
return charset;
}
public void setCharset(String charset) {
this.charset = charset;
}
public String getFontTypeface() {
return fontTypeface;
}
public void setFontTypeface(String fontTypeface) {
this.fontTypeface = fontTypeface;
}
public int getFontSize() {
return fontSize;
}
public void setFontSize(int fontSize) {
this.fontSize = fontSize;
}
public String getLookAndFeel() {
return lookAndFeel;
}
public void setLookAndFeel(String lookAndFeel) {
this.lookAndFeel = lookAndFeel;
}
/**
* Write application preference data to a properties file
*/
public void writePrefsToFile() {
//Create prefs file text
String prefsData = "";
prefsData += SCREENPOSX_PROPERTY + "=" + (int)position.getX() + CaveConverter.newline;
prefsData += SCREENPOSY_PROPERTY + "=" + (int)position.getY() + CaveConverter.newline;
prefsData += HEIGHT_PROPERTY + "=" + (int)size.getHeight() + CaveConverter.newline;
prefsData += WIDTH_PROPERTY + "=" + (int)size.getWidth() + CaveConverter.newline;
prefsData += CHARSET_PROPERTY + "=" + getCharset() + CaveConverter.newline;
prefsData += FONT_PROPERTY + "=" + getFontTypeface() + CaveConverter.newline;
prefsData += FONTSIZE_PROPERTY + "=" + getFontSize() + CaveConverter.newline;
prefsData += LOOKANDFEEL + "=" + getLookAndFeel() + CaveConverter.newline;
//Save File
FileWriter textWriter = null;
try {
textWriter = new FileWriter("./app.properties");
textWriter.write( prefsData );
}
catch (Exception e) {
JOptionPane.showMessageDialog(null, "Error writing user preferences file: " + e.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
}
finally {
if ( textWriter != null ) {
try {
textWriter.close();
}
catch (IOException ex) {
JOptionPane.showMessageDialog(null, "Error closing user preferences file: " + ex.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
}
}
}
}
}
/**
* Private action class to handle file open operations
* @author Footleg
*/
private class OpenFileAction extends AbstractAction {
private JFileChooser fileOpenDialog;
public OpenFileAction(String name, Icon icon) {
super(name, icon);
//Initialise file open dialog
fileOpenDialog = new JFileChooser(".");
fileOpenDialog.setFileSelectionMode( JFileChooser.FILES_ONLY );
fileOpenDialog.addChoosableFileFilter( new FileNameExtensionFilter("Compass Data Files","dat") );
fileOpenDialog.addChoosableFileFilter( new FileNameExtensionFilter("DXF Files","dxf") );
fileOpenDialog.addChoosableFileFilter( new FileNameExtensionFilter("PocketTopo Text Files","txt") );
fileOpenDialog.addChoosableFileFilter( new FileNameExtensionFilter("Survex Files","svx") );
}
@Override
public void actionPerformed(ActionEvent evnt) {
//Show file open dialog for user to pick input file
fileOpenDialog.showOpenDialog( getMainFrame() );
if (fileOpenDialog.getSelectedFile() != null) {
//Check if this is the first data file to be opened
if (inputData.getFormat() == null ) {
//Add tab to tab pane
inputData.title = "Survey Input Data";
tabPane.addTab ( inputData.title, inputData.getTabPaneComponent() );
}
//Open file in inputData tab
boolean res = inputData.openFile( fileOpenDialog.getSelectedFile(), appPrefs.getCharset() );
//If data was loaded successfully
if ( res ) {
//Enable actions which can act on input data
saveFileAction.setEnabled( true );
saveAsFileAction.setEnabled( true );
parseInputDataAction.setEnabled( true );
//Automatically process the data
buildCaveModelFromInputData();
}
}
}
}
/**
* Private action class to handle file save operations
* @author Footleg
*/
private class SaveFileAction extends AbstractAction {
public SaveFileAction(String name, Icon icon) {
super(name, icon);
//Disabled by default (enables when data is loaded)
enabled = false;
}
@Override
public void actionPerformed(ActionEvent evnt) {
//Get selected tab
SurveyDatafileTabPane fileContentTab = getActiveSurveyDataTabPane();
if ( fileContentTab != null ) {
boolean saveAs = false;
//Determine action to perform
if ( evnt.getActionCommand().equals( saveAsActionCmd ) ) {
saveAs = true;
}
//Show file save dialog for user to specify file path
fileContentTab.saveData( saveAs, appPrefs.getCharset() );
}
else {
JOptionPane.showMessageDialog( getMainFrame(), "No data to save. Select a survey data file tab before selecting save.",
"Error", JOptionPane.ERROR_MESSAGE);
}
}
}
/**
* Private action class to read input survey data to generate a cave model in the visualiser
* @author Footleg
*/
private class ParseInputDataAction extends AbstractAction {
public ParseInputDataAction(String name, Icon icon) {
super(name, icon);
//Disabled by default (enables when data is loaded)
enabled = false;
}
@Override
public void actionPerformed(ActionEvent evnt) {
//Process the data in the input data tab and generate a model
buildCaveModelFromInputData();
}
}
/**
* Private action class to generate LRUD data in cave model from splays
* @author Footleg
*/
private class GenerateLRUDAction extends AbstractAction {
public GenerateLRUDAction(String name, Icon icon) {
super(name, icon);
//Disabled by default (enables when data is loaded)
enabled = false;
}
@Override
public void actionPerformed(ActionEvent evnt) {
//Process the data in the input data tab and generate a model
if ( visualiser != null ) {
visualiser.generateLRUDFromSplays();
}
}
}
/**
* Private action class to generate new nested series in cave model from stations named using full paths
* @author Footleg
*/
private class GenerateSeriesFromStnNamesAction extends AbstractAction {
public GenerateSeriesFromStnNamesAction(String name, Icon icon) {
super(name, icon);
//Disabled by default (enables when data is loaded)
enabled = false;
}
@Override
public void actionPerformed(ActionEvent evnt) {
//Process selected series
if ( visualiser != null ) {
visualiser.generateSeriesFromFullPathStationNames();
}
}
}
/**
* Private action class to handle Survex export operation
* @author Footleg
*/
private class ExportSurvexDataAction extends AbstractAction {
public ExportSurvexDataAction(String name, Icon icon) {
super(name, icon);
//Disabled by default (enables when data is loaded)
enabled = false;
}
@Override
public void actionPerformed(ActionEvent evnt) {
//Process the data in the input data tab and generate a model
generateOutputData( SurveyDataOutputFormats.Survex );
}
}
/**
* Private action class to handle Toporobot export operation
* @author Footleg
*/
private class ExportToporobotDataAction extends AbstractAction {
public ExportToporobotDataAction(String name, Icon icon) {
super(name, icon);
//Disabled by default (enables when data is loaded)
enabled = false;
}
@Override
public void actionPerformed(ActionEvent evnt) {
//Process the data in the input data tab and generate a model
generateOutputData( SurveyDataOutputFormats.Toporobot );
}
}
// /**
// * Private action class to handle operations
// * @author Footleg
// */
// private class NewAction extends AbstractAction {
// public NewAction(String name, Icon icon) {
// super(name, icon);
// //Disabled by default (enables when data is loaded)
// enabled = false;
// }
//
// @Override
// public void actionPerformed(ActionEvent evnt) {
//
// }
// }
/**
* Private listener for SurveyDatafileTabPane modified events to indicate modified state in tab title
*/
private PropertyChangeListener dataModified = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
SurveyDatafileTabPane source = (SurveyDatafileTabPane)evt.getSource();
//Indicate modified state in tab title
int idx = getTabIndexForObject(source);
if ( idx >= 0 ) {
if ( (Boolean)evt.getNewValue() ) {
tabPane.setTitleAt( idx, source.title + "(*)" );
}
else {
tabPane.setTitleAt( idx, source.title );
}
}
}
};
//Themes
private static final String[][] THEMES = {
{"Metal","javax.swing.plaf.metal.MetalLookAndFeel"},
{"Ocean","javax.swing.plaf.metal.MetalLookAndFeel"},
{"Motif","com.sun.java.swing.plaf.motif.MotifLookAndFeel"},
{"System", UIManager.getSystemLookAndFeelClassName()},
{"CrossPlafrom", UIManager.getCrossPlatformLookAndFeelClassName() },
{"GTK","com.sun.java.swing.plaf.gtk.GTKLookAndFeel"} };
//Action name constants
private static final String saveActionCmd = "Save";
private static final String saveAsActionCmd = "SaveAs";
//UI Elements
private JTabbedPane tabPane;
private Font font;
private OpenFileAction openFileAction;
private SaveFileAction saveFileAction;
private SaveFileAction saveAsFileAction;
private ParseInputDataAction parseInputDataAction;
private GenerateLRUDAction generateLRUDAction;
private GenerateSeriesFromStnNamesAction genSeriesFromStnNamesAction;
private ExportSurvexDataAction exportSurvexDataAction;
private ExportToporobotDataAction exportToporobotDataAction;
//Data Objects
private AppPrefs appPrefs;
private SourceDataComponent inputData;
private CaveModelVisualiser visualiser;
private DialogMsgLogger dialogLogger;
/**
* Create main application window
*/
public MainForm() {
//Create application preferences manager first, as initialisation of UI depends on preference values
appPrefs = new AppPrefs();
//Set look and feel
initLookAndFeel( appPrefs.getLookAndFeel() );
//Create dialog logger
dialogLogger = new DialogMsgLogger(this);
//Set up properties from prefs
font = new Font( appPrefs.getFontTypeface(), Font.PLAIN, appPrefs.getFontSize() );
//Create action class instances here after look and feel is configured, or UI elements (JFileChoosers)
//do not pick up look and feel
openFileAction = new OpenFileAction("Open", createImageIcon("images/fileopen.png") );
saveFileAction = new SaveFileAction("Save", createImageIcon("images/filesave.png") );
saveAsFileAction = new SaveFileAction("Save As", createImageIcon("images/filesave.png") );
parseInputDataAction = new ParseInputDataAction("Build Cave Model", createImageIcon("images/process_data.png") );
generateLRUDAction = new GenerateLRUDAction("Generate LRUD", createImageIcon("images/lrud.png") );
genSeriesFromStnNamesAction = new GenerateSeriesFromStnNamesAction("Generate Series From Station Names", createImageIcon("images/expand-series.png") );
exportSurvexDataAction = new ExportSurvexDataAction("Export to Survex", createImageIcon("images/export_survex_data.png") );
exportToporobotDataAction = new ExportToporobotDataAction("Export to Toporobot", createImageIcon("images/export_toporobot_data.png") );
//Create application UI
initializeUI();
}
/**
* Getter for the main frame reference, for use in sub classes where 'this' cannot be used
* @return Returns the main frame reference from any within any sub class context
*/
public MainForm getMainFrame() {
return this;
}
public DialogMsgLogger getLogger() {
return dialogLogger;
}
/**
* Getter for selected survey data tab if one is active.
* @return The active tab pane if it is a Survey data file tab, otherwise returns null
*/
public SurveyDatafileTabPane getActiveSurveyDataTabPane() {
//Get selected tab
SurveyDatafileTabPane fileContentTab = null;
if ( tabPane.getTabCount() > 0 ) {
try {
JScrollPane selectedTab = (JScrollPane) tabPane.getComponentAt( tabPane.getSelectedIndex() );
fileContentTab = (SurveyDatafileTabPane) selectedTab.getViewport().getView();
}
catch (ClassCastException e) {
fileContentTab = null;
}
}
return fileContentTab;
}
/**
* Determines the index in the tabPane of the tab holding the specified object
* @param matchObj Object to match in the tabs
* @return The index of the tab pane matching the object, or -1 if there is no matching tab
*/
public int getTabIndexForObject( SurveyDatafileTabPane matchObj ) {
int idx = -1;
for (int i = 0; i < tabPane.getTabCount(); i++) {
if ( tabPane.getComponentAt(i) == matchObj.getTabPaneComponent() ) {
idx = i;
break;
}
}
return idx;
}
/**
* Create UI elements
*/
private void initializeUI() {
//Pick up version number and display in title bar
ResourceBundle resourceString = ResourceBundle.getBundle("footleg.cavesurvey.gui.app");
String version = resourceString.getString("build.version");
setTitle( "Footleg's Cave Converter - " + version );
//Set window size and position from preferences
setSize( appPrefs.getSize() );
if (appPrefs.getPosition().getX() < 0) {
setLocationRelativeTo(null);
}
else {
setLocation( appPrefs.getPosition() );
}
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
addWindowListener(
new WindowAdapter(){
@Override
public void windowClosing(WindowEvent windowEvent) {
boolean close = true;
//Prompt to save changes for any modified tab panes
for (int i = 0; i < tabPane.getTabCount(); i++) {
if ( tabPane.getComponentAt(i) instanceof JScrollPane ) {
JScrollPane pane = (JScrollPane) tabPane.getComponentAt(i);
if ( pane.getViewport().getView() instanceof SurveyDatafileTabPane ) {
//Make this pane the selected one
tabPane.setSelectedComponent( pane );
//Call method to prompt to save changes if modified
SurveyDatafileTabPane filePane = (SurveyDatafileTabPane) pane.getViewport().getView();
int res = filePane.promptToSaveChanges( appPrefs.getCharset() );
if ( res == JFileChooser.CANCEL_OPTION ) {
//User cancelled, so abort closing of application
close = false;
break;
}
}
}
}
if ( close ) {
//Update preferences with window size and position
Point pos = windowEvent.getWindow().getLocationOnScreen();
appPrefs.setSize( windowEvent.getWindow().getWidth(), windowEvent.getWindow().getHeight() );
appPrefs.setPosition( pos );
appPrefs.writePrefsToFile();
//Close window to exit application
dispose();
}
else {
//Ensure window is made visible
setVisible( true );
}
}
}
);
createMenuBar();
//Set up tab pane
tabPane = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);
add(tabPane,BorderLayout.CENTER);
tabPane.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
//Enable actions appropriate to selected tab type
if (e.getSource() instanceof JTabbedPane) {
JTabbedPane tp = (JTabbedPane) e.getSource();
int selIdx = tp.getSelectedIndex();
if ( tp.getComponentAt( selIdx ) instanceof JScrollPane) {
JScrollPane pane = (JScrollPane) tp.getComponentAt( selIdx );
if ( pane.getViewport().getView() instanceof SurveyDatafileTabPane ) {
saveFileAction.setEnabled( true );
saveAsFileAction.setEnabled( true );
}
else {
saveFileAction.setEnabled( false );
saveAsFileAction.setEnabled( false );
}
}
else if ( tp.getComponentAt( selIdx ) instanceof JSplitPane ) {
JSplitPane pane = (JSplitPane) tp.getComponentAt( selIdx );
if ( pane instanceof CaveModelVisualiser ) {
//Nothing to save directly from cave model pane
saveFileAction.setEnabled( false );
saveAsFileAction.setEnabled( false );
}
else {
//No other pane types yet, so disable save for when there are
saveFileAction.setEnabled( false );
saveAsFileAction.setEnabled( false );
}
}
}
}
});
//Create input data tab pane (but do not add to tab pane here, that is done when a file is opened
inputData = new SourceDataComponent( font, this.getLogger() );
//Add modified listener to tab pane
inputData.addPropertyChangeListener("modified", dataModified);
}
// JOptionPane.showMessageDialog(getMainFrame(), "Modified property changed to: " + inputData.isModified(),
// "Debug", JOptionPane.OK_OPTION);
/**
* Create the main form menus and toolbar
*/
private void createMenuBar() {
JMenuBar menubar = new JMenuBar();
JToolBar toolbar = new JToolBar();
//File Menu
JMenu fileMenu = new JMenu("File");
fileMenu.setMnemonic(KeyEvent.VK_F);
menubar.add( fileMenu );
//Define Open item
createMenuItemAndToolbarBtn( fileMenu, toolbar, openFileAction, KeyEvent.VK_O, "Open file", "" );
//Define Save item
createMenuItemAndToolbarBtn( fileMenu, toolbar, saveFileAction, KeyEvent.VK_S, "Save file", saveActionCmd );
saveFileAction.setEnabled( false );
//Define Save As item
createMenuItem( fileMenu, saveAsFileAction, KeyEvent.VK_A, "Save file with a new name", saveAsActionCmd );
//Define Exit item
JMenuItem eMenuItem = new JMenuItem("Exit", createImageIcon("images/exit.png") );
eMenuItem.setMnemonic(KeyEvent.VK_X);
eMenuItem.setToolTipText("Exit application");
eMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
//Trigger closing of application
closeApp();
}
});
//Add to File menu
fileMenu.add(eMenuItem);
//Data Menu
JMenu dataMenu = new JMenu("Data");
dataMenu.setMnemonic(KeyEvent.VK_D);
menubar.add( dataMenu );
//Define Process Data item
createMenuItemAndToolbarBtn( dataMenu, toolbar, parseInputDataAction, KeyEvent.VK_P, "Process Survey Data", "" );
//Define LRUD item
createMenuItemAndToolbarBtn( dataMenu, toolbar, generateLRUDAction, KeyEvent.VK_L, "Generate LRUD data from splays", "" );
//Define Generate Series from Stn Names item
createMenuItemAndToolbarBtn( dataMenu, toolbar, genSeriesFromStnNamesAction, KeyEvent.VK_G, "Generate new series using station names", "" );
//Define ConvertToSurvex item
createMenuItemAndToolbarBtn( dataMenu, toolbar, exportSurvexDataAction, KeyEvent.VK_U, "Export data model to Survex format", "" );
//Define ConvertToToporobot item
createMenuItemAndToolbarBtn( dataMenu, toolbar, exportToporobotDataAction, KeyEvent.VK_T, "Export data model to Toporobot format", "" );
//Add menu bar and toolbar to frame
setJMenuBar( menubar );
add( toolbar, BorderLayout.NORTH );
}
/**
* Sets the look and feel from one of the supported styles
* @param lookAndFeel
* @param theme
*/
private static void initLookAndFeel( String lookAndFeel ) {
String themeClass = "";
//Look up this LookAndFeel name in the themes array to get class name and theme
for (int i = 0; i < THEMES.length; i++) {
if ( lookAndFeel.compareTo( THEMES[i][0]) == 0) {
//Use this look and feel item
themeClass = THEMES[i][1];
break;
}
}
//If look and feel class name was not found then use system default
if ( themeClass.equals("") ) {
themeClass = UIManager.getSystemLookAndFeelClassName();
}
//Set look and feel ;
try {
//Set the theme for the themed Default Metal look and feels
if ( lookAndFeel.equals( "Metal" ) ) {
MetalLookAndFeel.setCurrentTheme(new DefaultMetalTheme());
}
else if ( lookAndFeel.equals( "Ocean" ) ) {
MetalLookAndFeel.setCurrentTheme(new OceanTheme());
}
UIManager.setLookAndFeel( themeClass );
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
}
catch (ClassNotFoundException e) {
System.err.println("Couldn't find class for specified look and feel:"
+ lookAndFeel);
System.err.println("Did you include the L&F library in the class path?");
System.err.println("Using the default look and feel.");
}
catch (UnsupportedLookAndFeelException e) {
System.err.println("Can't use the specified look and feel ("
+ lookAndFeel
+ ") on this platform.");
System.err.println("Using the default look and feel.");
}
catch (Exception e) {
System.err.println("Couldn't get specified look and feel ("
+ lookAndFeel
+ "), for some reason.");
System.err.println("Using the default look and feel.");
e.printStackTrace();
}
}
/**
* Helper method to add menu item and toolbar button for an action to the UI
* @param parentMenu Menu to create the new menu item under
* @param toolbar Toolbar to create the new button on
* @param action Action to associate with new menu item and toolbar button
* @param shortcutKey Keyboard shortcut to associate with the menu
* @param toolTipText Tooltop text to associate with the new menu item and toolbar button
*/
private void createMenuItemAndToolbarBtn( JMenu parentMenu, JToolBar toolbar, Action action, int shortcutKey,
String toolTipText, String actionCommand ) {
createMenuItem ( parentMenu, action, shortcutKey, toolTipText, actionCommand );
JButton btn = toolbar.add( action );
btn.setToolTipText( toolTipText );
btn.setActionCommand( actionCommand );
}
/**
* Helper method to add menu item for an action to the UI
* @param parentMenu Menu to create the new menu item under
* @param action Action to associate with new menu item
* @param shortcutKey Keyboard shortcut to associate with the menu
* @param toolTipText Tooltop text to associate with the new menu item
*/
private void createMenuItem( JMenu parentMenu, Action action, int shortcutKey, String toolTipText, String actionCommand ) {
JMenuItem menuItem = parentMenu.add ( action );
menuItem.setMnemonic( shortcutKey);
menuItem.setToolTipText( toolTipText );
menuItem.setActionCommand( actionCommand );
}
/**
* Close application in a controlled manner, allowing preferences and data to be saved
*/
private void closeApp() {
this.dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
}
/**
* Generate survey data from the cave data model in the specified output format
*/
private void generateOutputData( CaveConverter.SurveyDataOutputFormats format) {
if ( visualiser != null ) {
List outputData = visualiser.exportModel( format );
//Create tabPane for this output data
SurveyDatafileTabPane exportTab = new ExportDataComponent( font, this.getLogger(), format );
if ( format == SurveyDataOutputFormats.Survex ) {
exportTab.title = "Survex Export Data";
}
else if ( format == SurveyDataOutputFormats.Toporobot ) {
exportTab.title = "Toporobot Export Data";
}
tabPane.addTab ( exportTab.title, exportTab.getTabPaneComponent() );
exportTab.addPropertyChangeListener("modified", dataModified);
//Write strings from list to the file
exportTab.setText("");
Iterator iterator = outputData.listIterator();
while ( iterator.hasNext() ) {
String line = iterator.next();
if ( iterator.hasNext() ) {
exportTab.append( line + System.getProperty("line.separator") );
}
else {
exportTab.append( line );
}
}
//Bring to front
tabPane.setSelectedComponent( exportTab.getTabPaneComponent() );
}
}
/**
* Process the data in the input data tab and generate a model
*/
private void buildCaveModelFromInputData() {
CaveSurvey dataModel = processInputData();
//Create or update model tab if data was loaded
if ( ( dataModel != null ) && ( dataModel.isEmpty() == false ) ) {
//Reuse tab if already exists
if ( visualiser == null ) {
visualiser = new CaveModelVisualiser(this, dataModel, font );
tabPane.addTab ( "Survey Data Model", visualiser.getTabPaneComponent() );
}
else {
//Update cave model in visualiser
visualiser.setCaveModel( dataModel );
}
//Bring to front
tabPane.setSelectedComponent( visualiser.getTabPaneComponent() );
//Enable actions which can act on cave model
generateLRUDAction.setEnabled( true );
exportSurvexDataAction.setEnabled( true );
exportToporobotDataAction.setEnabled( true );
}
}
/**
* Sets the enabled state of all actions which require a survey series
* @param enabled State to set the enabled status to
*/
public void setSurveySeriesActionsEnabled( boolean enabled ) {
genSeriesFromStnNamesAction.setEnabled( enabled );
}
/**
* Parse the cave survey data in an input data tab into a cave data model
* @return Cave data model generated from parsed data file
*/
private CaveSurvey processInputData() {
CaveSurvey surveyData = null;
//Get text data from input tab
List surveyDataLines = getSurveyDataFromText( inputData.getText() );
//Determine data type from input tab
if ( inputData.getFormat() == null ) {
//TODO In case of unknown format, ask user to select the format
JOptionPane.showMessageDialog(this, "Format of data unknown. Currently this is determined by the file extension. The option to let you specify the format is not written yet.",
"Unknown Survey Data Format", JOptionPane.OK_OPTION);
}
if ( inputData.getFormat() != null ) {
//Parse the data into the cave data model
switch ( inputData.getFormat() ) {
case Compass:
//Parse Compass data
CompassParser cParser = new CompassParser(dialogLogger);
try {
surveyData = cParser.parseFile( surveyDataLines );
} catch (ParseException e) {
//Display error message
JOptionPane.showMessageDialog(this, "Error processing Compass file data: " + e.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
} catch (Exception e) {
JOptionPane.showMessageDialog(this, "Unexpected error processing Compass file data: " + e.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
e.printStackTrace();
}
break;
case DXF:
//Parse DXF data
DxfParser dParser = new DxfParser(dialogLogger);
try {
surveyData = dParser.parseFile( surveyDataLines, 0 );
} catch (Exception e) {
JOptionPane.showMessageDialog(this, "Unexpected error processing DXF file data: " + e.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
e.printStackTrace();
}
break;
case PocketTopo:
//Parse PocketTopo data
PocketTopoParser pParser = new PocketTopoParser(dialogLogger);
try {
surveyData = pParser.parseFile( surveyDataLines );
//Generate LRUD from splays by default for PocketTopo
// CaveConverter.logMessage("Generating LRUD data from splays...");
surveyData.generateLRUDfromSplays();
} catch (ParseException e) {
//Display error message
JOptionPane.showMessageDialog(this, "Error processing PocketTopo file data: " + e.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
} catch (Exception e) {
JOptionPane.showMessageDialog(this, "Unexpected error processing PocketTopo file data: " + e.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
e.printStackTrace();
}
break;
case Survex:
//Parse Survex data
SurvexParser sParser = new SurvexParser(dialogLogger);
try {
surveyData = sParser.parseFile( surveyDataLines, null );
} catch (ParseException e) {
//Display error message
JOptionPane.showMessageDialog(this, "Error processing Survex file data: " + e.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
} catch (Exception e) {
JOptionPane.showMessageDialog(this, "Unexpected error processing Survex file data: " + e.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
e.printStackTrace();
}
break;
}
}
return surveyData;
}
/**
* Returns an ImageIcon, or null if the path was invalid
* @param path Path to the icon image relative to this class
* @return ImageIcon using the image at the path specified
*/
protected static ImageIcon createImageIcon(String path) {
java.net.URL imgURL = MainForm.class.getResource(path);
if (imgURL != null) {
return new ImageIcon(imgURL);
} else {
System.err.println("Failed to find icon file: " + path);
return null;
}
}
/**
* @param args Command line arguments (ignored)
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
MainForm mainWindow = new MainForm();
mainWindow.setVisible(true);
}
});
}
private List getSurveyDataFromText(String text) {
List dataLines = new ArrayList();
String[] parts = text.split("\n");
for (int i = 0; i < parts.length; i++) {
dataLines.add( parts[i] );
}
return dataLines;
}
}
CaveConverter_src/src/footleg/cavesurvey/gui/swing/SurveyDatafileTabPane.java 0000644 0000000 0000000 00000020762 13036425356 026571 0 ustar root root /**
* Copyright (C) 2015-2017 Paul Fretwell - aka 'Footleg' (drfootleg@gmail.com)
*
* This file is part of Cave Converter.
*
* Cave Converter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cave Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cave Converter. If not, see .
*/
package footleg.cavesurvey.gui.swing;
import java.awt.Font;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import footleg.cavesurvey.converter.Logger;
/**
* Component to display and hold cave survey data.
*
* @author Footleg
* @version 2017.01.10 (ISO 8601 YYYY.MM.DD)
* @since 1.7 (The Java version used)
*
* @to.do
* TODO The tab should indicate it's role (as the source data) by colour or other visual indicator.
*/
@SuppressWarnings("serial")
public class SurveyDatafileTabPane extends JTextArea {
private JScrollPane scrollPane;
protected JFileChooser fileSaveDialog;
protected FileNameExtensionFilter compassFileFilter = new FileNameExtensionFilter("Compass Data Files","dat");
protected FileNameExtensionFilter dxfFileFilter = new FileNameExtensionFilter("DXF Files","dxf");
protected FileNameExtensionFilter pocketTopoFileFilter = new FileNameExtensionFilter("PocketTopo Text Files","txt");
protected FileNameExtensionFilter survexFileFilter = new FileNameExtensionFilter("Survex Files","svx");
protected FileNameExtensionFilter toporobotFileFilter = new FileNameExtensionFilter("Toporobot Data Files","text");
private boolean modified;
protected String title = "";
protected File loadedFile;
protected Logger logger;
public boolean isModified() {
return modified;
}
protected void setModified(boolean modified) {
boolean oldModified = this.modified;
this.modified = modified;
firePropertyChange( "modified", oldModified, modified );
}
/**
* Create TextArea scrollable tab instance with specified font
* @param font The font to use to display survey data file contents
* @param logger Logging class to output information, warning and error messages to
*/
public SurveyDatafileTabPane(Font font, Logger logger) {
super();
this.setFont(font);
this.logger = logger;
modified = false;
scrollPane = new JScrollPane (this, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setViewportView(this);
//Add listener for document change events
this.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void removeUpdate(DocumentEvent e) {
//Update modified state
setModified(true);
}
@Override
public void insertUpdate(DocumentEvent e) {
//Update modified state
setModified(true);
}
@Override
public void changedUpdate(DocumentEvent e) {
//Update modified state
setModified(true);
}
});
//Initialise file save dialog
fileSaveDialog = new JFileChooser(".");
fileSaveDialog.setFileSelectionMode( JFileChooser.FILES_ONLY );
fileSaveDialog.addChoosableFileFilter( compassFileFilter );
fileSaveDialog.addChoosableFileFilter( dxfFileFilter );
fileSaveDialog.addChoosableFileFilter( pocketTopoFileFilter );
fileSaveDialog.addChoosableFileFilter( survexFileFilter );
fileSaveDialog.addChoosableFileFilter( toporobotFileFilter );
}
/**
* @return the scrollPane
*/
public JScrollPane getTabPaneComponent() {
return scrollPane;
}
/**
* Prompts user to save changes if data has been modified. Returns a JFileChooser option value.
* @param characterSetEncoding The character set encoding to use for the file being written (e.g. UTF8, Cp1252 (for ANSI) )
* @return APPROVE_OPTION if saved, ERROR_OPTION if failed, CANCEL_OPTION if user aborted save
*/
public int promptToSaveChanges(String characterSetEncoding) {
int action = JOptionPane.NO_OPTION;
int saveRes = JFileChooser.APPROVE_OPTION;
//Check whether currently loaded data has been modified
if ( isModified() ) {
//Ask whether to save before loading new data
action = JOptionPane.showConfirmDialog( this.getParent(), "Save changes to survey data file " + title + "?",
"Save changes?", JOptionPane.YES_NO_CANCEL_OPTION);
if ( action == JOptionPane.YES_OPTION ) {
//Save file before proceeding
saveRes = saveData( false, characterSetEncoding );
}
else if ( action == JOptionPane.CANCEL_OPTION ) {
//Change result to Cancel if user cancelled save
saveRes = JFileChooser.CANCEL_OPTION;
}
}
return saveRes;
}
/**
* Save survey data to file. Returns JFileChooser Option responses (APPROVE_OPTION, ERROR_OPTION, CANCEL_OPTION)
* @param saveAs If true then show file chooser even if a filename is already set
* @param characterSetEncoding The character set encoding to use for the file being written (e.g. UTF8, Cp1252 (for ANSI) )
* @return Returns JFileChooser Option responses: APPROVE_OPTION if saved, ERROR_OPTION if failed, CANCEL_OPTION if user aborted save
*/
public int saveData( boolean saveAs, String characterSetEncoding ) {
int action = JFileChooser.APPROVE_OPTION;
File saveFile = loadedFile;
if ( saveAs || loadedFile == null ) {
//Set file chooser to current filename
fileSaveDialog.setSelectedFile( loadedFile );
//Enable new filename to be specified
boolean overwriteCheckDone = false;
while ( overwriteCheckDone == false ) {
//Show file save dialog for user to specify file path
action = fileSaveDialog.showSaveDialog( this );
if ( action == JFileChooser.APPROVE_OPTION ) {
//Check whether specified file already exists and warn of overwrite
int overwriteConfirm = JOptionPane.YES_OPTION;
if ( fileSaveDialog.getSelectedFile().exists() ) {
overwriteConfirm = JOptionPane.showConfirmDialog( this.getParent(),
"File " + fileSaveDialog.getSelectedFile().getPath() + " already exists. Do you want to replace this file?",
"Overwrite file?", JOptionPane.YES_NO_OPTION);
}
if ( overwriteConfirm == JOptionPane.YES_OPTION ) {
saveFile = fileSaveDialog.getSelectedFile();
overwriteCheckDone = true;
}
}
else {
overwriteCheckDone = true;
}
}
}
if ( action == JFileChooser.APPROVE_OPTION ) {
//Save File
BufferedWriter textWriter = null;
try {
FileOutputStream fos = new FileOutputStream( saveFile );
Writer out = new OutputStreamWriter(fos, characterSetEncoding);
textWriter = new BufferedWriter(out);
textWriter.write( this.getText() );
//Update file reference for new saved file
loadedFile = saveFile;
//Reset modified status
setModified( false );
}
catch (Exception e) {
JOptionPane.showMessageDialog( this, "Error writing file: " + e.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
action = JFileChooser.ERROR_OPTION;
}
finally {
if ( textWriter != null ) {
try {
textWriter.close();
}
catch (IOException ex) {
JOptionPane.showMessageDialog( this, "Error closing file.",
"Error", JOptionPane.ERROR_MESSAGE);
action = JFileChooser.ERROR_OPTION;
}
}
}
}
return action;
}
}
CaveConverter_src/src/footleg/cavesurvey/gui/swing/SourceDataComponent.java 0000644 0000000 0000000 00000010732 13036425356 026320 0 ustar root root /**
* Copyright (C) 2015-2017 Paul Fretwell - aka 'Footleg' (drfootleg@gmail.com)
*
* This file is part of Cave Converter.
*
* Cave Converter is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cave Converter is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cave Converter. If not, see .
*/
package footleg.cavesurvey.gui.swing;
import java.awt.Font;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import javax.swing.JFileChooser;
import footleg.cavesurvey.converter.CaveConverter;
import footleg.cavesurvey.converter.Logger;
import footleg.cavesurvey.converter.CaveConverter.SurveyDataInputFormats;
import footleg.cavesurvey.tools.UtilityFunctions;
/**
* Component to display and hold cave survey source data for conversions and editing.
*
* @author Footleg
* @version 2017.01.10 (ISO 8601 YYYY.MM.DD)
* @since 1.7 (The Java version used)
*
* @to.do
* TODO Add highlighting of lines in file which parser could not interpret
*/
@SuppressWarnings("serial")
public class SourceDataComponent extends SurveyDatafileTabPane {
private CaveConverter.SurveyDataInputFormats format;
private List multifileLineRefs;
/**
* Create TextArea scrollable tab instance with specified font
* @param font The font to use to display survey data file contents
* @param logger Logging class to output information, warning and error messages to
*/
public SourceDataComponent(Font font, Logger logger) {
super( font, logger );
}
/**
* @return the format
*/
public CaveConverter.SurveyDataInputFormats getFormat() {
return format;
}
/**
* Opens a new survey data input file and loads the contents into a source data tab pane
* @param dataFile The file to open
* @param characterSetEncoding The character set encoding to use for the file being read (e.g. UTF8, Cp1252 (for ANSI) )
* @return Returns true if file was opened successfully
*/
public boolean openFile(File dataFile, String characterSetEncoding ) {
boolean res = false;
//Check whether currently loaded data has been modified
int action = promptToSaveChanges( characterSetEncoding );
//Load new data if prompt check passed
if ( action == JFileChooser.APPROVE_OPTION ) {
//Reset fields before attempting to load new data
setText("");
format = null;
//Determine file type from file extension
boolean multiFile = false;
String fileName = dataFile.getName();
int extnStart = fileName.lastIndexOf('.') + 1;
String fileExtn = "";
if ( extnStart > 0 ) {
fileExtn = fileName.substring( extnStart );
}
format = CaveConverter.inputFormatFromFileExtn( fileExtn );
//Set file filter appropriate to file format (this instance of the file
//save dialog is specific to this class instance, so this can be set here)
if ( format == SurveyDataInputFormats.Compass ) {
fileSaveDialog.setFileFilter( compassFileFilter );
}
else if ( format == SurveyDataInputFormats.DXF ) {
fileSaveDialog.setFileFilter( dxfFileFilter );
}
else if ( format == SurveyDataInputFormats.PocketTopo ) {
fileSaveDialog.setFileFilter( pocketTopoFileFilter );
}
else if ( format == SurveyDataInputFormats.Survex ) {
fileSaveDialog.setFileFilter( survexFileFilter );
multiFile = true;
multifileLineRefs = new ArrayList();
}
//Read data from file
List fileData =
UtilityFunctions.readTextFile(dataFile, characterSetEncoding, multiFile, multifileLineRefs, logger);
//Put data into tab
ListIterator dataIter = fileData.listIterator();
while ( dataIter.hasNext() ) {
append(dataIter.next() + "\n");
}
//Store filename in class property
loadedFile = dataFile;
//Reset modified flag now file loading is complete
setModified( false );
res = true;
}
return res;
}
}
CaveConverter_src/src/footleg/cavesurvey/gui/swing/images/ 0000755 0000000 0000000 00000000000 13036467352 023004 5 ustar root root CaveConverter_src/src/footleg/cavesurvey/gui/swing/images/export_toporobot_data.png 0000644 0000000 0000000 00000001635 12621447114 030131 0 ustar root root PNG
IHDR a ,tEXtCreation Time Thu 20 Aug 2015 11:50:31 -0000џ$ tIME
5
!- pHYs
B4 gAMA a IDATxmS]HTAfj{vS-6MC2L Hꡠ"7P#-,0JZR+EPj]uwﶫwޙfJtw3yNFs?Q3Vj[E!ߑ9xo3(LÚyp:!a@"[9dr۶*+=uLنQLj ͛6 9Gl:gjC
dVH! BX'~&+SԓfKk G(a̅3s"x]:[EJ2pU^jfNt[Li1Oح#H)"@{G{"+w.WC8dq)d(pί?oTS酢J!_zr1凢Rk!l}̇{9#>0Q'=1~$(+CÞ&v"[[+ן.LOeLʥ8ݷ0X^^^Fg a}br"I$
ko\=犨 IENDB` CaveConverter_src/src/footleg/cavesurvey/gui/swing/images/expand-series.png 0000644 0000000 0000000 00000007141 10513513600 026245 0 ustar root root PNG
IHDR a pHYs
OiCCPPhotoshop ICC profile xڝSgTS=BKKoR RB&*! J!QEEȠQ,
!{kּ>H3Q5B.@
$p d!s# ~<<+" x M0B\t8K @zB @F&S