1 /******************************************************************************
2  * $Id: minidriver_wms.cpp 27942 2014-11-11 00:57:41Z rouault $
3  *
4  * Project:  WMS Client Driver
5  * Purpose:  Implementation of Dataset and RasterBand classes for WMS
6  *           and other similar services.
7  * Author:   Adam Nowacki, nowak@xpam.de
8  *
9  ******************************************************************************
10  * Copyright (c) 2007, Adam Nowacki
11  * Copyright (c) 2007-2012, Even Rouault <even dot rouault at mines-paris dot org>
12  *
13  * Permission is hereby granted, free of charge, to any person obtaining a
14  * copy of this software and associated documentation files (the "Software"),
15  * to deal in the Software without restriction, including without limitation
16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17  * and/or sell copies of the Software, and to permit persons to whom the
18  * Software is furnished to do so, subject to the following conditions:
19  *
20  * The above copyright notice and this permission notice shall be included
21  * in all copies or substantial portions of the Software.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29  * DEALINGS IN THE SOFTWARE.
30  ****************************************************************************/
31 
32 #include "wmsdriver.h"
33 #include "minidriver_wms.h"
34 
CPP_GDALWMSMiniDriverFactory(WMS)35 CPP_GDALWMSMiniDriverFactory(WMS)
36 
37 GDALWMSMiniDriver_WMS::GDALWMSMiniDriver_WMS() {
38 }
39 
~GDALWMSMiniDriver_WMS()40 GDALWMSMiniDriver_WMS::~GDALWMSMiniDriver_WMS() {
41 }
42 
Initialize(CPLXMLNode * config)43 CPLErr GDALWMSMiniDriver_WMS::Initialize(CPLXMLNode *config) {
44     CPLErr ret = CE_None;
45 
46     if (ret == CE_None) {
47         const char *version = CPLGetXMLValue(config, "Version", "1.1.0");
48         if (version[0] != '\0') {
49             m_version = version;
50             m_iversion = VersionStringToInt(version);
51             if (m_iversion == -1) {
52                 CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, WMS mini-driver: Invalid version.");
53                 ret = CE_Failure;
54             }
55         } else {
56             CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, WMS mini-driver: Version missing.");
57             ret = CE_Failure;
58         }
59     }
60 
61     if (ret == CE_None) {
62         const char *base_url = CPLGetXMLValue(config, "ServerURL", "");
63         if (base_url[0] != '\0') {
64             /* Try the old name */
65             base_url = CPLGetXMLValue(config, "ServerUrl", "");
66         }
67         if (base_url[0] != '\0') {
68             m_base_url = base_url;
69         } else {
70             CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, WMS mini-driver: ServerURL missing.");
71             ret = CE_Failure;
72         }
73     }
74 
75     if (ret == CE_None) {
76 /* SRS is WMS version 1.1 and earlier, if SRS is not set use default unless CRS is set
77    CRS is WMS version 1.3, if CRS is not set use default unless SRS is set */
78         const char *crs = CPLGetXMLValue(config, "CRS", "");
79         const char *srs = CPLGetXMLValue(config, "SRS", "");
80         if (m_iversion >= VersionStringToInt("1.3")) {
81             /* Version 1.3 and above */
82             if ((srs[0] != '\0') && (crs[0] == '\0')) {
83                 CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, WMS mini-driver: WMS version 1.3 and above expects CRS however SRS was set instead.");
84                 ret = CE_Failure;
85             } else if (crs[0] != '\0') {
86                 m_crs = crs;
87             } else {
88                 m_crs = "EPSG:4326";
89             }
90         } else {
91             /* Version 1.1.1 and below */
92             if ((srs[0] == '\0') && (crs[0] != '\0')) {
93                 CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, WMS mini-driver: WMS version 1.1.1 and below expects SRS however CRS was set instead.");
94                 ret = CE_Failure;
95             } else if (srs[0] != '\0') {
96                 m_srs = srs;
97             } else {
98                 m_srs = "EPSG:4326";
99             }
100         }
101     }
102 
103     if (ret == CE_None) {
104         if (m_srs.size()) {
105             m_projection_wkt = ProjToWKT(m_srs);
106         } else if (m_crs.size()) {
107             m_projection_wkt = ProjToWKT(m_crs);
108         }
109     }
110 
111     if (ret == CE_None) {
112         m_image_format = CPLGetXMLValue(config, "ImageFormat", "image/jpeg");
113         m_layers = CPLGetXMLValue(config, "Layers", "");
114         m_styles = CPLGetXMLValue(config, "Styles", "");
115         m_transparent = CPLGetXMLValue(config, "Transparent","");
116         // the transparent flag needs to be "TRUE" or "FALSE" in upper case according to the WMS spec so force upper case
117         for(int i=0; i<(int)m_transparent.size();i++)
118         {
119             m_transparent[i] = (char) toupper(m_transparent[i]);
120         }
121     }
122 
123     if (ret == CE_None) {
124         const char *bbox_order = CPLGetXMLValue(config, "BBoxOrder", "xyXY");
125         if (bbox_order[0] != '\0') {
126             int i;
127             for (i = 0; i < 4; ++i) {
128                 if ((bbox_order[i] != 'x') && (bbox_order[i] != 'y') && (bbox_order[i] != 'X') && (bbox_order[i] != 'Y')) break;
129             }
130             if (i == 4) {
131                 m_bbox_order = bbox_order;
132             } else {
133                 CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, WMS mini-driver: Incorrect BBoxOrder.");
134                 ret = CE_Failure;
135             }
136         } else {
137             CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, WMS mini-driver: BBoxOrder missing.");
138             ret = CE_Failure;
139         }
140     }
141 
142     return ret;
143 }
144 
GetCapabilities(GDALWMSMiniDriverCapabilities * caps)145 void GDALWMSMiniDriver_WMS::GetCapabilities(GDALWMSMiniDriverCapabilities *caps) {
146     caps->m_capabilities_version = 1;
147     caps->m_has_arb_overviews = 1;
148     caps->m_has_image_request = 1;
149     caps->m_has_tiled_image_requeset = 1;
150     caps->m_max_overview_count = 32;
151 }
152 
BuildURL(CPLString * url,const GDALWMSImageRequestInfo & iri,const char * pszRequest)153 void GDALWMSMiniDriver_WMS::BuildURL(CPLString *url, const GDALWMSImageRequestInfo &iri, const char* pszRequest) {
154     // http://onearth.jpl.nasa.gov/wms.cgi?request=GetMap&width=1000&height=500&layers=modis,global_mosaic&styles=&srs=EPSG:4326&format=image/jpeg&bbox=-180.000000,-90.000000,180.000000,090.000000
155     *url = m_base_url;
156     if (m_base_url.ifind( "service=") == std::string::npos)
157         URLAppend(url, "&service=WMS");
158     URLAppendF(url, "&request=%s", pszRequest);
159     URLAppendF(url, "&version=%s", m_version.c_str());
160     URLAppendF(url, "&layers=%s", m_layers.c_str());
161     URLAppendF(url, "&styles=%s", m_styles.c_str());
162     if (m_srs.size()) URLAppendF(url, "&srs=%s", m_srs.c_str());
163     if (m_crs.size()) URLAppendF(url, "&crs=%s", m_crs.c_str());
164     if (m_transparent.size()) URLAppendF(url, "&transparent=%s", m_transparent.c_str());
165     URLAppendF(url, "&format=%s", m_image_format.c_str());
166     URLAppendF(url, "&width=%d", iri.m_sx);
167     URLAppendF(url, "&height=%d", iri.m_sy);
168     URLAppendF(url, "&bbox=%.8f,%.8f,%.8f,%.8f",
169         GetBBoxCoord(iri, m_bbox_order[0]), GetBBoxCoord(iri, m_bbox_order[1]),
170         GetBBoxCoord(iri, m_bbox_order[2]), GetBBoxCoord(iri, m_bbox_order[3]));
171 }
172 
ImageRequest(CPLString * url,const GDALWMSImageRequestInfo & iri)173 void GDALWMSMiniDriver_WMS::ImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri) {
174     BuildURL(url, iri, "GetMap");
175     CPLDebug("WMS", "URL = %s", url->c_str());
176 }
177 
TiledImageRequest(CPLString * url,const GDALWMSImageRequestInfo & iri,CPL_UNUSED const GDALWMSTiledImageRequestInfo & tiri)178 void GDALWMSMiniDriver_WMS::TiledImageRequest(CPLString *url,
179                                               const GDALWMSImageRequestInfo &iri,
180                                               CPL_UNUSED const GDALWMSTiledImageRequestInfo &tiri) {
181     ImageRequest(url, iri);
182 }
183 
184 
GetTiledImageInfo(CPLString * url,const GDALWMSImageRequestInfo & iri,CPL_UNUSED const GDALWMSTiledImageRequestInfo & tiri,int nXInBlock,int nYInBlock)185 void GDALWMSMiniDriver_WMS::GetTiledImageInfo(CPLString *url,
186                                               const GDALWMSImageRequestInfo &iri,
187                                               CPL_UNUSED const GDALWMSTiledImageRequestInfo &tiri,
188                                               int nXInBlock,
189                                               int nYInBlock)
190 {
191     BuildURL(url, iri, "GetFeatureInfo");
192     URLAppendF(url, "&query_layers=%s", m_layers.c_str());
193     URLAppendF(url, "&x=%d", nXInBlock);
194     URLAppendF(url, "&y=%d", nYInBlock);
195     const char* pszInfoFormat = CPLGetConfigOption("WMS_INFO_FORMAT", "application/vnd.ogc.gml");
196     URLAppendF(url, "&info_format=%s", pszInfoFormat);
197 
198     CPLDebug("WMS", "URL = %s", url->c_str());
199 }
200 
201 
GetProjectionInWKT()202 const char *GDALWMSMiniDriver_WMS::GetProjectionInWKT() {
203     return m_projection_wkt.c_str();
204 }
205 
GetBBoxCoord(const GDALWMSImageRequestInfo & iri,char what)206 double GDALWMSMiniDriver_WMS::GetBBoxCoord(const GDALWMSImageRequestInfo &iri, char what) {
207     switch (what) {
208         case 'x': return MIN(iri.m_x0, iri.m_x1);
209         case 'y': return MIN(iri.m_y0, iri.m_y1);
210         case 'X': return MAX(iri.m_x0, iri.m_x1);
211         case 'Y': return MAX(iri.m_y0, iri.m_y1);
212     }
213     return 0.0;
214 }
215