Project

General

Profile

1 3035 perry
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2003 Regents of the University of California.
4
 *
5
 * Author: Matthew Perry
6
 * '$Date$'
7
 * '$Revision$'
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
 */
23
package edu.ucsb.nceas.metacat.spatial;
24
25
import java.io.File;
26
27 5015 daigle
import edu.ucsb.nceas.metacat.database.DBConnection;
28 5030 daigle
import edu.ucsb.nceas.metacat.properties.PropertyService;
29 4699 daigle
import edu.ucsb.nceas.metacat.util.MetacatUtil;
30 4080 daigle
import edu.ucsb.nceas.metacat.util.SystemUtil;
31
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
32 3035 perry
33
import com.vividsolutions.jts.geom.Coordinate;
34
import com.vividsolutions.jts.geom.Point;
35
import com.vividsolutions.jts.geom.Polygon;
36
import com.vividsolutions.jts.geom.MultiPolygon;
37
import com.vividsolutions.jts.geom.MultiPoint;
38
import com.vividsolutions.jts.geom.GeometryFactory;
39
import com.vividsolutions.jts.geom.PrecisionModel;
40
41
import org.geotools.feature.AttributeType;
42
import org.geotools.feature.AttributeTypeFactory;
43
import org.geotools.feature.Feature;
44
import org.geotools.feature.FeatureType;
45
import org.geotools.feature.FeatureTypeFactory;
46
import org.geotools.feature.SchemaException;
47
48
import java.sql.ResultSet;
49
import java.sql.PreparedStatement;
50
import java.util.Vector;
51
52
import org.apache.log4j.Logger;
53
54 3040 perry
/**
55 4080 daigle
 *
56
 * Class representing the spatial portions of an xml document as a geotools
57
 * Feature.
58 3040 perry
 */
59 3035 perry
public class SpatialDocument {
60
61 4080 daigle
	private DBConnection dbconn;
62 3035 perry
63 4080 daigle
	private static Logger log = Logger.getLogger(SpatialDocument.class.getName());
64 3035 perry
65 4080 daigle
	private SpatialFeatureSchema featureSchema = new SpatialFeatureSchema();
66 3035 perry
67 4080 daigle
	Vector west = new Vector();
68
	Vector south = new Vector();
69
	Vector east = new Vector();
70
	Vector north = new Vector();
71 3035 perry
72 4080 daigle
	String title = "";
73
	String docid = null;
74 3035 perry
75 4080 daigle
	/**
76
	 * Constructor that queries the db
77
	 *
78
	 * @param docid
79
	 *            The document id to be represented spatially
80
	 * @param dbconn
81
	 *            The database connection shared from the refering method.
82
	 */
83
	public SpatialDocument(String docid, DBConnection dbconn) {
84 3035 perry
85 4080 daigle
		this.docid = docid;
86
		PreparedStatement pstmt = null;
87
		ResultSet rs = null;
88
		this.dbconn = dbconn;
89
		boolean isSpatialDocument = false;
90
		String thisDocname = null;
91
		String westPath = null;
92
		String eastPath = null;
93
		String northPath = null;
94
		String southPath = null;
95 3035 perry
96 4080 daigle
		/*
97
		 * Determine the docname/schema and decide how to proceed with spatial
98
		 * harvest
99
		 */
100
		String query = "SELECT docname FROM xml_documents WHERE docid='" + docid.trim()
101
				+ "';";
102
		String docname = "";
103
		try {
104
			pstmt = dbconn.prepareStatement(query);
105
			pstmt.execute();
106
			rs = pstmt.getResultSet();
107
			while (rs.next()) {
108
				docname = rs.getString(1);
109
			}
110
			rs.close();
111
			pstmt.close();
112
		} catch (Exception e) {
113
			log.error(" ---- Could not get docname for " + docid);
114
			e.printStackTrace();
115
		}
116
		if (docname == null)
117
			docname = "";
118 3035 perry
119 4080 daigle
		// Loop through all our spatial docnames and determine if the current
120
		// document matches
121
		// If so, get the appropriate corner xpaths
122
		try {
123 4699 daigle
			Vector spatialDocnames = MetacatUtil.getOptionList(PropertyService
124 4178 daigle
					.getProperty("spatial.spatialDocnameList"));
125 4080 daigle
			for (int i = 0; i < spatialDocnames.size(); i++) {
126
				thisDocname = ((String) spatialDocnames.elementAt(i)).trim();
127
				if (docname.trim().equals(thisDocname)) {
128
					isSpatialDocument = true;
129 3035 perry
130 4080 daigle
					// determine its east,west,north and south coord xpaths
131 4178 daigle
					westPath = PropertyService.getProperty("spatial." + thisDocname
132 4080 daigle
							+ "_westBoundingCoordinatePath");
133 4178 daigle
					eastPath = PropertyService.getProperty("spatial." + thisDocname
134 4080 daigle
							+ "_eastBoundingCoordinatePath");
135 4178 daigle
					northPath = PropertyService.getProperty("spatial." + thisDocname
136 4080 daigle
							+ "_northBoundingCoordinatePath");
137 4178 daigle
					southPath = PropertyService.getProperty("spatial." + thisDocname
138 4080 daigle
							+ "_southBoundingCoordinatePath");
139
				}
140
			}
141
		} catch (PropertyNotFoundException pnfe) {
142
			log.error("Could not find spatialDocnameList or bounding coordinate "
143
					+ "path for: " + docid);
144
			pnfe.printStackTrace();
145
		}
146 3120 perry
147 4080 daigle
		// If it is a spatial document, harvest the corners and title
148
		if (isSpatialDocument) {
149 3120 perry
150 4080 daigle
			/*
151
			 * Get the bounding coordinates
152
			 */
153
			query = "SELECT path, nodedatanumerical, parentnodeid FROM xml_path_index"
154
					+ " WHERE docid = '"
155
					+ docid.trim()
156
					+ "'"
157
					+ " AND docid IN (SELECT distinct docid FROM xml_access WHERE docid = '"
158
					+ docid.trim()
159
					+ "' AND principal_name = 'public' AND perm_type = 'allow')"
160
					+ " AND (path = '" + westPath + "'" + "  OR path = '" + southPath
161
					+ "'" + "  OR path = '" + eastPath + "'" + "  OR path = '"
162
					+ northPath + "'" + " ) ORDER BY parentnodeid;";
163 3120 perry
164 4080 daigle
			try {
165
				pstmt = dbconn.prepareStatement(query);
166
				pstmt.execute();
167
				rs = pstmt.getResultSet();
168
				while (rs.next()) {
169
					if (rs.getString(1).equals(westPath))
170
						this.west.add(new Float(rs.getFloat(2)));
171
					else if (rs.getString(1).equals(southPath))
172
						this.south.add(new Float(rs.getFloat(2)));
173
					else if (rs.getString(1).equals(eastPath))
174
						this.east.add(new Float(rs.getFloat(2)));
175
					else if (rs.getString(1).equals(northPath))
176
						this.north.add(new Float(rs.getFloat(2)));
177
					else
178
						log.error("** An xml path not related to your bounding coordinates was returned by this query \n"
179
										+ query + "\n");
180
				}
181
				rs.close();
182
				pstmt.close();
183
			} catch (Exception e) {
184
				log.error(" ---- Could not get bounding coordinates for " + docid);
185
				e.printStackTrace();
186
			}
187 3120 perry
188 4080 daigle
			/*
189
			 * Get the title
190
			 */
191 3035 perry
192 4080 daigle
			try {
193
194 4178 daigle
				String docTitlePath = PropertyService.getProperty("spatial.docTitle");
195 4080 daigle
				query = "select nodedata from xml_path_index where path = '"
196
						+ docTitlePath.trim() + "' and docid = '" + docid.trim() + "'";
197
				pstmt = dbconn.prepareStatement(query);
198
				pstmt.execute();
199
				rs = pstmt.getResultSet();
200
				if (rs.next())
201
					this.title = rs.getString(1);
202
				rs.close();
203
				pstmt.close();
204
			} catch (Exception e) {
205
				log.error(" **** Error getting docids from getTitle for docid = "
206
								+ docid);
207
				e.printStackTrace();
208
				this.title = docid;
209
			}
210
		}
211
212 3035 perry
  }
213
214 4080 daigle
  /**
215
	 * Returns a geotools (multi)polygon feature with geometry plus attributes
216
	 * ready to be inserted into our spatial dataset cache
217
	 */
218 3035 perry
  public Feature getPolygonFeature() {
219
      // Get polygon feature type
220
      FeatureType polyType = featureSchema.getPolygonFeatureType();
221
222
      MultiPolygon theGeom = getPolygonGeometry();
223
      if (theGeom == null)
224
          return null;
225
226
      // Populate the feature schema
227
      try {
228
          Feature polyFeature = polyType.create(new Object[]{
229
              theGeom,
230
              this.docid,
231
              getUrl(this.docid),
232
              this.title });
233
          return polyFeature;
234
      } catch (org.geotools.feature.IllegalAttributeException e) {
235
          log.error("!!!!!!! org.geotools.feature.IllegalAttributeException");
236
          return null;
237
      }
238
  }
239
240 3040 perry
  /**
241
   * Returns a geotools (multi)point feature with geometry plus attributes
242 3035 perry
   * ready to be inserted into our spatial dataset cache
243 3040 perry
   *
244 3035 perry
   */
245
  public Feature getPointFeature() {
246
      // Get polygon feature type
247
      FeatureType pointType = featureSchema.getPointFeatureType();
248
249
      MultiPoint theGeom = getPointGeometry();
250
      if (theGeom == null)
251
          return null;
252
253
      // Populate the feature schema
254
      try {
255
          Feature pointFeature = pointType.create(new Object[]{
256
              theGeom,
257
              this.docid,
258
              getUrl(this.docid),
259
              this.title });
260
          return pointFeature;
261
      } catch (org.geotools.feature.IllegalAttributeException e) {
262
          log.error("!!!!!!! org.geotools.feature.IllegalAttributeException");
263
          return null;
264
      }
265
  }
266
267 3040 perry
  /**
268 3035 perry
   * Given a valid docid, return an appropriate URL
269
   * for viewing the metadata document
270 3040 perry
   *
271
   * @param docid The document id for which to construct the access url.
272 3035 perry
   */
273
  private String getUrl( String docid ) {
274 4080 daigle
     String docUrl = null;
275
     try {
276
    	 docUrl = SystemUtil.getServletURL()
277
                    + "?action=read&docid=" + docid
278
                    + "&qformat="
279 4091 daigle
                    + PropertyService.getProperty("application.default-style");
280 4080 daigle
     } catch (PropertyNotFoundException pnfe) {
281
    	 log.error("Could not get access url because of unavailable property: "
282
    			 + pnfe.getMessage());
283
     }
284 3035 perry
285
     return docUrl;
286
  }
287
288 3040 perry
289 3035 perry
  /**
290 3037 perry
   * Returns a mutlipolygon geometry representing the geographic coverage(s) of the document
291 3040 perry
   *
292 3035 perry
   */
293
  private MultiPolygon getPolygonGeometry() {
294
295
    PrecisionModel precModel = new PrecisionModel(); // default: Floating point
296
    GeometryFactory geomFac = new GeometryFactory( precModel, featureSchema.srid );
297
    Vector polygons = new Vector();
298 3070 perry
    float w;
299
    float s;
300
    float e;
301
    float n;
302 3035 perry
303
    if ( west.size() == south.size() && south.size() == east.size() && east.size() == north.size() ) {
304
        for (int i = 0; i < west.size(); i++) {
305
306 3070 perry
            w = ((Float)west.elementAt(i)).floatValue();
307
            s = ((Float)south.elementAt(i)).floatValue();
308
            e = ((Float)east.elementAt(i)).floatValue();
309
            n = ((Float)north.elementAt(i)).floatValue();
310 3035 perry
311
            // Check if it's actually a valid polygon
312 3037 perry
            if (  w == 0.0 && s == 0.0 && e == 0.0 && n == 0.0) {
313 3035 perry
                log.warn("        Invalid or empty coodinates ... skipping");
314
                continue;
315 3070 perry
            } else if( Float.compare(w, e) == 0 && Float.compare(n,s) == 0 ) {
316 3035 perry
                log.warn("        Point coordinates only.. skipping polygon generation");
317
                continue;
318
            }
319
320 3037 perry
            // Handle the case of crossing the dateline and poles
321 3054 perry
            // dateline crossing is valid
322
            // polar crossing is not ( so we swap north and south )
323 3037 perry
            // Assumes all coordinates are confined to -180 -90 180 90
324 3070 perry
            float dl = 180.0f;
325
            float _dl = -180.0f;
326 3037 perry
327
            if ( w > e && s > n ) {
328 3054 perry
                log.info( "Crosses both the dateline and the poles .. split into 2 polygons, swap n & s" );
329
                polygons.add( createPolygonFromBbox( geomFac,   w,   n, dl, s ) );
330
                polygons.add( createPolygonFromBbox( geomFac, _dl,   n,  e, s ) );
331 3037 perry
            } else if ( w > e ) {
332
                log.info( "Crosses the dateline .. split into 2 polygons" );
333
                polygons.add( createPolygonFromBbox( geomFac,   w, s, dl, n ) );
334
                polygons.add( createPolygonFromBbox( geomFac, _dl, s,  e, n ) );
335
            } else if ( s > n ) {
336 3054 perry
                log.info( "Crosses the poles .. swap north and south" );
337
                polygons.add( createPolygonFromBbox( geomFac, w, n, e, s ) );
338 3037 perry
            } else {
339
                // Just a standard polygon that fits nicely onto our flat earth
340
                polygons.add( createPolygonFromBbox( geomFac, w, s, e, n ) );
341
            }
342
343
344 3035 perry
        }
345
    } else {
346
       log.error(" *** Something went wrong.. your east,west,north and south bounding arrays are different sizes!");
347
    }
348
349
    if( polygons.size() > 0 ) {
350
       Polygon[] polyArray = geomFac.toPolygonArray( polygons );
351
       MultiPolygon multiPolyGeom= geomFac.createMultiPolygon( polyArray );
352
       return multiPolyGeom;
353
    } else {
354
       return null;
355
    }
356
357
  }
358
359 3037 perry
360 3040 perry
  /**
361
   * Returns a polygon given the four bounding box coordinates
362 3037 perry
   */
363 3070 perry
  private Polygon createPolygonFromBbox( GeometryFactory geomFac, float w, float s, float e, float n ) {
364 3037 perry
365
        Coordinate[] linestringCoordinates = new Coordinate[5];
366
367
        linestringCoordinates[0] = new Coordinate( w, s );
368
        linestringCoordinates[1] = new Coordinate( w, n );
369
        linestringCoordinates[2] = new Coordinate( e, n );
370
        linestringCoordinates[3] = new Coordinate( e, s );
371
        linestringCoordinates[4] = new Coordinate( w, s );
372
373
        return geomFac.createPolygon( geomFac.createLinearRing(linestringCoordinates), null);
374
  }
375
376
377 3040 perry
  /**
378
   * Returns a multipoint geometry represnting the geographic coverage(s) of the document
379
   *
380
   * @todo Handle the case of crossing the dateline and poles
381 3035 perry
   */
382
  private MultiPoint getPointGeometry() {
383
384
    PrecisionModel precModel = new PrecisionModel(); // default: Floating point
385
    GeometryFactory geomFac = new GeometryFactory( precModel, featureSchema.srid );
386 3070 perry
    float w;
387
    float s;
388
    float e;
389
    float n;
390 3035 perry
391
    PreparedStatement pstmt = null;
392
    ResultSet rs = null;
393
394
    Vector points = new Vector();
395
396
    if ( west.size() == south.size() && south.size() == east.size() && east.size() == north.size() ) {
397
        for (int i = 0; i < west.size(); i++) {
398
399 3070 perry
            w = ((Float)west.elementAt(i)).floatValue();
400
            s = ((Float)south.elementAt(i)).floatValue();
401
            e = ((Float)east.elementAt(i)).floatValue();
402
            n = ((Float)north.elementAt(i)).floatValue();
403 3050 perry
404 3035 perry
            // Check if it's actually a valid point
405 3070 perry
            if (  w == 0.0f && s == 0.0f && e == 0.0f && n == 0.0f) {
406 3050 perry
                 log.warn("        Invalid or empty coodinates ... skipping");
407
                 continue;
408
            }
409 3035 perry
410 3070 perry
            float xCenter;
411
            float yCenter;
412 3050 perry
413
            // Handle the case of crossing the dateline and poles
414
            // Assumes all coordinates are confined to -180 -90 180 90
415
416 3054 perry
            if ( w > e ) {
417 3050 perry
                log.info( "Crosses the dateline .. " );
418 3070 perry
                xCenter = (360.0f - w + e)/ 2.0f + w;
419
                if( xCenter > 180.0f )
420
                    xCenter = xCenter - 360.0f;
421
                yCenter = ( s + n ) / 2.0f;
422 3050 perry
            } else {
423
                // Just a standard point that can be calculated by the average coordinates
424 3070 perry
                xCenter = ( w + e ) / 2.0f;
425
                yCenter = ( s + n ) / 2.0f;
426 3035 perry
            }
427
428
            points.add( geomFac.createPoint( new Coordinate( xCenter, yCenter)) );
429
        }
430
    } else {
431
       log.error(" *** Something went wrong.. your east,west,north and south bounding vectors are different sizes!");
432
    }
433
434
    if( points.size() > 0 ) {
435
       Point[] pointArray = geomFac.toPointArray( points );
436
       MultiPoint multiPointGeom= geomFac.createMultiPoint( pointArray );
437
       return multiPointGeom;
438
    } else {
439
       return null;
440
    }
441
442
443
  }
444
}