001: /*
002: * This file is part of the GeOxygene project source files.
003: *
004: * GeOxygene aims at providing an open framework which implements OGC/ISO specifications for
005: * the development and deployment of geographic (GIS) applications. It is a open source
006: * contribution of the COGIT laboratory at the Institut Géographique National (the French
007: * National Mapping Agency).
008: *
009: * See: http://oxygene-project.sourceforge.net
010: *
011: * Copyright (C) 2005 Institut Géographique National
012: *
013: * This library is free software; you can redistribute it and/or modify it under the terms
014: * of the GNU Lesser General Public License as published by the Free Software Foundation;
015: * either version 2.1 of the License, or any later version.
016: *
017: * This library is distributed in the hope that it will be useful, but WITHOUT ANY
018: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
019: * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
020: *
021: * You should have received a copy of the GNU Lesser General Public License along with
022: * this library (see file LICENSE if present); if not, write to the Free Software
023: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024: *
025: */
026:
027: package fr.ign.cogit.geoxygene.util.viewer;
028:
029: import java.awt.Color;
030: import java.lang.reflect.Field;
031: import java.util.HashSet;
032: import java.util.Iterator;
033: import java.util.Set;
034: import java.util.Vector;
035:
036: import uk.ac.leeds.ccg.geotools.DataSource;
037: import uk.ac.leeds.ccg.geotools.GeoData;
038: import uk.ac.leeds.ccg.geotools.GeoLine;
039: import uk.ac.leeds.ccg.geotools.GeoPoint;
040: import uk.ac.leeds.ccg.geotools.GeoPolygon;
041: import uk.ac.leeds.ccg.geotools.GeoShape;
042: import uk.ac.leeds.ccg.geotools.HSVShader;
043: import uk.ac.leeds.ccg.geotools.Layer;
044: import uk.ac.leeds.ccg.geotools.LineLayer;
045: import uk.ac.leeds.ccg.geotools.PointLayer;
046: import uk.ac.leeds.ccg.geotools.PolygonLayer;
047: import uk.ac.leeds.ccg.geotools.Shader;
048: import uk.ac.leeds.ccg.geotools.ShapeLayer;
049: import uk.ac.leeds.ccg.geotools.SimpleGeoData;
050: import uk.ac.leeds.ccg.geotools.Theme;
051: import uk.ac.leeds.ccg.geotools.UniqueShader;
052: import fr.ign.cogit.geoxygene.feature.FT_Feature;
053: import fr.ign.cogit.geoxygene.feature.FT_FeatureCollection;
054: import fr.ign.cogit.geoxygene.spatial.coordgeom.DirectPositionList;
055: import fr.ign.cogit.geoxygene.spatial.coordgeom.GM_LineString;
056: import fr.ign.cogit.geoxygene.spatial.coordgeom.GM_Polygon;
057: import fr.ign.cogit.geoxygene.spatial.geomaggr.GM_Aggregate;
058: import fr.ign.cogit.geoxygene.spatial.geomaggr.GM_MultiCurve;
059: import fr.ign.cogit.geoxygene.spatial.geomaggr.GM_MultiPoint;
060: import fr.ign.cogit.geoxygene.spatial.geomaggr.GM_MultiSurface;
061: import fr.ign.cogit.geoxygene.spatial.geomprim.GM_Point;
062: import fr.ign.cogit.geoxygene.spatial.geomroot.GM_Object;
063: import fr.ign.cogit.geoxygene.util.browser.ObjectBrowser;
064:
065: /**
066: * A class to simplify the process of loading a FT_FeatureCollection from GeOxygene.
067: * Collections MUST be homogeneous.
068: * Geometry types in the collection MUST be the same for all features (but some null geometries are accepted).
069: * All features in the collection must belong to same class.
070: * Works only for collections of features whose geometry type is :
071: * GM_Point, GM_LineString, GM_Polygon, GM_MultiPoint, GM_MultiCurve (with linestrings), GM_Multisurface (with polygons)
072: * and GM_Aggregate with an homogeneous geometry type inside.
073: * ID are fill with hashcode().
074: * One GeOxygeneReader is created by Theme stemming from GeOxygene data.
075: *
076: * @author Thierry Badard & Arnaud Braun & Eric Grosso
077: * @version 1.1
078: *
079: *
080: */
081:
082: class GeOxygeneReader implements DataSource {
083:
084: // Default
085: public static boolean SHOW_PUBLIC_ATTRIBUTES = true;
086: public static boolean SHOW_PROTECTED_ATTRIBUTES = true;
087:
088: // GeOxygene fields
089: private FT_FeatureCollection coll;
090: private Class featureClass;
091:
092: // GEOTOOLS fields
093: private Layer layer;
094: private Theme theme;
095:
096: public GeOxygeneReader(FT_FeatureCollection featColl) {
097: coll = featColl;
098:
099: // guess feature class (type of the first feature ...)
100: coll.initIterator();
101: FT_Feature feature = coll.next();
102: featureClass = feature.getClass();
103:
104: // guess geometry type (type of the first geometry is the type for all geometries ...)
105: coll.initIterator();
106: GM_Object geom = null;
107: boolean hasGeom = false;
108: while (coll.hasNext()) {
109: geom = coll.next().getGeom();
110: if (geom != null) {
111: hasGeom = true;
112: break;
113: }
114: }
115: if (!hasGeom) {
116: System.out.println("No geometry in "
117: + coll.get(0).getClass().getName());
118: return;
119: }
120:
121: // init layer
122: if (GM_Point.class.isAssignableFrom(geom.getClass())
123: || GM_MultiPoint.class
124: .isAssignableFrom(geom.getClass()))
125: layer = readPoints();
126: else if (GM_LineString.class.isAssignableFrom(geom.getClass())
127: || GM_MultiCurve.class
128: .isAssignableFrom(geom.getClass()))
129: layer = readLines();
130: else if (GM_Polygon.class.isAssignableFrom(geom.getClass())
131: || GM_MultiSurface.class.isAssignableFrom(geom
132: .getClass()))
133: layer = readPolygons();
134: //ajout pour le cas des GM_Aggregate (
135: else if (GM_Aggregate.class.isAssignableFrom(geom.getClass())) {
136: Class classe = ((GM_Aggregate) geom).get(0).getClass();
137: if (GM_Point.class.isAssignableFrom(geom.getClass())
138: || GM_MultiPoint.class.isAssignableFrom(classe)) {
139: layer = readPoints();
140: } else if (GM_LineString.class.isAssignableFrom(classe)
141: || GM_MultiCurve.class.isAssignableFrom(classe)) {
142: layer = readLines();
143: } else if (GM_Polygon.class.isAssignableFrom(classe)
144: || GM_MultiSurface.class.isAssignableFrom(classe)) {
145: layer = readPolygons();
146: }
147: } else {
148: System.out
149: .println(" ## GeOxygeneReader works only for GM_Point, GM_LineString or GM_Polygon or MultiPrimitives or GM_Aggregate with the same geometry");
150: System.out.println(" ## Have tried to read : "
151: + geom.getClass().getName());
152: }
153:
154: // init theme
155: theme = new Theme(getLayer());
156: }
157:
158: /** Layer for use in a Theme. */
159: public Layer getLayer() {
160: return layer;
161: }
162:
163: /** Theme for use in a Viewer. */
164: public Theme getTheme() {
165: return theme;
166: }
167:
168: /** Returns a Theme shaded by an attribute. */
169: public Theme getTheme(Shader shader, String shadedBy) {
170: if (shader instanceof HSVShader) {
171: InitHSVShader hsvShader = new InitHSVShader(shader,
172: shadedBy);
173: hsvShader.start();
174: try {
175: hsvShader.join();
176: theme.setShader(hsvShader.getShader());
177: } catch (Exception e) {
178: e.printStackTrace();
179: }
180: return theme;
181:
182: } else if (shader instanceof UniqueShader) {
183: InitUniqueShader uShader = new InitUniqueShader(shader,
184: shadedBy);
185: uShader.start();
186: try {
187: uShader.join();
188: theme.setShader(uShader.getShader());
189: } catch (Exception e) {
190: e.printStackTrace();
191: }
192:
193: return theme;
194: }
195:
196: return theme;
197: }
198:
199: /** Refresh the feature. The feature must belong to the collection.*/
200: public void refreshFeature(FT_Feature feature) {
201: ShapeLayer shLayer = (ShapeLayer) layer;
202: GM_Object geom = feature.getGeom();
203: if (shLayer.getGeoShape(feature.hashCode()) == null) { // new shape
204: addGeoShape(feature, geom);
205: } else {
206: GeoShape shape = shLayer.getGeoShape(feature.hashCode());
207: shLayer.removeGeoShape(shape);
208: addGeoShape(feature, geom);
209: }
210: }
211:
212: /** Reads the feature information from the FT_FeatureCollection and produces a PointLayer for use in a Theme. */
213: private Layer readPoints() {
214: PointLayer layer = new PointLayer(false);
215: coll.initIterator();
216: while (coll.hasNext()) {
217: FT_Feature feat = coll.next();
218: GM_Object geom = feat.getGeom();
219: if (geom != null) {
220: if (GM_Point.class.isAssignableFrom(geom.getClass())) {
221: layer.addGeoPoint(geOxygenePointToGeoPoint(feat
222: .hashCode(), geom));
223: } else if (GM_MultiPoint.class.isAssignableFrom(geom
224: .getClass())
225: || GM_Aggregate.class.isAssignableFrom(geom
226: .getClass())) {
227: GM_Aggregate aggr = (GM_Aggregate) geom;
228: aggr.initIterator();
229: while (aggr.hasNext())
230: layer.addGeoPoint(geOxygenePointToGeoPoint(feat
231: .hashCode(), aggr.next()));
232: }
233: }
234: }
235: return layer;
236: }
237:
238: /** Reads the feature information from the FT_FeatureCollection and produces a LineLayer for use in a Theme. */
239: private Layer readLines() {
240: LineLayer layer = new LineLayer();
241: coll.initIterator();
242: while (coll.hasNext()) {
243: FT_Feature feat = coll.next();
244: GM_Object geom = feat.getGeom();
245: if (geom != null) {
246: if (GM_LineString.class.isAssignableFrom(geom
247: .getClass())) {
248: layer.addGeoLine(geOxygeneLineStringToGeoLine(feat
249: .hashCode(), geom));
250: } else if (GM_MultiCurve.class.isAssignableFrom(geom
251: .getClass())
252: || GM_Aggregate.class.isAssignableFrom(geom
253: .getClass())) {
254: GM_Aggregate aggr = (GM_Aggregate) geom;
255: aggr.initIterator();
256: while (aggr.hasNext())
257: layer.addGeoLine(geOxygeneLineStringToGeoLine(
258: feat.hashCode(), aggr.next()));
259: }
260: }
261: }
262: return layer;
263: }
264:
265: /** Reads the feature information from the FT_FeatureCollection and produces a PolygonLayer for use in a Theme. */
266: private Layer readPolygons() {
267: PolygonLayer layer = new PolygonLayer();
268: coll.initIterator();
269: while (coll.hasNext()) {
270: FT_Feature feat = coll.next();
271: GM_Object geom = feat.getGeom();
272: if (geom != null) {
273: if (GM_Polygon.class.isAssignableFrom(geom.getClass())) {
274: GM_Polygon poly = (GM_Polygon) geom;
275: GM_Polygon ext = new GM_Polygon(poly.getExterior());
276: layer.addGeoPolygon(geOxygenePolygonToGeoPolygon(
277: feat.hashCode(), ext));
278: for (int i = 0; i < poly.sizeInterior(); i++) {
279: GM_Polygon inter = new GM_Polygon(poly
280: .getInterior(i));
281: layer
282: .addGeoPolygon(geOxygenePolygonToGeoPolygon(
283: feat.hashCode(), inter));
284: }
285: } else if (GM_MultiSurface.class.isAssignableFrom(geom
286: .getClass())
287: || GM_Aggregate.class.isAssignableFrom(geom
288: .getClass())) {
289: GM_Aggregate aggr = (GM_Aggregate) geom;
290: aggr.initIterator();
291: while (aggr.hasNext()) {
292: GM_Polygon poly = (GM_Polygon) aggr.next();
293: GM_Polygon ext = new GM_Polygon(poly
294: .getExterior());
295: layer
296: .addGeoPolygon(geOxygenePolygonToGeoPolygon(
297: feat.hashCode(), ext));
298: for (int i = 0; i < poly.sizeInterior(); i++) {
299: GM_Polygon inter = new GM_Polygon(poly
300: .getInterior(i));
301: layer
302: .addGeoPolygon(geOxygenePolygonToGeoPolygon(
303: feat.hashCode(), inter));
304: }
305: }
306: }
307: }
308: }
309: return layer;
310: }
311:
312: /** Convert a GM_Point to a GeoPoint */
313: private GeoPoint geOxygenePointToGeoPoint(int id, GM_Object geom) {
314: GM_Point geOxyPoint = (GM_Point) geom;
315: GeoPoint geoPoint = new GeoPoint(id, geOxyPoint.getPosition()
316: .getX(), geOxyPoint.getPosition().getY());
317: return geoPoint;
318: }
319:
320: /** Convert a GM_LineString to a GeoLine */
321: private GeoLine geOxygeneLineStringToGeoLine(int id, GM_Object geom) {
322: GM_LineString geOxyLineString = (GM_LineString) geom;
323: DirectPositionList dpl = geOxyLineString.coord();
324: int nbPts = dpl.size();
325: GeoLine geoLine = new GeoLine(id, dpl.toArrayX(), dpl
326: .toArrayY(), nbPts);
327: return geoLine;
328: }
329:
330: /** Convert a GM_Polygon (single, without holes) to a Polygon */
331: private GeoPolygon geOxygenePolygonToGeoPolygon(int id,
332: GM_Object geom) {
333: GM_Polygon geOxyPolygon = (GM_Polygon) geom;
334: DirectPositionList dpl = geOxyPolygon.coord();
335: int nbPts = dpl.size();
336: GeoPolygon geoPoly = new GeoPolygon(id, dpl.toArrayX(), dpl
337: .toArrayY(), nbPts);
338: return geoPoly;
339: }
340:
341: private void addGeoShape(FT_Feature feature, GM_Object geom) {
342: if (GM_Point.class.isAssignableFrom(geom.getClass())) {
343: ((PointLayer) layer).addGeoPoint(geOxygenePointToGeoPoint(
344: feature.hashCode(), geom));
345: } else if (GM_MultiPoint.class
346: .isAssignableFrom(geom.getClass())) {
347: GM_Aggregate aggr = (GM_Aggregate) geom;
348: aggr.initIterator();
349: while (aggr.hasNext())
350: ((PointLayer) layer)
351: .addGeoPoint(geOxygenePointToGeoPoint(feature
352: .hashCode(), aggr.next()));
353: } else if (GM_LineString.class
354: .isAssignableFrom(geom.getClass())) {
355: ((LineLayer) layer)
356: .addGeoLine(geOxygeneLineStringToGeoLine(feature
357: .hashCode(), geom));
358: } else if (GM_MultiCurve.class
359: .isAssignableFrom(geom.getClass())) {
360: GM_Aggregate aggr = (GM_Aggregate) geom;
361: aggr.initIterator();
362: while (aggr.hasNext())
363: ((LineLayer) layer)
364: .addGeoLine(geOxygeneLineStringToGeoLine(
365: feature.hashCode(), aggr.next()));
366: } else if (GM_Polygon.class.isAssignableFrom(geom.getClass())) {
367: GM_Polygon poly = (GM_Polygon) geom;
368: GM_Polygon ext = new GM_Polygon(poly.getExterior());
369: ((PolygonLayer) layer)
370: .addGeoPolygon(geOxygenePolygonToGeoPolygon(feature
371: .hashCode(), ext));
372: for (int i = 0; i < poly.sizeInterior(); i++) {
373: GM_Polygon inter = new GM_Polygon(poly.getInterior(i));
374: ((PolygonLayer) layer)
375: .addGeoPolygon(geOxygenePolygonToGeoPolygon(
376: feature.hashCode(), inter));
377: }
378: } else if (GM_MultiSurface.class.isAssignableFrom(geom
379: .getClass())) {
380: GM_Aggregate aggr = (GM_Aggregate) geom;
381: aggr.initIterator();
382: while (aggr.hasNext()) {
383: GM_Polygon poly = (GM_Polygon) aggr.next();
384: GM_Polygon ext = new GM_Polygon(poly.getExterior());
385: ((PolygonLayer) layer)
386: .addGeoPolygon(geOxygenePolygonToGeoPolygon(
387: feature.hashCode(), ext));
388: for (int i = 0; i < poly.sizeInterior(); i++) {
389: GM_Polygon inter = new GM_Polygon(poly
390: .getInterior(i));
391: ((PolygonLayer) layer)
392: .addGeoPolygon(geOxygenePolygonToGeoPolygon(
393: feature.hashCode(), inter));
394: }
395: }
396: } else {
397: System.out
398: .println(" ## GeOxygeneReader works only for GM_Point, GM_LineString or GM_Polygon or MultiPrimitives");
399: System.out.println(" ## Have tried to read : "
400: + geom.getClass().getName());
401: return;
402: }
403: }
404:
405: /** Fills geodata objects with all of the fields of the collection. */
406: public Object[] readData() {
407: Vector columnNames = new Vector();
408: Vector rowData = new Vector();
409:
410: // Fill columnNames
411: Field[] fields = getAccessibleFields();
412: int nbFields = fields.length;
413: for (int i = 0; i < nbFields; i++)
414: columnNames.add(fields[i].getName());
415:
416: // Fill rowData
417: coll.initIterator();
418: while (coll.hasNext()) {
419: FT_Feature feature = coll.next();
420: Vector row = new Vector();
421: for (int i = 0; i < nbFields; i++)
422: try {
423: row.add(fields[i].get(feature));
424: } catch (Exception e) {
425: System.out.println(e.getMessage());
426: row.add(null);
427: }
428: rowData.add(row);
429: }
430:
431: return new Vector[] { columnNames, rowData };
432:
433: }
434:
435: /** Fills a geodata objects with this field.
436: * Field type MUST be String ou double. */
437: private GeoData readData(String fieldName) {
438: SimpleGeoData data = new SimpleGeoData();
439: try {
440: Field field = getAccessibleField(fieldName);
441: coll.initIterator();
442: if (field.getType().equals(String.class)) {
443: data.setDataType(GeoData.CHARACTER);
444: while (coll.hasNext()) {
445: FT_Feature feature = coll.next();
446: Object obj = field.get(feature);
447: if (obj != null)
448: data.setText(feature.hashCode(), (String) obj);
449: }
450: } else if (field.getType().equals(double.class)) {
451: data.setDataType(GeoData.FLOATING);
452: while (coll.hasNext()) {
453: FT_Feature feature = coll.next();
454: Object obj = field.get(feature);
455: if (obj != null)
456: data.setValue(feature.hashCode(),
457: ((Double) obj).doubleValue());
458: }
459: } else if (field.getType().equals(int.class)) {
460: data.setDataType(GeoData.FLOATING);
461: while (coll.hasNext()) {
462: FT_Feature feature = coll.next();
463: Object obj = field.get(feature);
464: if (obj != null)
465: data.setValue(feature.hashCode(),
466: ((Integer) obj).doubleValue());
467: }
468: } else {
469: System.out
470: .println("readData() : works only for String or double or int "
471: + fieldName);
472: return data;
473: }
474: } catch (Exception e) {
475: e.printStackTrace();
476: }
477: return data;
478:
479: }
480:
481: /** Returns all fields name. */
482: public String[] getFieldsNames() {
483: Field[] fields = ObjectBrowser.getAccessibleFields(
484: featureClass, SHOW_PUBLIC_ATTRIBUTES,
485: SHOW_PROTECTED_ATTRIBUTES);
486: String[] strings = new String[fields.length];
487: for (int i = 0; i < strings.length; i++)
488: strings[i] = fields[i].getName();
489: return strings;
490: }
491:
492: /** Returns all possible values for a litteral field. */
493: private String[] getPossibleValues(String fieldName) {
494: Set values = new HashSet();
495: Field field = getAccessibleField(fieldName);
496: coll.initIterator();
497: while (coll.hasNext()) {
498: FT_Feature feature = coll.next();
499: try {
500: values.add(field.get(feature));
501: } catch (Exception e) {
502: e.printStackTrace();
503: }
504: }
505: String[] result = new String[values.size()];
506: Iterator it = values.iterator();
507: int i = 0;
508: while (it.hasNext()) {
509: result[i] = (String) it.next();
510: i++;
511: }
512: return result;
513:
514: }
515:
516: public Class getFieldType(String fieldName) {
517: return getAccessibleField(fieldName).getType();
518: }
519:
520: private Field getAccessibleField(String fieldName) {
521: Field[] fields = getAccessibleFields();
522: for (int i = 0; i < fields.length; i++)
523: if (fields[i].getName().equals(fieldName))
524: return fields[i];
525: System.out.println("No field found : " + fieldName + " class "
526: + featureClass);
527: return null;
528: }
529:
530: private Field[] getAccessibleFields() {
531: return ObjectBrowser.getAccessibleFields(featureClass,
532: SHOW_PUBLIC_ATTRIBUTES, SHOW_PROTECTED_ATTRIBUTES);
533: }
534:
535: private Color getRandomColor() {
536: return new Color((float) Math.random(), (float) Math.random(),
537: (float) Math.random());
538: }
539:
540: public FT_FeatureCollection getFeatureCollection() {
541: return coll;
542: }
543:
544: public FT_Feature getFeatureById(int id) {
545: coll.initIterator();
546: while (coll.hasNext()) {
547: FT_Feature f = coll.next();
548: if (f.hashCode() == id)
549: return f;
550: }
551: System.out.println("### No feature found - id = " + id);
552: return null;
553: }
554:
555: class InitUniqueShader extends Thread {
556: Shader shader;
557: String shadedBy;
558:
559: public InitUniqueShader(Shader sh, String field) {
560: shader = sh;
561: shadedBy = field;
562: }
563:
564: public void run() {
565: UniqueShader uShader = (UniqueShader) shader;
566: Field field = getAccessibleField(shadedBy);
567: String[] values = getPossibleValues(shadedBy);
568: int n = values.length;
569: Color[] colors = new Color[n];
570: for (int i = 0; i < n; i++)
571: colors[i] = getRandomColor();
572: coll.initIterator();
573: while (coll.hasNext()) {
574: FT_Feature feature = coll.next();
575: String value = null;
576: try {
577: value = (String) field.get(feature);
578: } catch (Exception e) {
579: e.printStackTrace();
580: }
581: for (int i = 0; i < n; i++) {
582: if (value == null) {
583: uShader
584: .setColor(
585: feature.hashCode(),
586: ObjectViewerThemeProperties.DEFAULT_MISSING_VALUE_SHADER_COLOR);
587: break;
588: }
589: if (value.equals(values[i])) {
590: uShader.setColor(feature.hashCode(), colors[i]);
591: break;
592: }
593:
594: }
595: }
596: }
597:
598: public Shader getShader() {
599: return shader;
600: }
601: }
602:
603: class InitHSVShader extends Thread {
604: Shader shader;
605: String shadedBy;
606:
607: public InitHSVShader(Shader sh, String field) {
608: shader = sh;
609: shadedBy = field;
610: }
611:
612: public void run() {
613: SimpleGeoData data = (SimpleGeoData) readData(shadedBy);
614: theme.setGeoData(data);
615: shader.setRange(data);
616:
617: }
618:
619: public Shader getShader() {
620: return shader;
621: }
622: }
623:
624: }
|