001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/tools/wms/WFS2WMS.java $
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005       Department of Geography, University of Bonn
006     and
007       lat/lon GmbH
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035    ----------------------------------------------------------------------------*/
036    package org.deegree.tools.wms;
037    
038    import static org.deegree.i18n.Messages.getMessage;
039    
040    import java.io.File;
041    import java.io.FileOutputStream;
042    import java.net.URL;
043    import java.util.ArrayList;
044    import java.util.HashMap;
045    import java.util.List;
046    import java.util.Map;
047    import java.util.Properties;
048    
049    import org.deegree.datatypes.QualifiedName;
050    import org.deegree.framework.log.ILogger;
051    import org.deegree.framework.log.LoggerFactory;
052    import org.deegree.framework.util.ColorUtils;
053    import org.deegree.framework.xml.NamespaceContext;
054    import org.deegree.framework.xml.XMLFragment;
055    import org.deegree.framework.xml.XMLParsingException;
056    import org.deegree.framework.xml.XMLTools;
057    import org.deegree.framework.xml.XSLTDocument;
058    import org.deegree.graphics.sld.AbstractLayer;
059    import org.deegree.graphics.sld.NamedLayer;
060    import org.deegree.graphics.sld.StyleFactory;
061    import org.deegree.graphics.sld.StyledLayerDescriptor;
062    import org.deegree.graphics.sld.UserStyle;
063    import org.deegree.ogcbase.CommonNamespaces;
064    import org.deegree.ogcwebservices.getcapabilities.InvalidCapabilitiesException;
065    import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilities;
066    import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilitiesDocument;
067    import org.deegree.ogcwebservices.wfs.capabilities.WFSFeatureType;
068    import org.w3c.dom.Node;
069    
070    /**
071     * Creates a deegree WMS configuration document or a Layer section from a WFS capabilities document.
072     * The datasource type for each layer will be LOCALWFS. Also a style with random color(s) will be
073     * created, assigned to the layers and stored in a xml document named $OUTFILE_BASE$_styles.xml.
074     * TODO support for usage of an already existing WMS configuration document TODO determine geometry
075     * types of the feature types registered within the wfs capabilities document
076     *
077     *
078     * @version $Revision: 18195 $
079     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
080     * @author last edited by: $Author: mschneider $
081     *
082     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
083     *
084     */
085    public class WFS2WMS {
086    
087        private static NamespaceContext nsc = CommonNamespaces.getNamespaceContext();
088    
089        private ILogger LOG = LoggerFactory.getLogger( WFS2WMS.class );
090    
091        private URL xsl = WFS2WMS.class.getResource( "wfs2wms.xsl" );
092    
093        private Properties prop = null;
094    
095        /**
096         * Create an instance with given properties
097         *
098         * @param prop
099         *            to use
100         */
101        public WFS2WMS( Properties prop ) {
102            this.prop = prop;
103        }
104    
105        /**
106         * @param node
107         *            to retrieve the minimum x value from.
108         * @return the minimum y value or -180 if not found
109         */
110        public static double getMinX( Node node ) {
111            double v = -180;
112            try {
113                String s = XMLTools.getNodeAsString( node, ".", nsc, "-180 -90" );
114                String[] t = s.split( " " );
115                v = Double.parseDouble( t[0] );
116            } catch ( XMLParsingException e ) {
117                e.printStackTrace();
118            }
119            return v;
120        }
121    
122        /**
123         * @param node
124         *            to retrieve the minimum y value from.
125         * @return the maximum y value or -90 if not found
126         */
127        public static double getMinY( Node node ) {
128            double v = -90;
129            try {
130                String s = XMLTools.getNodeAsString( node, ".", nsc, "-180 -90" );
131                String[] t = s.split( " " );
132                v = Double.parseDouble( t[1] );
133            } catch ( XMLParsingException e ) {
134                e.printStackTrace();
135            }
136            return v;
137        }
138    
139        /**
140         * @param node
141         *            to retrieve the maximum x value from.
142         * @return the maximum x value or 180 if not found
143         */
144        public static double getMaxX( Node node ) {
145            double v = 180;
146            try {
147                String s = XMLTools.getNodeAsString( node, ".", nsc, "180 90" );
148                String[] t = s.split( " " );
149                v = Double.parseDouble( t[0] );
150            } catch ( XMLParsingException e ) {
151                e.printStackTrace();
152            }
153            return v;
154        }
155    
156        /**
157         * @param node
158         *            to retrieve the maximum y value from.
159         * @return the maximum y value or 90 if not found
160         */
161        public static double getMaxY( Node node ) {
162            double v = 90;
163            try {
164                String s = XMLTools.getNodeAsString( node, ".", nsc, "180 90" );
165                String[] t = s.split( " " );
166                v = Double.parseDouble( t[1] );
167            } catch ( XMLParsingException e ) {
168                e.printStackTrace();
169            }
170            return v;
171        }
172    
173        /**
174         * append all required namespace definition to the root element
175         *
176         * @param xml
177         * @return
178         * @throws InvalidCapabilitiesException
179         */
180        private XMLFragment addNamespaces( XMLFragment wms, WFSCapabilities capa ) {
181            WFSFeatureType[] fts = capa.getFeatureTypeList().getFeatureTypes();
182            for ( int i = 0; i < fts.length; i++ ) {
183                QualifiedName qn = fts[i].getName();
184                XMLTools.appendNSBinding( wms.getRootElement(), qn.getPrefix(), qn.getNamespace() );
185            }
186            return wms;
187        }
188    
189        /**
190         * creates a style for each feature type registered in a WFS capabilities document
191         *
192         * @param wfs
193         * @return
194         */
195        private List<UserStyle> createStyles( WFSCapabilities capa ) {
196            List<UserStyle> styles = new ArrayList<UserStyle>();
197            Map<QualifiedName, Integer> types = determineGeometryType( capa );
198    
199            WFSFeatureType[] fts = capa.getFeatureTypeList().getFeatureTypes();
200    
201            UserStyle st = null;
202            for ( int i = 0; i < fts.length; i++ ) {
203                QualifiedName qn = fts[i].getName();
204                int type = types.get( qn );
205                switch ( type ) {
206                case 1: {
207                    st = (UserStyle) StyleFactory.createPointStyle( "square", ColorUtils.getRandomColor( false ),
208                                                                    ColorUtils.getRandomColor( false ), 1, 1, 10, 10, 0,
209                                                                    Double.MAX_VALUE );
210                    break;
211                }
212                case 2: {
213                    st = (UserStyle) StyleFactory.createLineStyle( ColorUtils.getRandomColor( false ), 4, 1, 0,
214                                                                   Double.MAX_VALUE );
215                    break;
216                }
217    
218                case 3: {
219                    st = (UserStyle) StyleFactory.createPolygonStyle( ColorUtils.getRandomColor( false ), 1,
220                                                                      ColorUtils.getRandomColor( false ), 1, 1, 0,
221                                                                      Double.MAX_VALUE );
222                    break;
223                }
224                }
225                st.setName( "default:" + qn.getPrefixedName() );
226                styles.add( st );
227            }
228    
229            return styles;
230        }
231    
232        /**
233         *
234         * @param styles
235         * @return
236         */
237        private StyledLayerDescriptor createSLD( List<UserStyle> styles ) {
238    
239            UserStyle[] us = styles.toArray( new UserStyle[styles.size()] );
240            NamedLayer nl = new NamedLayer( "defaultstyle", null, us );
241            return new StyledLayerDescriptor( new AbstractLayer[] { nl }, "1.0.0" );
242    
243        }
244    
245        /**
246         * returns the geometry type of each feature type registered within the passed WFS capabilities
247         * document<br>
248         * <ul>
249         * <li>1 = point or multi point
250         * <li>2 = curve or multi curve
251         * <li>3 = surface or multi surface
252         * </ul>
253         *
254         * @param capa
255         * @return
256         */
257        private Map<QualifiedName, Integer> determineGeometryType( WFSCapabilities capa ) {
258    
259            Map<QualifiedName, Integer> types = new HashMap<QualifiedName, Integer>();
260    
261            WFSFeatureType[] fts = capa.getFeatureTypeList().getFeatureTypes();
262            for ( int i = 0; i < fts.length; i++ ) {
263                QualifiedName qn = fts[i].getName();
264                // TODO
265                // get real geometry type
266                types.put( qn, 3 );
267            }
268    
269            return types;
270        }
271    
272        /**
273         * Run the conversion
274         *
275         * @throws Exception
276         */
277        public void run()
278                                throws Exception {
279    
280            String out = prop.getProperty( "-outFile" );
281            File file = new File( out );
282            int pos = file.getName().lastIndexOf( '.' );
283            String styleDoc = file.getName().substring( 0, pos ) + "_styles.xml";
284    
285            HashMap<String, String> param = new HashMap<String, String>();
286            param.put( "PARENTLAYER", prop.getProperty( "-parentLayer" ) );
287            param.put( "MINX", prop.getProperty( "-minx" ) );
288            param.put( "MINY", prop.getProperty( "-miny" ) );
289            param.put( "MAXX", prop.getProperty( "-maxx" ) );
290            param.put( "MAXY", prop.getProperty( "-maxy" ) );
291            param.put( "SRS", prop.getProperty( "-srs" ) );
292            if ( "true".equals( prop.getProperty( "-full" ) ) ) {
293                param.put( "WMSCAPS", "1" );
294            }
295            param.put( "STYLEDOC", styleDoc );
296    
297            LOG.logInfo( "XSLT-parameter: ", param );
298    
299            XSLTDocument outXSLSheet = new XSLTDocument();
300            outXSLSheet.load( xsl );
301    
302            file = new File( prop.getProperty( "-wfsCaps" ) );
303            param.put( "WFSCAPS", file.toURL().toExternalForm() );
304            XMLFragment doc = new XMLFragment();
305            doc.load( file.toURL() );
306    
307            XMLFragment resultDocument = outXSLSheet.transform( doc, null, null, param );
308    
309            WFSCapabilitiesDocument wfsdoc = new WFSCapabilitiesDocument();
310            wfsdoc.setRootElement( doc.getRootElement() );
311            WFSCapabilities capa = (WFSCapabilities) wfsdoc.parseCapabilities();
312    
313            resultDocument = addNamespaces( resultDocument, capa );
314    
315            List<UserStyle> styles = createStyles( capa );
316            StyledLayerDescriptor sld = createSLD( styles );
317    
318            String s = prop.getProperty( "-outFile" ).replace( ".xml", "_styles.xml" );
319            file = new File( s );
320            FileOutputStream fos = new FileOutputStream( file );
321            fos.write( sld.exportAsXML().getBytes() );
322            fos.close();
323    
324            file = new File( prop.getProperty( "-outFile" ) );
325            fos = new FileOutputStream( file );
326            resultDocument.write( fos );
327            fos.close();
328        }
329    
330        private static void validate( Properties map )
331                                throws Exception {
332            if ( map.get( "-parentLayer" ) == null ) {
333                throw new Exception( getMessage( "WFS2WMS.validate_2" ) );
334            }
335            if ( map.get( "-minx" ) != null ) {
336                Double.parseDouble( map.getProperty( "-minx" ) );
337            } else {
338                map.put( "-minx", "-180" );
339            }
340            if ( map.get( "-miny" ) != null ) {
341                Double.parseDouble( map.getProperty( "-miny" ) );
342            } else {
343                map.put( "-miny", "-90" );
344            }
345            if ( map.get( "-maxx" ) != null ) {
346                Double.parseDouble( map.getProperty( "-maxx" ) );
347            } else {
348                map.put( "-maxx", "180" );
349            }
350            if ( map.get( "-maxy" ) != null ) {
351                Double.parseDouble( map.getProperty( "-maxy" ) );
352            } else {
353                map.put( "-maxy", "90" );
354            }
355            if ( map.get( "-srs" ) == null ) {
356                map.put( "-srs", "EPSG:4326" );
357            }
358            if ( map.get( "-wfsCaps" ) == null ) {
359                throw new Exception( getMessage( "WFS2WMS.validate_0" ) );
360            }
361            if ( map.get( "-outFile" ) == null ) {
362                throw new Exception( getMessage( "WFS2WMS.validate_1" ) );
363            }
364        }
365    
366        /**
367         * @param args
368         * @throws Exception
369         *             if the conversion could not be fulfilled
370         */
371        public static void main( String[] args )
372                                throws Exception {
373    
374            Properties map = new Properties();
375            for ( int i = 0; i < args.length; i += 2 ) {
376                System.out.println( args[i + 1] );
377                map.put( args[i], args[i + 1] );
378            }
379    
380            try {
381                validate( map );
382            } catch ( Exception e ) {
383                System.out.println( "!!! E R R O R !!!" );
384                System.out.println( e.getMessage() );
385                System.out.println( "----------------------------------------------------" );
386                System.out.println( getMessage( "WFS2WMS.parentLayer" ) );
387                System.out.println( getMessage( "WFS2WMS.wfsCaps" ) );
388                System.out.println( getMessage( "WFS2WMS.outFile" ) );
389                return;
390            }
391    
392            WFS2WMS wfs2wms = new WFS2WMS( map );
393            wfs2wms.run();
394    
395        }
396    
397    }