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
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 3040 perry
/**
52 3054 perry
53 3040 perry
 * Class representing the spatial portions of an xml document
54
 * as a geotools Feature.
55
 */
56 3035 perry
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 = null;
71
  String docid = null;
72
73 3040 perry
  /**
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
   * @todo Rewrite the title query to use xml_path_index
79
   */
80 3035 perry
  public SpatialDocument( String docid , DBConnection dbconn ) {
81
82
    this.docid = docid;
83
    PreparedStatement pstmt = null;
84
    ResultSet rs = null;
85
    this.dbconn = dbconn;
86
87
    /*
88
     * Get the bounding coordinates
89
     */
90
    String query = "SELECT path, nodedatanumerical, parentnodeid FROM xml_path_index"
91
    + " WHERE docid = '" + docid.trim() + "'"
92 3068 perry
    + " AND docid IN (SELECT distinct docid FROM xml_access WHERE docid = '" + docid.trim()
93
    + "' AND principal_name = 'public' AND perm_type = 'allow')"
94 3035 perry
    + " AND (path = '" + MetaCatUtil.getOption("westBoundingCoordinatePath") + "'"
95
    + "  OR path = '" + MetaCatUtil.getOption("southBoundingCoordinatePath") + "'"
96
    + "  OR path = '" + MetaCatUtil.getOption("eastBoundingCoordinatePath") + "'"
97
    + "  OR path = '" + MetaCatUtil.getOption("northBoundingCoordinatePath") + "'"
98
    + " ) ORDER BY parentnodeid;";
99
100
    try {
101
      pstmt = dbconn.prepareStatement(query);
102
      pstmt.execute();
103
      rs = pstmt.getResultSet();
104
      while (rs.next()) {
105
        if ( rs.getString(1).equals( MetaCatUtil.getOption("westBoundingCoordinatePath") ) )
106
            this.west.add( rs.getFloat(2) );
107
        else if ( rs.getString(1).equals( MetaCatUtil.getOption("southBoundingCoordinatePath") ) )
108
            this.south.add( rs.getFloat(2) );
109
        else if ( rs.getString(1).equals( MetaCatUtil.getOption("eastBoundingCoordinatePath") ) )
110
            this.east.add( rs.getFloat(2) );
111
        else if ( rs.getString(1).equals( MetaCatUtil.getOption("northBoundingCoordinatePath") ) )
112
            this.north.add( rs.getFloat(2) );
113
        else
114
            log.error("** An xml path not related to your bounding coordinates was returned by this query \n" + query + "\n");
115
      }
116
      rs.close();
117
      pstmt.close();
118
    }
119
    catch(Exception e) {
120 3068 perry
      log.error(" ---- Could not get bounding coordinates for " + docid + );
121 3035 perry
      e.printStackTrace();
122
    }
123
124 3040 perry
    /*
125
     * Get the title
126
     */
127
    String docTitlePath = MetaCatUtil.getOption("docTitle");
128
    query = "select nodedata from xml_path_index where path = '"
129 3057 perry
          + docTitlePath.trim() + "' and docid = '" + docid.trim() + "'";
130 3035 perry
131
    try {
132
      pstmt = dbconn.prepareStatement(query);
133
      pstmt.execute();
134
      rs = pstmt.getResultSet();
135
      if (rs.next())
136 3040 perry
        this.title = rs.getString(1);
137 3057 perry
        //log.warn(" \n\n****** TITLE query is " + query + " \n\n");
138 3035 perry
      rs.close();
139
      pstmt.close();
140
    }
141
    catch(Exception e) {
142
      log.error(" **** Error getting docids from getTitle for docid = "+docid);
143
      e.printStackTrace();
144
      log.error(" query ============== \n " + query);
145
      this.title = docid;
146
    }
147
148
    /* try {
149
        dbconn.close();
150
    } catch( java.sql.SQLException e ) {
151
        log.error("java.sql.SQLException");
152
    }
153
    */
154
155
  }
156
157 3040 perry
  /**
158
   * Returns a geotools (multi)polygon feature with geometry plus attributes
159 3035 perry
   * ready to be inserted into our spatial dataset cache
160
   */
161
  public Feature getPolygonFeature() {
162
      // Get polygon feature type
163
      FeatureType polyType = featureSchema.getPolygonFeatureType();
164
165
      MultiPolygon theGeom = getPolygonGeometry();
166
      if (theGeom == null)
167
          return null;
168
169
      // Populate the feature schema
170
      try {
171
          Feature polyFeature = polyType.create(new Object[]{
172
              theGeom,
173
              this.docid,
174
              getUrl(this.docid),
175
              this.title });
176
          return polyFeature;
177
      } catch (org.geotools.feature.IllegalAttributeException e) {
178
          log.error("!!!!!!! org.geotools.feature.IllegalAttributeException");
179
          return null;
180
      }
181
  }
182
183 3040 perry
  /**
184
   * Returns a geotools (multi)point feature with geometry plus attributes
185 3035 perry
   * ready to be inserted into our spatial dataset cache
186 3040 perry
   *
187 3035 perry
   */
188
  public Feature getPointFeature() {
189
      // Get polygon feature type
190
      FeatureType pointType = featureSchema.getPointFeatureType();
191
192
      MultiPoint theGeom = getPointGeometry();
193
      if (theGeom == null)
194
          return null;
195
196
      // Populate the feature schema
197
      try {
198
          Feature pointFeature = pointType.create(new Object[]{
199
              theGeom,
200
              this.docid,
201
              getUrl(this.docid),
202
              this.title });
203
          return pointFeature;
204
      } catch (org.geotools.feature.IllegalAttributeException e) {
205
          log.error("!!!!!!! org.geotools.feature.IllegalAttributeException");
206
          return null;
207
      }
208
  }
209
210 3040 perry
  /**
211 3035 perry
   * Given a valid docid, return an appropriate URL
212
   * for viewing the metadata document
213 3040 perry
   *
214
   * @param docid The document id for which to construct the access url.
215 3035 perry
   */
216
  private String getUrl( String docid ) {
217
     String docUrl = MetaCatUtil.getOption("metacatUrl")
218
                    + "?action=read"
219
                    + "&docid=" + docid
220
                    + "&qformat=" + MetaCatUtil.getOption("default-style");
221
222
     return docUrl;
223
  }
224
225 3040 perry
226 3035 perry
  /**
227 3037 perry
   * Returns a mutlipolygon geometry representing the geographic coverage(s) of the document
228 3040 perry
   *
229 3035 perry
   */
230
  private MultiPolygon getPolygonGeometry() {
231
232
    PrecisionModel precModel = new PrecisionModel(); // default: Floating point
233
    GeometryFactory geomFac = new GeometryFactory( precModel, featureSchema.srid );
234
    Vector polygons = new Vector();
235 3037 perry
    Float w;
236
    Float s;
237
    Float e;
238
    Float n;
239 3035 perry
240
    if ( west.size() == south.size() && south.size() == east.size() && east.size() == north.size() ) {
241
        for (int i = 0; i < west.size(); i++) {
242
243 3037 perry
            w = (Float)west.elementAt(i);
244
            s = (Float)south.elementAt(i);
245
            e = (Float)east.elementAt(i);
246
            n = (Float)north.elementAt(i);
247 3035 perry
248
            // Check if it's actually a valid polygon
249 3037 perry
            if (  w == 0.0 && s == 0.0 && e == 0.0 && n == 0.0) {
250 3035 perry
                log.warn("        Invalid or empty coodinates ... skipping");
251
                continue;
252 3037 perry
            } else if( w.compareTo( e ) == 0 && n.compareTo( s ) == 0 ) {
253 3035 perry
                log.warn("        Point coordinates only.. skipping polygon generation");
254
                continue;
255
            }
256
257 3037 perry
            // Handle the case of crossing the dateline and poles
258 3054 perry
            // dateline crossing is valid
259
            // polar crossing is not ( so we swap north and south )
260 3037 perry
            // Assumes all coordinates are confined to -180 -90 180 90
261
            Float dl = new Float("180.0");
262
            Float _dl = new Float("-180.0");
263
264
            if ( w > e && s > n ) {
265 3054 perry
                log.info( "Crosses both the dateline and the poles .. split into 2 polygons, swap n & s" );
266
                polygons.add( createPolygonFromBbox( geomFac,   w,   n, dl, s ) );
267
                polygons.add( createPolygonFromBbox( geomFac, _dl,   n,  e, s ) );
268 3037 perry
            } else if ( w > e ) {
269
                log.info( "Crosses the dateline .. split into 2 polygons" );
270
                polygons.add( createPolygonFromBbox( geomFac,   w, s, dl, n ) );
271
                polygons.add( createPolygonFromBbox( geomFac, _dl, s,  e, n ) );
272
            } else if ( s > n ) {
273 3054 perry
                log.info( "Crosses the poles .. swap north and south" );
274
                polygons.add( createPolygonFromBbox( geomFac, w, n, e, s ) );
275 3037 perry
            } else {
276
                // Just a standard polygon that fits nicely onto our flat earth
277
                polygons.add( createPolygonFromBbox( geomFac, w, s, e, n ) );
278
            }
279
280
281 3035 perry
        }
282
    } else {
283
       log.error(" *** Something went wrong.. your east,west,north and south bounding arrays are different sizes!");
284
    }
285
286
    if( polygons.size() > 0 ) {
287
       Polygon[] polyArray = geomFac.toPolygonArray( polygons );
288
       MultiPolygon multiPolyGeom= geomFac.createMultiPolygon( polyArray );
289
       return multiPolyGeom;
290
    } else {
291
       return null;
292
    }
293
294
  }
295
296 3037 perry
297 3040 perry
  /**
298
   * Returns a polygon given the four bounding box coordinates
299 3037 perry
   */
300
  private Polygon createPolygonFromBbox( GeometryFactory geomFac, Float w, Float s, Float e, Float n ) {
301
302
        Coordinate[] linestringCoordinates = new Coordinate[5];
303
304
        linestringCoordinates[0] = new Coordinate( w, s );
305
        linestringCoordinates[1] = new Coordinate( w, n );
306
        linestringCoordinates[2] = new Coordinate( e, n );
307
        linestringCoordinates[3] = new Coordinate( e, s );
308
        linestringCoordinates[4] = new Coordinate( w, s );
309
310
        return geomFac.createPolygon( geomFac.createLinearRing(linestringCoordinates), null);
311
  }
312
313
314 3040 perry
  /**
315
   * Returns a multipoint geometry represnting the geographic coverage(s) of the document
316
   *
317
   * @todo Handle the case of crossing the dateline and poles
318 3035 perry
   */
319
  private MultiPoint getPointGeometry() {
320
321
    PrecisionModel precModel = new PrecisionModel(); // default: Floating point
322
    GeometryFactory geomFac = new GeometryFactory( precModel, featureSchema.srid );
323 3050 perry
    Float w;
324
    Float s;
325
    Float e;
326
    Float n;
327 3035 perry
328
    PreparedStatement pstmt = null;
329
    ResultSet rs = null;
330
331
    Vector points = new Vector();
332
333
    if ( west.size() == south.size() && south.size() == east.size() && east.size() == north.size() ) {
334
        for (int i = 0; i < west.size(); i++) {
335
336 3050 perry
            w = (Float)west.elementAt(i);
337
            s = (Float)south.elementAt(i);
338
            e = (Float)east.elementAt(i);
339
            n = (Float)north.elementAt(i);
340
341 3035 perry
            // Check if it's actually a valid point
342 3050 perry
            if (  w == 0.0 && s == 0.0 && e == 0.0 && n == 0.0) {
343
                 log.warn("        Invalid or empty coodinates ... skipping");
344
                 continue;
345
            }
346 3035 perry
347 3050 perry
            Double xCenter;
348
            Double yCenter;
349
350
            // Handle the case of crossing the dateline and poles
351
            // Assumes all coordinates are confined to -180 -90 180 90
352
353 3054 perry
            if ( w > e ) {
354 3050 perry
                log.info( "Crosses the dateline .. " );
355
                xCenter = ((float)360.0 - w + e)/ (Double)2.0 + w;
356
                if( xCenter > 180 )
357
                    xCenter = xCenter - 360;
358
                yCenter = ( s + n ) / (Double) 2.0;
359
            } else {
360
                // Just a standard point that can be calculated by the average coordinates
361
                xCenter = ( w + e ) / (Double) 2.0;
362
                yCenter = ( s + n ) / (Double) 2.0;
363 3035 perry
            }
364
365
            points.add( geomFac.createPoint( new Coordinate( xCenter, yCenter)) );
366
        }
367
    } else {
368
       log.error(" *** Something went wrong.. your east,west,north and south bounding vectors are different sizes!");
369
    }
370
371
    if( points.size() > 0 ) {
372
       Point[] pointArray = geomFac.toPointArray( points );
373
       MultiPoint multiPointGeom= geomFac.createMultiPoint( pointArray );
374
       return multiPointGeom;
375
    } else {
376
       return null;
377
    }
378
379
380
  }
381
}