]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/load/FieldGuesser.java
Version 13.4, May 2012
[GpsPrune.git] / tim / prune / load / FieldGuesser.java
1 package tim.prune.load;
2
3 import tim.prune.I18nManager;
4 import tim.prune.data.Field;
5 import tim.prune.data.Latitude;
6 import tim.prune.data.Longitude;
7 import tim.prune.data.Timestamp;
8
9 /**
10  * Class to try to match data with field names,
11  * using a variety of guessing techniques
12  */
13 public abstract class FieldGuesser
14 {
15         /**
16          * Try to guess whether the given line is a header line or data
17          * @param inValues array of values from first non-blank line of file
18          * @return true if it looks like a header row, false if it looks like data
19          */
20         private static boolean isHeaderRow(String[] inValues)
21         {
22                 // Loop over values looking for a Latitude value
23                 if (inValues != null)
24                 {
25                         for (int v=0; v<inValues.length; v++)
26                         {
27                                 Latitude lat = new Latitude(inValues[v]);
28                                 if (lat.isValid()) {return false;}
29                         }
30                 }
31                 // No valid Latitude value found so presume header
32                 return true;
33         }
34
35
36         /**
37          * Try to guess the fields for the given values from the file
38          * @param inValues array of values from first non-blank line of file
39          * @return array of fields which hopefully match
40          */
41         public static Field[] guessFields(String[] inValues)
42         {
43                 // Guess whether it's a header line or not
44                 boolean isHeader = isHeaderRow(inValues);
45                 // make array of Fields
46                 int numFields = inValues.length;
47                 Field[] fields = new Field[numFields];
48                 // Loop over fields to try to guess the main ones
49                 for (int f=0; f<numFields; f++)
50                 {
51                         if (inValues[f] != null) {
52                                 String value = inValues[f].trim();
53                                 // check for latitude
54                                 if (!checkArrayHasField(fields, Field.LATITUDE) && fieldLooksLikeLatitude(value, isHeader))
55                                 {
56                                         fields[f] = Field.LATITUDE;
57                                         continue;
58                                 }
59                                 // check for longitude
60                                 if (!checkArrayHasField(fields, Field.LONGITUDE) && fieldLooksLikeLongitude(value, isHeader))
61                                 {
62                                         fields[f] = Field.LONGITUDE;
63                                         continue;
64                                 }
65                                 // check for altitude
66                                 if (!checkArrayHasField(fields, Field.ALTITUDE) && fieldLooksLikeAltitude(value, isHeader))
67                                 {
68                                         fields[f] = Field.ALTITUDE;
69                                         continue;
70                                 }
71                                 // check for waypoint name
72                                 if (!checkArrayHasField(fields, Field.WAYPT_NAME) && fieldLooksLikeName(value, isHeader))
73                                 {
74                                         fields[f] = Field.WAYPT_NAME;
75                                         continue;
76                                 }
77                                 // check for timestamp
78                                 if (!checkArrayHasField(fields, Field.TIMESTAMP) && fieldLooksLikeTimestamp(value, isHeader))
79                                 {
80                                         fields[f] = Field.TIMESTAMP;
81                                         continue;
82                                 }
83                                 // check for tracksegment
84                                 if (!checkArrayHasField(fields, Field.NEW_SEGMENT) && fieldLooksLikeSegment(value, isHeader))
85                                 {
86                                         fields[f] = Field.NEW_SEGMENT;
87                                         continue;
88                                 }
89                                 // check for waypoint type
90                                 if (!checkArrayHasField(fields, Field.WAYPT_TYPE) && fieldLooksLikeWaypointType(value, isHeader))
91                                 {
92                                         fields[f] = Field.WAYPT_TYPE;
93                                         continue;
94                                 }
95                         }
96                 }
97                 // Fill in the rest of the fields using just custom fields
98                 // Could try to guess other fields (waypoint type, segment) or unguessed altitude, name, but keep simple for now
99                 String customPrefix = I18nManager.getText("fieldname.prefix") + " ";
100                 int customFieldNum = 0;
101                 for (int f=0; f<numFields; f++) {
102                         if (fields[f] == null)
103                         {
104                                 // Make sure lat and long are filled in if not already
105                                 if (!checkArrayHasField(fields, Field.LATITUDE)) {
106                                         fields[f] = Field.LATITUDE;
107                                 }
108                                 else if (!checkArrayHasField(fields, Field.LONGITUDE)) {
109                                         fields[f] = Field.LONGITUDE;
110                                 }
111                                 else {
112                                         customFieldNum++;
113                                         fields[f] = new Field(customPrefix + (customFieldNum));
114                                 }
115                         }
116                 }
117                 // Do a final check to make sure lat and long are in there
118                 if (!checkArrayHasField(fields, Field.LATITUDE)) {
119                         fields[0] = Field.LATITUDE;
120                 }
121                 else if (!checkArrayHasField(fields, Field.LONGITUDE)) {
122                         fields[1] = Field.LONGITUDE;
123                 }
124                 // Longitude _could_ have overwritten latitude in position 1
125                 if (!checkArrayHasField(fields, Field.LATITUDE)) {
126                         fields[0] = Field.LATITUDE;
127                 }
128                 return fields;
129         }
130
131
132         /**
133          * Check whether the given field array has the specified field
134          * @param inFields
135          * @param inCheckField
136          * @return true if Field is contained within the array
137          */
138         private static boolean checkArrayHasField(Field[] inFields, Field inCheckField)
139         {
140                 for (int f=0; f<inFields.length; f++)
141                 {
142                         if (inFields[f] != null && inFields[f].equals(inCheckField)) {
143                                 return true;
144                         }
145                 }
146                 // not found
147                 return false;
148         }
149
150
151         /**
152          * Check whether the given String looks like a Latitude value
153          * @param inValue value from file
154          * @param inIsHeader true if this is a header line, false for data
155          * @return true if it could be latitude
156          */
157         private static boolean fieldLooksLikeLatitude(String inValue, boolean inIsHeader)
158         {
159                 if (inValue == null || inValue.equals("")) {return false;}
160                 if (inIsHeader)
161                 {
162                         // This is a header line so look for english or local text
163                         String upperValue = inValue.toUpperCase();
164                         return (upperValue.equals("LATITUDE")
165                                 || upperValue.equals(I18nManager.getText("fieldname.latitude").toUpperCase()));
166                 }
167                 else
168                 {
169                         // Note this will also catch longitudes too
170                         Latitude lat = new Latitude(inValue);
171                         return lat.isValid();
172                 }
173         }
174
175         /**
176          * Check whether the given String looks like a Longitude value
177          * @param inValue value from file
178          * @param inIsHeader true if this is a header line, false for data
179          * @return true if it could be longitude
180          */
181         private static boolean fieldLooksLikeLongitude(String inValue, boolean inIsHeader)
182         {
183                 if (inValue == null || inValue.equals("")) {return false;}
184                 if (inIsHeader)
185                 {
186                         // This is a header line so look for english or local text
187                         String upperValue = inValue.toUpperCase();
188                         return (upperValue.equals("LONGITUDE")
189                                 || upperValue.equals(I18nManager.getText("fieldname.longitude").toUpperCase()));
190                 }
191                 else
192                 {
193                         // Note this will also catch latitudes too
194                         Longitude lon = new Longitude(inValue);
195                         return lon.isValid();
196                 }
197         }
198
199         /**
200          * Check whether the given String looks like an Altitude value
201          * @param inValue value from file
202          * @param inIsHeader true if this is a header line, false for data
203          * @return true if it could be altitude
204          */
205         private static boolean fieldLooksLikeAltitude(String inValue, boolean inIsHeader)
206         {
207                 if (inValue == null || inValue.equals("")) {return false;}
208                 if (inIsHeader)
209                 {
210                         // This is a header line so look for english or local text
211                         String upperValue = inValue.toUpperCase();
212                         return (upperValue.equals("ALTITUDE")
213                                 || upperValue.equals("ALT")
214                                 || upperValue.equals("HMSL") // height above mean sea level
215                                 || upperValue.equals(I18nManager.getText("fieldname.altitude").toUpperCase()));
216                 }
217                 else
218                 {
219                         // Look for a number less than 100000
220                         try
221                         {
222                                 int intValue = Integer.parseInt(inValue);
223                                 return (intValue > 0 && intValue < 100000);
224                         }
225                         catch (NumberFormatException nfe) {}
226                         return false;
227                 }
228         }
229
230
231         /**
232          * Check whether the given String looks like a waypoint name
233          * @param inValue value from file
234          * @param inIsHeader true if this is a header line, false for data
235          * @return true if it could be a name
236          */
237         private static boolean fieldLooksLikeName(String inValue, boolean inIsHeader)
238         {
239                 if (inValue == null || inValue.equals("")) {return false;}
240                 if (inIsHeader)
241                 {
242                         // This is a header line so look for english or local text
243                         String upperValue = inValue.toUpperCase();
244                         return (upperValue.equals("NAME")
245                                 || upperValue.equals("LABEL")
246                                 || upperValue.equals(I18nManager.getText("fieldname.waypointname").toUpperCase()));
247                 }
248                 else
249                 {
250                         // Look for at least two letters in it
251                         int numLetters = 0;
252                         for (int i=0; i<inValue.length(); i++)
253                         {
254                                 char currChar = inValue.charAt(i);
255                                 if (Character.isLetter(currChar)) {
256                                         numLetters++;
257                                 }
258                                 // Not interested if it contains ":" or "."
259                                 if (currChar == ':' || currChar == '.') {return false;}
260                         }
261                         return numLetters >= 2;
262                 }
263         }
264
265         /**
266          * Check whether the given String looks like a timestamp
267          * @param inValue value from file
268          * @param inIsHeader true if this is a header line, false for data
269          * @return true if it could be a timestamp
270          */
271         private static boolean fieldLooksLikeTimestamp(String inValue, boolean inIsHeader)
272         {
273                 if (inValue == null || inValue.equals("")) {return false;}
274                 if (inIsHeader)
275                 {
276                         String upperValue = inValue.toUpperCase();
277                         // This is a header line so look for english or local text
278                         return (upperValue.equals("TIMESTAMP")
279                                 || upperValue.equals("TIME")
280                                 || upperValue.equals(I18nManager.getText("fieldname.timestamp").toUpperCase()));
281                 }
282                 else
283                 {
284                         // must be at least 7 characters long
285                         if (inValue.length() < 7) {return false;}
286                         Timestamp stamp = new Timestamp(inValue);
287                         return stamp.isValid();
288                 }
289         }
290
291         /**
292          * Check whether the given String looks like a track segment field
293          * @param inValue value from file
294          * @param inIsHeader true if this is a header line, false for data
295          * @return true if it could be a track segment
296          */
297         private static boolean fieldLooksLikeSegment(String inValue, boolean inIsHeader)
298         {
299                 if (inValue == null || inValue.equals("")) {return false;}
300                 if (inIsHeader)
301                 {
302                         String upperValue = inValue.toUpperCase();
303                         // This is a header line so look for english or local text
304                         return upperValue.equals("SEGMENT")
305                                 || upperValue.equals(I18nManager.getText("fieldname.newsegment").toUpperCase());
306                 }
307                 else
308                 {
309                         // can't reliably identify it just using the value
310                         return false;
311                 }
312         }
313
314         /**
315          * Check whether the given String looks like a waypoint type
316          * @param inValue value from file
317          * @param inIsHeader true if this is a header line, false for data
318          * @return true if it could be a waypoint type
319          */
320         private static boolean fieldLooksLikeWaypointType(String inValue, boolean inIsHeader)
321         {
322                 if (inValue == null || inValue.equals("")) {return false;}
323                 if (inIsHeader)
324                 {
325                         String upperValue = inValue.toUpperCase();
326                         // This is a header line so look for english or local text
327                         return (upperValue.equals("TYPE")
328                                 || upperValue.equals(I18nManager.getText("fieldname.waypointtype").toUpperCase()));
329                 }
330                 else
331                 {
332                         // can't reliably identify it just using the value
333                         return false;
334                 }
335         }
336 }