001: /*
002: * uDig - User Friendly Desktop Internet GIS client
003: * http://udig.refractions.net
004: * (C) 2004, Refractions Research Inc.
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: */
017: package net.refractions.udig.style.sld;
018:
019: import java.awt.Color;
020: import java.io.File;
021: import java.io.IOException;
022: import java.io.StringReader;
023: import java.net.URL;
024: import java.util.Random;
025:
026: import javax.xml.transform.TransformerException;
027:
028: import net.refractions.udig.catalog.IGeoResource;
029: import net.refractions.udig.catalog.IService;
030: import net.refractions.udig.project.ILayer;
031: import net.refractions.udig.project.StyleContent;
032: import net.refractions.udig.project.internal.StyleBlackboard;
033: import net.refractions.udig.ui.graphics.SLDs;
034:
035: import org.eclipse.core.runtime.IProgressMonitor;
036: import org.eclipse.ui.IMemento;
037: import org.geotools.data.FeatureSource;
038: import org.geotools.data.shapefile.ShapefileDataStore;
039: import org.geotools.feature.FeatureType;
040: import org.geotools.feature.GeometryAttributeType;
041: import org.geotools.filter.CompareFilter;
042: import org.geotools.filter.Expression;
043: import org.geotools.filter.FilterFactory;
044: import org.geotools.filter.FilterFactoryFinder;
045: import org.geotools.filter.FilterType;
046: import org.geotools.filter.IllegalFilterException;
047: import org.geotools.filter.function.FilterFunction_geometryType;
048: import org.geotools.styling.FeatureTypeConstraint;
049: import org.geotools.styling.FeatureTypeStyle;
050: import org.geotools.styling.Fill;
051: import org.geotools.styling.LineSymbolizer;
052: import org.geotools.styling.Mark;
053: import org.geotools.styling.PointSymbolizer;
054: import org.geotools.styling.PolygonSymbolizer;
055: import org.geotools.styling.RasterSymbolizer;
056: import org.geotools.styling.Rule;
057: import org.geotools.styling.SLDParser;
058: import org.geotools.styling.SLDTransformer;
059: import org.geotools.styling.Stroke;
060: import org.geotools.styling.Style;
061: import org.geotools.styling.StyleBuilder;
062: import org.geotools.styling.StyleFactory;
063: import org.geotools.styling.StyleFactoryFinder;
064: import org.geotools.styling.StyledLayerDescriptor;
065: import org.geotools.styling.Symbolizer;
066: import org.geotools.styling.TextSymbolizer;
067: import org.geotools.styling.UserLayer;
068:
069: import com.vividsolutions.jts.geom.LineString;
070: import com.vividsolutions.jts.geom.LinearRing;
071: import com.vividsolutions.jts.geom.MultiLineString;
072: import com.vividsolutions.jts.geom.MultiPoint;
073: import com.vividsolutions.jts.geom.MultiPolygon;
074: import com.vividsolutions.jts.geom.Point;
075: import com.vividsolutions.jts.geom.Polygon;
076:
077: /**
078: * Style content for Style Layer Descriptor (SLD).
079: * This class is not intended for extension (jg: so I marked it final).
080: *
081: * @author Justin Deoliveira, Refractions Research Inc.
082: */
083: public final class SLDContent extends StyleContent {
084:
085: /** style id, used to identify sld style on a blackboard * */
086: public static final String ID = "net.refractions.udig.style.sld"; //$NON-NLS-1$
087:
088: /** factory used to create style and builder * */
089: private static StyleFactory styleFactory = StyleFactoryFinder
090: .createStyleFactory();
091:
092: /** factory used to create style content * */
093: private static StyleBuilder styleBuilder = new StyleBuilder(
094: styleFactory);
095:
096: /** random number generator used to create random colors * */
097: private static Random random = new Random();
098:
099: /**
100: * SLDContent constructor.
101: */
102: public SLDContent() {
103: super (ID);
104: }
105:
106: /*
107: * (non-Javadoc)
108: *
109: * @see net.refractions.udig.project.StyleContent#getStyleClass()
110: */
111: public Class getStyleClass() {
112: return Style.class;
113: }
114:
115: /*
116: * (non-Javadoc)
117: *
118: * @see net.refractions.udig.project.StyleContent#save(org.eclipse.ui.IMemento,
119: * java.lang.Object)
120: */
121: public void save(IMemento memento, Object value) {
122: Style style = (Style) value;
123:
124: // serialize out the style objects
125: SLDTransformer sldWriter = new SLDTransformer();
126: String out = ""; //$NON-NLS-1$
127: try {
128: out = sldWriter.transform(style);
129: } catch (TransformerException e) {
130: SLDPlugin.log("SLDTransformer failed", e); //$NON-NLS-1$
131: e.printStackTrace();
132: } catch (Exception e) {
133: SLDPlugin.log("SLDTransformer failed", e); //$NON-NLS-1$
134: }
135: memento.putTextData(out);
136: memento.putString("type", "SLDStyle"); //$NON-NLS-1$ //$NON-NLS-2$
137: memento.putString("version", "1.0"); //$NON-NLS-1$ //$NON-NLS-2$
138: }
139:
140: /*
141: * (non-Javadoc)
142: *
143: * @see net.refractions.udig.project.StyleContent#load(org.eclipse.ui.IMemento)
144: */
145: public Object load(IMemento momento) {
146: // parse the sld object
147: if (momento.getTextData() == null)
148: return null;
149: StringReader reader = new StringReader(momento.getTextData());
150: SLDParser sldParser = new SLDParser(getStyleFactory(), reader);
151:
152: Style[] parsed = sldParser.readXML();
153: if (parsed != null && parsed.length > 0)
154: return parsed[0];
155:
156: return null;
157: }
158:
159: /*
160: * (non-Javadoc)
161: *
162: * @see net.refractions.udig.project.StyleContent#load(java.net.URL)
163: */
164: public Object load(URL url, IProgressMonitor m) throws IOException {
165: return parse(url);
166: }
167:
168: public static void apply(ILayer layer, Style style,
169: IProgressMonitor m) throws IOException {
170:
171: if (layer == null)
172: return;
173: if (style == null)
174: return;
175:
176: if (layer.hasResource(FeatureSource.class)) {
177: IGeoResource resource = layer
178: .getGeoResource(FeatureSource.class);
179: FeatureSource featureSource = resource.resolve(
180: FeatureSource.class, m);
181:
182: if (featureSource != null) {
183: //match up the feature type style name and the feature type name
184: FeatureType type = featureSource.getSchema();
185: FeatureTypeStyle fstyle = SLDs.featureTypeStyle(style,
186: type);
187: if (fstyle == null) {
188: //force a name match
189: FeatureTypeStyle[] fstyles = style
190: .getFeatureTypeStyles();
191: if (fstyles != null && fstyles.length > 0) {
192: fstyle = fstyles[0];
193: }
194: }
195:
196: if (fstyle != null) {
197: fstyle.setName(type.getTypeName());
198: StyleBlackboard styleBlackboard = (StyleBlackboard) layer
199: .getStyleBlackboard();
200: styleBlackboard.put(SLDContent.ID, style);
201: styleBlackboard
202: .setSelected(new String[] { SLDContent.ID });
203:
204: // //force a rerender, TODO: blackboard events
205: // layer.getMap().getRenderManager().refresh(
206: // layer, resource.getInfo(m).getBounds()
207: // );
208: }
209: }
210: }
211: }
212:
213: /**
214: * This will need to know the "scheme."
215: */
216: public Object createDefaultStyle(IGeoResource resource,
217: Color colour, IProgressMonitor m) throws IOException {
218:
219: if (!resource.canResolve(FeatureSource.class))
220: return null;
221:
222: FeatureSource featureSource = null;
223: try {
224: featureSource = resource.resolve(FeatureSource.class, m);
225: } catch (IOException e) {
226: e.printStackTrace();
227: return null;
228: }
229:
230: if (featureSource == null) {
231: return null;
232: }
233:
234: FeatureType schema = featureSource.getSchema();
235: GeometryAttributeType geom = schema.getDefaultGeometry();
236: if (geom == null)
237: return null;
238:
239: Style style = null;
240:
241: // do a check for shapefiles, there may be a parallel sld
242: IService service = resource.service(m);
243: if (service.canResolve(ShapefileDataStore.class)) {
244: URL url = service.getIdentifier();
245: String shp = url.getFile();
246:
247: //strip off the extension and check for sld
248: String sld = shp.substring(0, shp.length() - 4) + ".sld"; //$NON-NLS-1$
249: File f = new File(sld);
250: if (!f.exists()) {
251: //try upper case
252: sld = shp.substring(0, shp.length() - 4) + ".SLD"; //$NON-NLS-1$
253: f = new File(sld);
254: }
255:
256: if (f.exists()) {
257: //parse it up
258: SLDParser parser = new SLDParser(styleFactory);
259: parser.setInput(f);
260: Style[] styles = parser.readXML();
261:
262: //put the first one on
263: if (styles != null && styles.length > 0) {
264: style = SLDs.matchingStyle(styles, schema);
265: if (style == null) {
266: //just choose the first one
267: style = styles[0];
268: }
269: }
270: }
271: }
272:
273: SLDContentManager sldContentManager;
274:
275: if (style == null) {
276: // fall back to create some default
277: style = styleBuilder.createStyle();
278: sldContentManager = new SLDContentManager(styleBuilder,
279: style);
280:
281: // initialize the symbolizers based on geometry type
282: if (geom != null) {
283: if (SLD.isLine(geom)) {
284: sldContentManager
285: .addSymbolizer(createLineSymbolizer(colour));
286: } else if (SLD.isPoint(geom)) {
287: sldContentManager
288: .addSymbolizer(createPointSymbolizer(colour));
289: } else if (SLD.isPolygon(geom)) {
290: PolygonSymbolizer symbol = createPolygonSymbolizer(colour);
291: sldContentManager.addSymbolizer(symbol);
292: } else {
293: try {
294: createGeometrySLD(colour, schema
295: .getDefaultGeometry().getName(),
296: sldContentManager);
297: } catch (Exception e) {
298: SLDPlugin.log(
299: "Failed to create geometry SLD", e); //$NON-NLS-1$
300: sldContentManager.addSymbolizer(styleBuilder
301: .createLineSymbolizer());
302: }
303: }
304: }
305: } else {
306: sldContentManager = new SLDContentManager(styleBuilder,
307: style);
308: }
309:
310: //set the feature type name
311: FeatureTypeStyle fts = sldContentManager
312: .getDefaultFeatureTypeStyle();
313: fts.setFeatureTypeName(schema.getTypeName());
314: fts.setName("simple"); //$NON-NLS-1$
315: fts.setSemanticTypeIdentifiers(new String[] {
316: "generic:geometry", "simple" }); //$NON-NLS-1$ //$NON-NLS-2$
317:
318: return style;
319: }
320:
321: private void createGeometrySLD(Color colour, String geomXPath,
322: SLDContentManager sldContentManager)
323: throws IllegalFilterException {
324: // create Point rule.
325: Rule rule = sldContentManager.getDefaultRule();
326: CompareFilter filter = createGeometryFunctionFilter(geomXPath,
327: Point.class.getSimpleName());
328: rule.setFilter(filter);
329: rule
330: .setSymbolizers(new Symbolizer[] { createPointSymbolizer(colour) });
331:
332: // create MultiPoint rule
333: rule = sldContentManager.createRule();
334: filter = createGeometryFunctionFilter(geomXPath,
335: MultiPoint.class.getSimpleName());
336: rule.setFilter(filter);
337: rule
338: .setSymbolizers(new Symbolizer[] { createPointSymbolizer(colour) });
339: sldContentManager.getDefaultFeatureTypeStyle().addRule(rule);
340:
341: // create LineString rule
342: rule = sldContentManager.createRule();
343: filter = createGeometryFunctionFilter(geomXPath,
344: LineString.class.getSimpleName());
345: rule.setFilter(filter);
346: rule
347: .setSymbolizers(new Symbolizer[] { createLineSymbolizer(colour) });
348: sldContentManager.getDefaultFeatureTypeStyle().addRule(rule);
349:
350: // create LinearRing rule
351: rule = sldContentManager.createRule();
352: filter = createGeometryFunctionFilter(geomXPath,
353: LinearRing.class.getSimpleName());
354: rule.setFilter(filter);
355: rule
356: .setSymbolizers(new Symbolizer[] { createLineSymbolizer(colour) });
357: sldContentManager.getDefaultFeatureTypeStyle().addRule(rule);
358:
359: // create MultiLineString rule
360: rule = sldContentManager.createRule();
361: filter = createGeometryFunctionFilter(geomXPath,
362: MultiLineString.class.getSimpleName());
363: rule.setFilter(filter);
364: rule
365: .setSymbolizers(new Symbolizer[] { createLineSymbolizer(colour) });
366: sldContentManager.getDefaultFeatureTypeStyle().addRule(rule);
367:
368: // create Polygon rule
369: rule = sldContentManager.createRule();
370: filter = createGeometryFunctionFilter(geomXPath, Polygon.class
371: .getSimpleName());
372: rule.setFilter(filter);
373: rule
374: .setSymbolizers(new Symbolizer[] { createPolygonSymbolizer(colour) });
375: sldContentManager.getDefaultFeatureTypeStyle().addRule(rule);
376:
377: // create MultiPolygon rule
378: rule = sldContentManager.createRule();
379: filter = createGeometryFunctionFilter(geomXPath,
380: MultiPolygon.class.getSimpleName());
381: rule.setFilter(filter);
382: rule
383: .setSymbolizers(new Symbolizer[] { createPolygonSymbolizer(colour) });
384: sldContentManager.getDefaultFeatureTypeStyle().addRule(rule);
385:
386: }
387:
388: private CompareFilter createGeometryFunctionFilter(
389: String geomXPath, Object geometryClassSimpleName)
390: throws IllegalFilterException {
391: FilterFactory factory = FilterFactoryFinder
392: .createFilterFactory();
393: FilterFunction_geometryType geomTypeExpr = new FilterFunction_geometryType();
394: geomTypeExpr.setArgs(new Expression[] { factory
395: .createAttributeExpression(geomXPath) });
396:
397: CompareFilter filter = factory
398: .createCompareFilter(FilterType.COMPARE_EQUALS);
399: filter.addLeftValue(geomTypeExpr);
400: filter.addRightValue(factory
401: .createLiteralExpression(geometryClassSimpleName));
402: return filter;
403: }
404:
405: public static Style parse(URL url) throws IOException {
406: StyleFactory factory = StyleFactoryFinder.createStyleFactory();
407: SLDParser styleReader = new SLDParser(factory, url);
408: Style style = styleReader.readXML()[0];
409:
410: return style;
411: }
412:
413: public static StyledLayerDescriptor createDefaultStyledLayerDescriptor() {
414: StyledLayerDescriptor sld = styleFactory
415: .createStyledLayerDescriptor();
416: return sld;
417: }
418:
419: /**
420: * Creates an SLD and UserLayer, and nests the style (SLD-->UserLayer-->Style).
421: *
422: * @see net.refractions.project.internal.render.SelectionStyleContent#createDefaultStyledLayerDescriptor
423: * @param style
424: * @return SLD
425: */
426: public static StyledLayerDescriptor createDefaultStyledLayerDescriptor(
427: Style style) {
428: StyledLayerDescriptor sld = createDefaultStyledLayerDescriptor();
429: UserLayer layer = styleFactory.createUserLayer();
430: //FeatureTypeConstraint ftc = styleFactory.createFeatureTypeConstraint(null, Filter.NONE, null);
431: layer
432: .setLayerFeatureConstraints(new FeatureTypeConstraint[] { null });
433: sld.addStyledLayer(layer);
434: layer.addUserStyle(style);
435: return sld;
436: }
437:
438: public static Style createDefaultStyle() {
439: Style style = styleBuilder.createStyle();
440: SLDContentManager sldContentManager = new SLDContentManager(
441: styleBuilder, style);
442:
443: // sldContentManager.addSymbolizer(styleBuilder.createPointSymbolizer());
444: sldContentManager
445: .addSymbolizer(createLineSymbolizer(createRandomColor()));
446: sldContentManager
447: .addSymbolizer(createPolygonSymbolizer(createRandomColor()));
448: sldContentManager.addSymbolizer(createTextSymbolizer());
449: // sldContentManager.addSymbolizer(styleBuilder.createRasterSymbolizer());
450:
451: //tag as a simple FeatureTypeStyle
452: FeatureTypeStyle fts = style.getFeatureTypeStyles()[0];
453: fts.setName("simple"); //$NON-NLS-1$
454: fts.setSemanticTypeIdentifiers(new String[] {
455: "generic:geometry", "simple" }); //$NON-NLS-1$ //$NON-NLS-2$
456:
457: //TODO: add StyledLayerDescriptor to sldContentManager?
458: return style;
459: }
460:
461: public static StyleFactory getStyleFactory() {
462: return styleFactory;
463: }
464:
465: public static StyleBuilder getStyleBuilder() {
466: return styleBuilder;
467: }
468:
469: protected static PointSymbolizer createPointSymbolizer(Color colour) {
470: PointSymbolizer symb = styleBuilder.createPointSymbolizer();
471: Fill fill = styleBuilder.createFill(colour, 1.0);
472: Stroke outline = styleBuilder.createStroke(Color.BLACK, 1, 1);
473: symb.getGraphic().setMarks(
474: new Mark[] { styleBuilder.createMark(
475: StyleBuilder.MARK_SQUARE, fill, outline) });
476:
477: return symb;
478: }
479:
480: /**
481: * Creates a simple LineSymbolizer using the specified colour.
482: *
483: * @author Pati
484: * @param colour
485: * @return LineSymbolizer
486: */
487: protected static LineSymbolizer createLineSymbolizer(Color colour) {
488: if (colour == null) {
489: colour = createRandomColor();
490: }
491: Stroke stroke = styleBuilder.createStroke();
492: stroke.setColor(styleBuilder.colorExpression(colour));
493: stroke.setWidth(styleBuilder.literalExpression(1));
494:
495: LineSymbolizer symbolizer = styleBuilder
496: .createLineSymbolizer(stroke);
497:
498: return symbolizer;
499: }
500:
501: /**
502: * Creates a simple PolygonSymbolizer using the specified colour.
503: *
504: * @author Pati
505: * @param colour
506: * @return LineSymbolizer
507: */
508: protected static PolygonSymbolizer createPolygonSymbolizer(
509: Color colour) {
510: if (colour == null) {
511: colour = createRandomColor();
512: }
513:
514: Stroke stroke = styleBuilder.createStroke();
515: stroke.setColor(styleBuilder.colorExpression(colour));
516: stroke.setWidth(styleBuilder.literalExpression(1));
517:
518: Fill fill = styleBuilder.createFill();
519: fill.setColor(styleBuilder.colorExpression(colour));
520: fill.setOpacity(styleBuilder.literalExpression(.5));
521:
522: PolygonSymbolizer symbolizer = styleBuilder
523: .createPolygonSymbolizer(stroke, fill);
524:
525: return symbolizer;
526: }
527:
528: protected static TextSymbolizer createTextSymbolizer() {
529: TextSymbolizer symbolizer = styleBuilder.createTextSymbolizer();
530: return symbolizer;
531: }
532:
533: protected static RasterSymbolizer createRasterSymbolizer() {
534: RasterSymbolizer symbolizer = styleBuilder
535: .createRasterSymbolizer();
536: return symbolizer;
537: }
538:
539: protected static Color createRandomColor() {
540: return new Color(random.nextInt(200), random.nextInt(200),
541: random.nextInt(200));
542: }
543:
544: }
|