Statistics
| Revision:

root / branches / v05 / libraries / libRemoteServices / src / org / gvsig / remoteClient / wms / WMSProtocolHandler.java @ 3906

History | View | Annotate | Download (17 KB)

1
package org.gvsig.remoteClient.wms;
2

    
3
import java.io.ByteArrayInputStream;
4
import java.io.DataInputStream;
5
import java.io.File;
6
import java.io.IOException;
7
import java.io.InputStream;
8
import java.io.InputStreamReader;
9
import java.io.Reader;
10
import java.io.StringReader;
11
import java.net.URL;
12
import java.net.URLConnection;
13
import java.util.ArrayList;
14
import java.util.TreeMap;
15
import java.util.Vector;
16

    
17
import org.gvsig.remoteClient.exceptions.ServerErrorException;
18
import org.gvsig.remoteClient.exceptions.WMSException;
19
import org.gvsig.remoteClient.exceptions.WMSWrongSizeException;
20
import org.gvsig.remoteClient.utils.CapabilitiesTags;
21
import org.gvsig.remoteClient.utils.ExceptionTags;
22
import org.gvsig.remoteClient.utils.Utilities;
23
import org.kxml2.io.KXmlParser;
24
import org.xmlpull.v1.XmlPullParserException;
25

    
26
import com.iver.andami.PluginServices;
27

    
28
/**
29
 * <p> Abstract class that represents handlers to comunicate via WMS protocol.
30
 * </p>
31
 * 
32
 */
33
public abstract class WMSProtocolHandler {
34
        
35
        public static final int GETCAPABILITIES_OPERATION = 0;
36
        public static final int GETMAP_OPERATION = 1;
37
        public static final int GETFEATUREINFO_OPERATION = 2;
38
        public static final int DESCRIBELAYER_OPERATION = 3;
39
        public static final int GETLEGENDGRAPHIC_OPERATION = 4; // this is only for sld-enabled WMS
40
        
41
        /**
42
         * procotol handler name
43
         */
44
    protected String name;
45
    /**
46
     * protocol handler version
47
     */
48
    protected String version;
49
    /**
50
     * host of the WMS to connect
51
     */
52
    protected String host;
53
    /**
54
     *  port number of the comunication channel of the WMS to connect
55
     */
56
    protected String port;    
57
    /**
58
     * WMS metadata
59
     */
60
    protected ServiceInformation serviceInfo;
61
    public TreeMap layers;
62
    public WMSLayer rootLayer;
63
    public Vector srs;
64
    
65
    /**
66
     * parses the data retrieved by the WMS in XML format. 
67
     * It will be mostly the WMS Capabilities, but the implementation
68
     * will be placed in the handler implementing certain version of the protocol.
69
     * 
70
     */
71
    public abstract void parse(File f) ;
72

    
73
    public String getName() {        
74
            return name;
75
    } 
76

    
77
    public String getVersion() {        
78
            return version;
79
    }    
80
    
81
    public ServiceInformation getServiceInformation() {        
82
        return serviceInfo;
83
    }  
84
    public String getHost ()
85
    {
86
            return host;
87
    }
88
    public void setHost(String _host)
89
    {
90
            host = _host;
91
    }
92
    public String getPort()
93
    {
94
            return port;
95
    }
96
    public void setPort(String _port)
97
    {
98
            port = _port;
99
    }
100

    
101

    
102
    /**
103
         * <p>Builds a GetCapabilities request that is sent to the WMS
104
         * the response will be parse to extract the data needed by the
105
         * WMS client</p>
106
         */
107
    public void getCapabilities()
108
    {                
109
            URL request = null;
110
                try
111
                {
112
                        request = new URL(buildCapabilitiesRequest());
113
                }
114
                catch(Exception e)
115
                {
116
                        e.printStackTrace();
117
                }
118
                try
119
                {
120
                File f = com.iver.andami.Utilities.downloadFile(request,"wms_capabilities.xml");
121
            parse(f);
122
            } catch(Exception e)
123
                {
124
                        //TODO
125
                        e.printStackTrace();
126
                }
127
    }
128

    
129
    /**
130
     * <p>It will send a GetFeatureInfo request to the WMS
131
     * Parsing the response and redirecting the info to the WMS client</p>
132
     */
133
    public String getFeatureInfo(WMSStatus status, int x, int y, int featureCount)
134
    {
135
            URL request = null;
136
            StringBuffer output = new StringBuffer();
137
            String outputFormat = new String();
138
            String ServiceException = "ServiceExceptionReport";                        
139
            StringBuffer sb = new StringBuffer();
140
            sb.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
141
            
142
                try
143
                {
144
                        //TODO:
145
                        //pass this buildXXXRequest to the WMSProtocolHandlerXXX: The request can depend on the WMS version.
146
                        request = new URL(buildGetFeatureInfoRequest(status, x, y));
147
                        
148
                        //TODO:
149
                        //see which output format is being requested.                                         
150
                }
151
                catch(Exception e)
152
                {
153
                        e.printStackTrace();
154
                        return "";
155
                }
156
                
157
            try
158
            {                                
159
                    System.out.println(request.toString());          
160
                    byte[] buffer = new byte[1024*256];                    
161
                    DataInputStream is = new DataInputStream(request.openStream());                    
162
                    outputFormat = request.openConnection().getContentType();
163

    
164
                    for (int i = is.read(buffer); i>0; i = is.read(buffer))
165
                    {
166
                            String str = new String(buffer,0,i);
167
                            output.append(str);                    
168
                    }
169
                                
170
                    is.close();
171
                    if ( (outputFormat == null) || (outputFormat.indexOf("xml") != -1)
172
                                    ||output.toString().toLowerCase().startsWith("<?xml")
173
                                    ||(outputFormat.indexOf("gml") != -1))
174
                    {
175
                            int tag;
176
                            KXmlParser kxmlParser = null;
177
                            kxmlParser = new KXmlParser();            
178
                            kxmlParser.setInput(new StringReader(output.toString()));
179
                            
180
                            tag = kxmlParser.nextTag();                   
181
                                  while(tag != KXmlParser.END_DOCUMENT)
182
                                 {
183
                                         switch(tag)
184
                                         {
185
                                                case KXmlParser.START_TAG:                
186
                                                        if (kxmlParser.getName().compareTo(ServiceException)==0)
187
                                                        {
188
                                                            sb.append("<INFO>").append(parseException( output.toString().getBytes())).append("</INFO>");
189
                                                            return sb.toString();                                                                        
190
                                                        }
191
                                                        else if (kxmlParser.getName().compareToIgnoreCase("ERROR")==0)
192
                                                                return output.toString();
193
                                                        else                                                                
194
                                                                sb.append("<" + kxmlParser.getName() + ">\n");
195
                                                        break;
196
                                                case KXmlParser.END_TAG:        
197
                                                        sb.append("</" + kxmlParser.getName() + ">\n");
198
                                                        break;
199
                                                case KXmlParser.TEXT:
200
                                                        sb.append(kxmlParser.getText());                                                
201
                                                break;
202
                                         }
203
                                     tag = kxmlParser.next();
204
                             }                                    
205
                            return sb.toString();
206
                    }
207
                    else
208
                    {
209
                            sb.append("<INFO>").append(PluginServices.getText(this, "info_format_not_supported")).append("</INFO>");
210
                            return sb.toString();
211
                    }
212
                }
213
            catch(XmlPullParserException parserEx)
214
            {
215
                    if (output.toString().toLowerCase().indexOf("xml") != -1)
216
                    {
217
                            return output.toString().trim();
218
                    }
219
                    else
220
                    {
221
                               sb.append("<INFO>").append(PluginServices.getText(this, "info_format_not_supported")).append("</INFO>");
222
                        return sb.toString();
223
                    }
224
            }
225
            catch(Exception e)
226
            {
227
                    e.printStackTrace();
228
                    sb.append("<INFO>").append(PluginServices.getText(this, "info_format_not_supported")).append("</INFO>");
229
                    return sb.toString();
230

    
231
            }
232
    }
233
    /**
234
     * <p>Builds a GetMap request that is sent to the WMS
235
     * the response (image) will be redirect to the
236
     * WMS client</p>
237
     */   
238
    public byte[] getMap(WMSStatus status) throws ServerErrorException, WMSException
239
    {        
240
            URL request = null;
241
                try
242
                {
243
                        //TODO:
244
                        //pass this buildXXXRequest to the WMSProtocolHandlerXXX: The request can depend on the WMS version.
245
                        request = new URL(buildMapRequest(status));
246
                        URLConnection conn = request.openConnection();
247
                        System.out.println(request.toString());
248
            String type = conn.getContentType();
249
                                
250
                    byte[] imageBytes = null;
251
                    byte[] buffer = new byte[1024*256];
252
            InputStream is = conn.getInputStream();
253
                    int readed = 0;
254
                    
255
                    for (int i = is.read(buffer); i>0; i = is.read(buffer)){
256
                // Creates a new buffer to contain the previous readed bytes and the next bunch of bytes
257
                            byte[] buffered = new byte[readed+i];
258
                            for (int j = 0; j < buffered.length; j++) {
259
                                    if (j<readed){
260
                        // puts the previously downloaded bytes into the image buffer
261
                                            buffered[j] = imageBytes[j];
262
                                    }
263
                                    else {
264
                        // appends the recently downloaded bytes to the image buffer.
265
                                            buffered[j] = buffer[j-readed];
266
                                    }
267
                                }
268
                            imageBytes = (byte[]) buffered.clone();
269
                            readed += i;                            
270
                    }
271
                    
272
                    if ((type !=null && !type.subSequence(0,5).equals("image")) 
273
                            ||(Utilities.isTextData(imageBytes)))
274
                    {                            
275
                       WMSException wmsEx = null;
276
                       
277
                    String exceptionMessage = parseException(imageBytes);
278
                if (exceptionMessage==null)
279
                {
280
                         String error = new String(imageBytes);
281
                        int pos = error.indexOf("<?xml");
282
                        if (pos!= -1)
283
                        {
284
                                String xml = error.substring(pos,error.length());
285
                                exceptionMessage = parseException(xml.getBytes());
286
                        if (exceptionMessage == null)
287
                                exceptionMessage = new String(imageBytes);
288
                        }
289
                }
290
                     //WMSWrongSizeException.check(exceptionMessage);               
291
                // error desconocido.
292
                    wmsEx = new WMSException(exceptionMessage);
293
                    wmsEx.setWMSMessage(new String(imageBytes));
294
                throw wmsEx;
295
            }
296
                        return imageBytes;                    
297
                }
298
                catch(IOException e)
299
                {
300
                        e.printStackTrace();
301
            throw new ServerErrorException();
302
                }
303
    } 
304

    
305
    /* (non-Javadoc)
306
     * @see org.gvsig.remoteClient.wms.WMSProtocolHandler#parseException(byte[])
307
     */
308
    protected String parseException(byte[] data) {
309
        ArrayList errors = new ArrayList();
310
        KXmlParser kxmlParser = new KXmlParser();
311
        Reader reader = new InputStreamReader(new ByteArrayInputStream(data));
312
        try
313
        {
314
            kxmlParser.setInput(reader);        
315
            kxmlParser.nextTag();
316
            int tag;
317
            if ( kxmlParser.getEventType() != KXmlParser.END_DOCUMENT ) 
318
            { 
319
                kxmlParser.require(KXmlParser.START_TAG, null, ExceptionTags.EXCEPTION_ROOT);             
320
                tag = kxmlParser.nextTag();
321
                 while(tag != KXmlParser.END_DOCUMENT)
322
                 {
323
                     switch(tag)
324
                     {
325
                        case KXmlParser.START_TAG:
326
                            if (kxmlParser.getName().compareTo(ExceptionTags.SERVICE_EXCEPTION)==0){
327
                                String errorCode = kxmlParser.getAttributeValue("", ExceptionTags.CODE);
328
                                errorCode = (errorCode != null) ? "["+errorCode+"] " : "";
329
                                String errorMessage = kxmlParser.nextText();
330
                                errors.add(errorCode+errorMessage);
331
                            }
332
                            break;
333
                        case KXmlParser.END_TAG:                            
334
                            break;
335
                        
336
                     }
337
                     tag = kxmlParser.nextTag();
338
                 }
339
                 //kxmlParser.require(KXmlParser.END_DOCUMENT, null, null);
340
            }
341
        }
342
        catch(XmlPullParserException parser_ex){ 
343
            parser_ex.printStackTrace();
344
        }
345
        catch (IOException ioe) {           
346
            ioe.printStackTrace();            
347
        }
348
        String message = errors.size()>0? "" : null;
349
        for (int i = 0; i < errors.size(); i++) {
350
            message += (String) errors.get(i)+"\n";
351
        }
352
        return message;
353
    }
354
    
355
    /**
356
     * Builds the GetCapabilitiesRequest according to the OGC WMS Specifications
357
     * without a VERSION, to get the highest version than a WMS supports.
358
     */
359
    public static String buildCapabilitiesSuitableVersionRequest(String _host, String _version)
360
    {
361
                String req = new String();                
362
        String symbol = getSymbol(_host);
363
        req = req + _host + symbol + "REQUEST=GetCapabilities&SERVICE=WMS&";                
364
        if((_version != null) && (_version.length()>0 ))
365
        {
366
                req += ("&VERSION=" + _version);
367
        }
368
                req += ("&EXCEPTIONS=XML");
369
                return req;           
370
    }
371
    
372
    /**
373
     * Builds the GetCapabilitiesRequest according to the OGC WMS Specifications
374
     */
375
    private String buildCapabilitiesRequest()
376
    {
377
                String req = new String();
378
                String host = getHost();
379

    
380
        String symbol = getSymbol(host);
381
        req = req + getHost() + symbol + "REQUEST=GetCapabilities&SERVICE=WMS&";
382
                req = req + "VERSION=" + getVersion();
383
                req += ("&EXCEPTIONS=XML");
384
                return req;
385
    }
386

    
387
    /**
388
     * Builds the GetFeatureInfoRequest according to the OGC WMS Specifications
389
     */
390
    private String buildGetFeatureInfoRequest(WMSStatus status, int x, int y)
391
    {
392
                String req = new String();
393

    
394
        String symbol = getSymbol(host);
395
        
396
                req = req + getHost() + symbol + "REQUEST=GetFeatureInfo&SERVICE=WMS&";
397
                req = req + "QUERY_LAYERS="+Utilities.Vector2CS(status.getLayerNames()); 
398
                req = req + "&VERSION=" + getVersion() + "&";
399
                req = req + "INFO_FORMAT=application/vnd.ogc.gml&";
400
                req = req + getPartialQuery(status);
401
                req = req + "&x="+x + "&y="+y;
402
       if (status.getExceptionFormat() != null) {
403
            req += ("&EXCEPTIONS=" + status.getExceptionFormat());
404
        } else {
405
            req += ("&EXCEPTIONS=XML");
406
        }
407
                return req;
408
    }    
409

    
410
    /**
411
     * Builds the GetMapRequest according to the OGC WMS Specifications
412
     */
413
    private String buildMapRequest(WMSStatus status)
414
    { 
415
                String req = new String();
416

    
417
                String symbol = getSymbol(host);
418
                req = req + getHost() + symbol + "REQUEST=GetMap&SERVICE=WMS&";
419
                req = req + "VERSION=" + getVersion() + "&";
420
                req = req + getPartialQuery(status);
421
       if (status.getExceptionFormat() != null) {
422
            req += ("&EXCEPTIONS=" + status.getExceptionFormat());
423
        } else {
424
            req += ("&EXCEPTIONS=XML");
425
        }
426
                return req;
427
    }
428
    
429
    /**
430
     * Just for not repeat code. Gets the correct separator according to the server URL
431
     * @param h
432
     * @return
433
     */
434
    private static String getSymbol(String h) {
435
        String symbol;
436
        if (h.indexOf("?")==-1) 
437
            symbol = "?";
438
        else if (h.indexOf("?")!=h.length()-1)
439
            symbol = "&";
440
        else
441
            symbol = "";
442
        return symbol;
443
    }
444

    
445
    /**
446
     * Gets the part of the OGC request that share GetMap and GetFeatureInfo
447
     * @return String request
448
     */
449
    public String getPartialQuery(WMSStatus status)
450
    {            
451
        String req = "LAYERS=" + Utilities.Vector2CS(status.getLayerNames()) + 
452
                    "&SRS=" + status.getSrs() +
453
                    "&BBOX=" + status.getExtent().getMinX()+ "," +
454
                                    status.getExtent().getMinY()+ "," +
455
                                    status.getExtent().getMaxX()+ "," +
456
                                    status.getExtent().getMaxY() +
457
                    "&WIDTH=" + status.getWidth() +
458
                    "&HEIGHT=" + status.getHeight() + "&FORMAT=" + status.getFormat() +
459
                    "&STYLES=";
460
        Vector v = status.getStyles();
461
        if (v!=null && v.size()>0)
462
                req += Utilities.Vector2CS(v); 
463
        v = status.getDimensions();
464
        if (v!=null && v.size()>0)
465
            req += "&" + Utilities.Vector2URLParamString(v);
466
        if (status.getTransparency()) {
467
            req += "&TRANSPARENT=TRUE";
468
        }
469
//
470
//        if (status.getBGColor() != null) {
471
//            req += ("&COLOR=" + status.getBGColor());
472
//        }
473
//
474
        return req.replaceAll(" ", "%20");
475
    }
476

    
477
    
478
    
479
    public void close() {        
480
        // your code here
481
    } 
482
    
483
    /**
484
     * Inner class that represents the description of the WMS metadata.
485
     * The first part of the capabilities will return the service information
486
     * from the WMS, this class will hold this information. 
487
     * 
488
     */
489
    public class ServiceInformation {
490

    
491
        public String online_resource;
492
        public String version;
493
        public String name;
494
        public String scope;
495
        public String title;
496
        public String abstr;
497
        public String keywords;
498
        public String fees;
499
        public String operationsInfo;
500
        public String personname;
501
        public String organization;
502
        public String function;
503
        public String addresstype;
504
        public String address;
505
        public String place;
506
        public String province;
507
        public String postcode;
508
        public String country;
509
        public String phone;
510
        public String fax;
511
        public String email;
512
        public Vector formats;
513
        public Vector operations; // operations that WMS supports
514
        
515
        public ServiceInformation()
516
        {
517
                online_resource = new String();
518
            version = new String();
519
            name = new String();
520
            scope = new String();
521
            title = new String();
522
            abstr = new String();
523
            keywords = new String();
524
            fees = new String();
525
            operationsInfo = new String();
526
            personname = new String();
527
            organization = new String();
528
            function = new String();
529
            addresstype = new String();
530
            address = new String();
531
            place = new String();
532
            province = new String();
533
            postcode = new String();
534
            country = new String();
535
            phone = new String();
536
            fax = new String();
537
            email = new String();
538
            formats = new Vector();               
539
            operations = new Vector();            
540
        }
541
        public boolean isQueryable()
542
        {
543
                if (operations.contains( CapabilitiesTags.GETFEATUREINFO ))
544
                        return true;
545
                else
546
                        return false;
547
        }
548
     }   
549
 }