Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2003 Regents of the University of California.
4
 *
5
 * Author: Matthew Perry 
6
 * '$Date: 2006-12-13 16:38:12 -0800 (Wed, 13 Dec 2006) $'
7
 * '$Revision: 3120 $'
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
import edu.ucsb.nceas.metacat.DBConnection;
28
import edu.ucsb.nceas.metacat.MetaCatUtil;
29

    
30
import com.vividsolutions.jts.geom.Coordinate;
31
import com.vividsolutions.jts.geom.Point;
32
import com.vividsolutions.jts.geom.Polygon;
33
import com.vividsolutions.jts.geom.MultiPolygon;
34
import com.vividsolutions.jts.geom.MultiPoint;
35
import com.vividsolutions.jts.geom.GeometryFactory;
36
import com.vividsolutions.jts.geom.PrecisionModel;
37

    
38
import org.geotools.feature.AttributeType;
39
import org.geotools.feature.AttributeTypeFactory;
40
import org.geotools.feature.Feature;
41
import org.geotools.feature.FeatureType;
42
import org.geotools.feature.FeatureTypeFactory;
43
import org.geotools.feature.SchemaException;
44

    
45
import java.sql.ResultSet;
46
import java.sql.PreparedStatement;
47
import java.util.Vector;
48

    
49
import org.apache.log4j.Logger;
50

    
51
/**
52

    
53
 * Class representing the spatial portions of an xml document
54
 * as a geotools Feature.
55
 */
56
public class SpatialDocument {
57

    
58
  private DBConnection dbconn;
59

    
60
  private static Logger log =
61
      Logger.getLogger(SpatialDocument.class.getName());
62

    
63
  private SpatialFeatureSchema featureSchema = new SpatialFeatureSchema();
64

    
65
  Vector west = new Vector();
66
  Vector south = new Vector();
67
  Vector east = new Vector();
68
  Vector north = new Vector();
69

    
70
  String title = "";
71
  String docid = null;
72

    
73
  /** 
74
   * Constructor that queries the db 
75
   *
76
   * @param docid The document id to be represented spatially
77
   * @param dbconn The database connection shared from the refering method.
78
   */
79
  public SpatialDocument( String docid , DBConnection dbconn ) {
80

    
81
    this.docid = docid;
82
    PreparedStatement pstmt = null;
83
    ResultSet rs = null;
84
    this.dbconn = dbconn;
85
    boolean isSpatialDocument = false;
86
    String thisDocname = null;
87
    String westPath = null;
88
    String eastPath = null;
89
    String northPath = null;
90
    String southPath = null;
91
   
92
    /*
93
     * Determine the docname/schema and decide how to proceed with
94
     * spatial harvest
95
     */
96
    String query = "SELECT docname FROM xml_documents WHERE docid='" + docid.trim() + "';";
97
    String docname = "";
98
    try {
99
      pstmt = dbconn.prepareStatement(query);
100
      pstmt.execute();
101
      rs = pstmt.getResultSet();
102
      while (rs.next()) {
103
        docname = rs.getString(1);	
104
      }
105
      rs.close();
106
      pstmt.close();
107
    }
108
    catch(Exception e) {
109
      log.error(" ---- Could not get docname for " + docid );
110
      e.printStackTrace();
111
    }      
112
    if (docname == null) 
113
	    docname = "";
114

    
115
    // Loop through all our spatial docnames and determine if the current document matches
116
    // If so, get the appropriate corner xpaths
117
    Vector spatialDocnames = MetaCatUtil.getOptionList( MetaCatUtil.getOption("spatialDocnameList") );
118
    for (int i = 0; i < spatialDocnames.size(); i++) {
119
	    thisDocname = ((String)spatialDocnames.elementAt(i)).trim();
120
	    if ( docname.trim().equals(thisDocname) ) {
121
		    isSpatialDocument = true;
122

    
123
    		    //  determine its east,west,north and south coord xpaths
124
		    westPath = MetaCatUtil.getOption(thisDocname + "_westBoundingCoordinatePath");
125
		    eastPath = MetaCatUtil.getOption(thisDocname + "_eastBoundingCoordinatePath");
126
		    northPath = MetaCatUtil.getOption(thisDocname + "_northBoundingCoordinatePath");
127
		    southPath = MetaCatUtil.getOption(thisDocname + "_southBoundingCoordinatePath");
128
	    }
129
    }
130

    
131
    //  If it is a spatial document, harvest the corners and title 
132
    if (isSpatialDocument) {
133

    
134
	    /*
135
	     * Get the bounding coordinates
136
	     */
137
	    query = "SELECT path, nodedatanumerical, parentnodeid FROM xml_path_index"
138
	    + " WHERE docid = '" + docid.trim() + "'"
139
	    + " AND docid IN (SELECT distinct docid FROM xml_access WHERE docid = '" + docid.trim() 
140
	    + "' AND principal_name = 'public' AND perm_type = 'allow')"
141
	    + " AND (path = '" + westPath + "'"
142
	    + "  OR path = '" + southPath + "'"
143
	    + "  OR path = '" +  eastPath + "'"
144
	    + "  OR path = '" + northPath + "'"
145
	    + " ) ORDER BY parentnodeid;";
146

    
147
	    try {
148
	      pstmt = dbconn.prepareStatement(query);
149
	      pstmt.execute();
150
	      rs = pstmt.getResultSet();
151
	      while (rs.next()) {
152
		if ( rs.getString(1).equals(westPath) )
153
		    this.west.add( new Float(rs.getFloat(2)) );
154
		else if ( rs.getString(1).equals(southPath) )
155
		    this.south.add( new Float(rs.getFloat(2)) );
156
		else if ( rs.getString(1).equals(eastPath) )
157
		    this.east.add( new Float(rs.getFloat(2)) );
158
		else if ( rs.getString(1).equals(northPath) )
159
		    this.north.add( new Float(rs.getFloat(2)) );
160
		else
161
		    log.error("** An xml path not related to your bounding coordinates was returned by this query \n" + query + "\n");
162
	      }
163
	      rs.close();
164
	      pstmt.close();
165
	    }
166
	    catch(Exception e) {
167
	      log.error(" ---- Could not get bounding coordinates for " + docid );
168
	      e.printStackTrace();
169
	    }      
170

    
171
	    /*
172
	     * Get the title
173
	     */
174
	    String docTitlePath = MetaCatUtil.getOption("docTitle");
175
	    query = "select nodedata from xml_path_index where path = '" 
176
		  + docTitlePath.trim() + "' and docid = '" + docid.trim() + "'";
177

    
178
	    try {
179
	      pstmt = dbconn.prepareStatement(query);
180
	      pstmt.execute();
181
	      rs = pstmt.getResultSet();
182
	      if (rs.next())
183
		this.title = rs.getString(1);
184
	      rs.close();
185
	      pstmt.close();
186
	    }
187
	    catch(Exception e) {
188
	      log.error(" **** Error getting docids from getTitle for docid = "+docid);
189
	      e.printStackTrace();
190
	      this.title = docid;
191
	    }
192
    }
193

    
194
  }
195

    
196
  /** 
197
   * Returns a geotools (multi)polygon feature with geometry plus attributes
198
   * ready to be inserted into our spatial dataset cache
199
   */
200
  public Feature getPolygonFeature() {
201
      // Get polygon feature type
202
      FeatureType polyType = featureSchema.getPolygonFeatureType();
203

    
204
      MultiPolygon theGeom = getPolygonGeometry();
205
      if (theGeom == null)
206
          return null;
207

    
208
      // Populate the feature schema
209
      try {
210
          Feature polyFeature = polyType.create(new Object[]{ 
211
              theGeom,
212
              this.docid,
213
              getUrl(this.docid), 
214
              this.title });
215
          return polyFeature; 
216
      } catch (org.geotools.feature.IllegalAttributeException e) {
217
          log.error("!!!!!!! org.geotools.feature.IllegalAttributeException");
218
          return null;
219
      }
220
  }
221

    
222
  /**
223
   * Returns a geotools (multi)point feature with geometry plus attributes
224
   * ready to be inserted into our spatial dataset cache
225
   *
226
   */
227
  public Feature getPointFeature() {
228
      // Get polygon feature type
229
      FeatureType pointType = featureSchema.getPointFeatureType();
230

    
231
      MultiPoint theGeom = getPointGeometry();
232
      if (theGeom == null)
233
          return null;
234

    
235
      // Populate the feature schema
236
      try {
237
          Feature pointFeature = pointType.create(new Object[]{ 
238
              theGeom,
239
              this.docid,
240
              getUrl(this.docid), 
241
              this.title });
242
          return pointFeature;
243
      } catch (org.geotools.feature.IllegalAttributeException e) {
244
          log.error("!!!!!!! org.geotools.feature.IllegalAttributeException");
245
          return null;
246
      }
247
  }
248

    
249
  /**
250
   * Given a valid docid, return an appropriate URL
251
   * for viewing the metadata document
252
   *
253
   * @param docid The document id for which to construct the access url.
254
   */
255
  private String getUrl( String docid ) {
256
     String docUrl = MetaCatUtil.getOption("metacatUrl")  
257
                    + "?action=read"
258
                    + "&docid=" + docid 
259
                    + "&qformat=" + MetaCatUtil.getOption("default-style");
260

    
261
     return docUrl;
262
  }
263

    
264

    
265
  /**
266
   * Returns a mutlipolygon geometry representing the geographic coverage(s) of the document
267
   *
268
   */
269
  private MultiPolygon getPolygonGeometry() {
270

    
271
    PrecisionModel precModel = new PrecisionModel(); // default: Floating point
272
    GeometryFactory geomFac = new GeometryFactory( precModel, featureSchema.srid );
273
    Vector polygons = new Vector();
274
    float w;
275
    float s;
276
    float e;
277
    float n;
278

    
279
    if ( west.size() == south.size() && south.size() == east.size() && east.size() == north.size() ) {
280
        for (int i = 0; i < west.size(); i++) {
281

    
282
            w = ((Float)west.elementAt(i)).floatValue();
283
            s = ((Float)south.elementAt(i)).floatValue();
284
            e = ((Float)east.elementAt(i)).floatValue();
285
            n = ((Float)north.elementAt(i)).floatValue();
286

    
287
            // Check if it's actually a valid polygon
288
            if (  w == 0.0 && s == 0.0 && e == 0.0 && n == 0.0) {
289
                log.warn("        Invalid or empty coodinates ... skipping");
290
                continue;
291
            } else if( Float.compare(w, e) == 0 && Float.compare(n,s) == 0 ) {
292
                log.warn("        Point coordinates only.. skipping polygon generation");
293
                continue;
294
            }
295

    
296
            // Handle the case of crossing the dateline and poles
297
            // dateline crossing is valid 
298
            // polar crossing is not ( so we swap north and south )
299
            // Assumes all coordinates are confined to -180 -90 180 90
300
            float dl = 180.0f;
301
            float _dl = -180.0f;
302
            
303
            if ( w > e && s > n ) {
304
                log.info( "Crosses both the dateline and the poles .. split into 2 polygons, swap n & s" );
305
                polygons.add( createPolygonFromBbox( geomFac,   w,   n, dl, s ) );
306
                polygons.add( createPolygonFromBbox( geomFac, _dl,   n,  e, s ) );
307
            } else if ( w > e ) {
308
                log.info( "Crosses the dateline .. split into 2 polygons" );
309
                polygons.add( createPolygonFromBbox( geomFac,   w, s, dl, n ) );
310
                polygons.add( createPolygonFromBbox( geomFac, _dl, s,  e, n ) );
311
            } else if ( s > n ) {
312
                log.info( "Crosses the poles .. swap north and south" );
313
                polygons.add( createPolygonFromBbox( geomFac, w, n, e, s ) );
314
            } else {
315
                // Just a standard polygon that fits nicely onto our flat earth
316
                polygons.add( createPolygonFromBbox( geomFac, w, s, e, n ) );    
317
            }
318

    
319
             
320
        }
321
    } else {
322
       log.error(" *** Something went wrong.. your east,west,north and south bounding arrays are different sizes!");
323
    }
324
    
325
    if( polygons.size() > 0 ) {
326
       Polygon[] polyArray = geomFac.toPolygonArray( polygons );
327
       MultiPolygon multiPolyGeom= geomFac.createMultiPolygon( polyArray );
328
       return multiPolyGeom; 
329
    } else {
330
       return null;
331
    } 
332

    
333
  }
334
   
335

    
336
  /**
337
   * Returns a polygon given the four bounding box coordinates
338
   */
339
  private Polygon createPolygonFromBbox( GeometryFactory geomFac, float w, float s, float e, float n ) {
340

    
341
        Coordinate[] linestringCoordinates = new Coordinate[5];
342

    
343
        linestringCoordinates[0] = new Coordinate( w, s );
344
        linestringCoordinates[1] = new Coordinate( w, n );
345
        linestringCoordinates[2] = new Coordinate( e, n );
346
        linestringCoordinates[3] = new Coordinate( e, s );
347
        linestringCoordinates[4] = new Coordinate( w, s );
348

    
349
        return geomFac.createPolygon( geomFac.createLinearRing(linestringCoordinates), null);
350
  }
351

    
352

    
353
  /**
354
   * Returns a multipoint geometry represnting the geographic coverage(s) of the document
355
   *
356
   * @todo Handle the case of crossing the dateline and poles
357
   */
358
  private MultiPoint getPointGeometry() {
359

    
360
    PrecisionModel precModel = new PrecisionModel(); // default: Floating point
361
    GeometryFactory geomFac = new GeometryFactory( precModel, featureSchema.srid );
362
    float w;
363
    float s;
364
    float e;
365
    float n;
366

    
367
    PreparedStatement pstmt = null;
368
    ResultSet rs = null;
369

    
370
    Vector points = new Vector();
371

    
372
    if ( west.size() == south.size() && south.size() == east.size() && east.size() == north.size() ) {
373
        for (int i = 0; i < west.size(); i++) {
374

    
375
            w = ((Float)west.elementAt(i)).floatValue();
376
            s = ((Float)south.elementAt(i)).floatValue();
377
            e = ((Float)east.elementAt(i)).floatValue();
378
            n = ((Float)north.elementAt(i)).floatValue();
379

    
380
            // Check if it's actually a valid point
381
            if (  w == 0.0f && s == 0.0f && e == 0.0f && n == 0.0f) {
382
                 log.warn("        Invalid or empty coodinates ... skipping");
383
                 continue;
384
            }
385

    
386
            float xCenter;
387
            float yCenter;
388

    
389
            // Handle the case of crossing the dateline and poles
390
            // Assumes all coordinates are confined to -180 -90 180 90
391

    
392
            if ( w > e ) {
393
                log.info( "Crosses the dateline .. " );
394
                xCenter = (360.0f - w + e)/ 2.0f + w;
395
                if( xCenter > 180.0f )
396
                    xCenter = xCenter - 360.0f;
397
                yCenter = ( s + n ) / 2.0f;
398
            } else {
399
                // Just a standard point that can be calculated by the average coordinates
400
                xCenter = ( w + e ) / 2.0f;
401
                yCenter = ( s + n ) / 2.0f;
402
            }
403

    
404
            points.add( geomFac.createPoint( new Coordinate( xCenter, yCenter)) );
405
        }
406
    } else {
407
       log.error(" *** Something went wrong.. your east,west,north and south bounding vectors are different sizes!");
408
    }
409
    
410
    if( points.size() > 0 ) {
411
       Point[] pointArray = geomFac.toPointArray( points );
412
       MultiPoint multiPointGeom= geomFac.createMultiPoint( pointArray );
413
       return multiPointGeom; 
414
    } else {
415
       return null;
416
    } 
417

    
418

    
419
  }
420
}
(3-3/8)