source: subversion/applications/rendering/wms/class.datafactory.php @ 28929

Last change on this file since 28929 was 9331, checked in by isortega, 11 years ago

Typo (missing parenthesis) in WMS

File size: 16.8 KB
Line 
1<?php
2
3/// @author Iván Sánchez Ortega <ivan@sanchezortega.es>
4
5/**
6    OSM WMS ("OpenStreetMap Web Map Service")
7    Copyright (C) 2008, Iván Sánchez Ortega
8
9    This program is free software: you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation, either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21*/   
22
23/// Data factory: ask this for some data in some projection, and the factory will ask the appropiate backend and (re)project it.
24
25
26class datafactory
27{
28       
29        static private $backend = 'backend_osmxapi';    /// Which backend to use. Valid values are "backend_api", "backend_osmxapi".
30        //static private $backend = 'backend_api';      /// Which backend to use. Valid values are "backend_api", "backend_osmxapi".
31       
32        static private $outerbounds = array();  /// This will store the max and min eastings and northings for the fetched data - that will surely not exactly fit the requested bbox.
33       
34        static public $available_crs = array('EPSG:4326' =>array(-180,-90,180,90)
35        ,'CRS:84'    =>array(-180,-90,180,90)
36       
37        // Infamous "google" projection
38        ,'EPSG:900913'=>array(-20037508.34, -20037508.34, 20037508.34, 20037508.34)
39       
40        // UTM zones, WGS 84
41        ,'EPSG:32601'=>array(-1500000,0,1500000,10000000)       // UTM 1 N
42        ,'EPSG:32602'=>array(-1500000,0,1500000,10000000)       // UTM 2 N
43        ,'EPSG:32603'=>array(-1500000,0,1500000,10000000)       // UTM 3 N
44        ,'EPSG:32604'=>array(-1500000,0,1500000,10000000)       // UTM 4 N
45        ,'EPSG:32605'=>array(-1500000,0,1500000,10000000)       // UTM 5 N
46        ,'EPSG:32606'=>array(-1500000,0,1500000,10000000)       // UTM 6 N
47        ,'EPSG:32607'=>array(-1500000,0,1500000,10000000)       // UTM 7 N
48        ,'EPSG:32608'=>array(-1500000,0,1500000,10000000)       // UTM 8 N
49        ,'EPSG:32609'=>array(-1500000,0,1500000,10000000)       // UTM 9 N
50        ,'EPSG:32610'=>array(-1500000,0,1500000,10000000)       // UTM 10 N
51        ,'EPSG:32611'=>array(-1500000,0,1500000,10000000)       // UTM 11 N
52        ,'EPSG:32612'=>array(-1500000,0,1500000,10000000)       // UTM 12 N
53        ,'EPSG:32613'=>array(-1500000,0,1500000,10000000)       // UTM 13 N
54        ,'EPSG:32614'=>array(-1500000,0,1500000,10000000)       // UTM 14 N
55        ,'EPSG:32615'=>array(-1500000,0,1500000,10000000)       // UTM 15 N
56        ,'EPSG:32616'=>array(-1500000,0,1500000,10000000)       // UTM 16 N
57        ,'EPSG:32617'=>array(-1500000,0,1500000,10000000)       // UTM 17 N
58        ,'EPSG:32618'=>array(-1500000,0,1500000,10000000)       // UTM 18 N
59        ,'EPSG:32619'=>array(-1500000,0,1500000,10000000)       // UTM 19 N
60        ,'EPSG:32620'=>array(-1500000,0,1500000,10000000)       // UTM 20 N
61        ,'EPSG:32621'=>array(-1500000,0,1500000,10000000)       // UTM 21 N
62        ,'EPSG:32622'=>array(-1500000,0,1500000,10000000)       // UTM 22 N
63        ,'EPSG:32623'=>array(-1500000,0,1500000,10000000)       // UTM 23 N
64        ,'EPSG:32624'=>array(-1500000,0,1500000,10000000)       // UTM 24 N
65        ,'EPSG:32625'=>array(-1500000,0,1500000,10000000)       // UTM 25 N
66        ,'EPSG:32626'=>array(-1500000,0,1500000,10000000)       // UTM 26 N
67        ,'EPSG:32627'=>array(-1500000,0,1500000,10000000)       // UTM 27 N
68        ,'EPSG:32628'=>array(-1500000,0,1500000,10000000)       // UTM 28 N
69        ,'EPSG:32629'=>array(-1500000,0,1500000,10000000)       // UTM 29 N
70        ,'EPSG:32630'=>array(-1500000,0,1500000,10000000)       // UTM 30 N
71        ,'EPSG:32631'=>array(-1500000,0,1500000,10000000)       // UTM 31 N
72        ,'EPSG:32632'=>array(-1500000,0,1500000,10000000)       // UTM 32 N
73        ,'EPSG:32633'=>array(-1500000,0,1500000,10000000)       // UTM 33 N
74        ,'EPSG:32634'=>array(-1500000,0,1500000,10000000)       // UTM 34 N
75        ,'EPSG:32635'=>array(-1500000,0,1500000,10000000)       // UTM 35 N
76        ,'EPSG:32636'=>array(-1500000,0,1500000,10000000)       // UTM 36 N
77        ,'EPSG:32637'=>array(-1500000,0,1500000,10000000)       // UTM 37 N
78        ,'EPSG:32638'=>array(-1500000,0,1500000,10000000)       // UTM 38 N
79        ,'EPSG:32639'=>array(-1500000,0,1500000,10000000)       // UTM 39 N
80        ,'EPSG:32640'=>array(-1500000,0,1500000,10000000)       // UTM 40 N
81        ,'EPSG:32641'=>array(-1500000,0,1500000,10000000)       // UTM 41 N
82        ,'EPSG:32642'=>array(-1500000,0,1500000,10000000)       // UTM 42 N
83        ,'EPSG:32643'=>array(-1500000,0,1500000,10000000)       // UTM 43 N
84        ,'EPSG:32644'=>array(-1500000,0,1500000,10000000)       // UTM 44 N
85        ,'EPSG:32645'=>array(-1500000,0,1500000,10000000)       // UTM 45 N
86        ,'EPSG:32646'=>array(-1500000,0,1500000,10000000)       // UTM 46 N
87        ,'EPSG:32647'=>array(-1500000,0,1500000,10000000)       // UTM 47 N
88        ,'EPSG:32648'=>array(-1500000,0,1500000,10000000)       // UTM 48 N
89        ,'EPSG:32649'=>array(-1500000,0,1500000,10000000)       // UTM 49 N
90        ,'EPSG:32650'=>array(-1500000,0,1500000,10000000)       // UTM 50 N
91        ,'EPSG:32651'=>array(-1500000,0,1500000,10000000)       // UTM 51 N
92        ,'EPSG:32652'=>array(-1500000,0,1500000,10000000)       // UTM 52 N
93        ,'EPSG:32653'=>array(-1500000,0,1500000,10000000)       // UTM 53 N
94        ,'EPSG:32654'=>array(-1500000,0,1500000,10000000)       // UTM 54 N
95        ,'EPSG:32655'=>array(-1500000,0,1500000,10000000)       // UTM 55 N
96        ,'EPSG:32656'=>array(-1500000,0,1500000,10000000)       // UTM 56 N
97        ,'EPSG:32657'=>array(-1500000,0,1500000,10000000)       // UTM 57 N
98        ,'EPSG:32658'=>array(-1500000,0,1500000,10000000)       // UTM 58 N
99        ,'EPSG:32659'=>array(-1500000,0,1500000,10000000)       // UTM 59 N
100        ,'EPSG:32660'=>array(-1500000,0,1500000,10000000)       // UTM 60 N
101       
102        ,'EPSG:32701'=>array(-1500000,0,1500000,10000000)       // UTM 1 S
103        ,'EPSG:32702'=>array(-1500000,0,1500000,10000000)       // UTM 2 S
104        ,'EPSG:32703'=>array(-1500000,0,1500000,10000000)       // UTM 3 S
105        ,'EPSG:32704'=>array(-1500000,0,1500000,10000000)       // UTM 4 S
106        ,'EPSG:32705'=>array(-1500000,0,1500000,10000000)       // UTM 5 S
107        ,'EPSG:32706'=>array(-1500000,0,1500000,10000000)       // UTM 6 S
108        ,'EPSG:32707'=>array(-1500000,0,1500000,10000000)       // UTM 7 S
109        ,'EPSG:32708'=>array(-1500000,0,1500000,10000000)       // UTM 8 S
110        ,'EPSG:32709'=>array(-1500000,0,1500000,10000000)       // UTM 9 S
111        ,'EPSG:32710'=>array(-1500000,0,1500000,10000000)       // UTM 10 S
112        ,'EPSG:32711'=>array(-1500000,0,1500000,10000000)       // UTM 11 S
113        ,'EPSG:32712'=>array(-1500000,0,1500000,10000000)       // UTM 12 S
114        ,'EPSG:32713'=>array(-1500000,0,1500000,10000000)       // UTM 13 S
115        ,'EPSG:32714'=>array(-1500000,0,1500000,10000000)       // UTM 14 S
116        ,'EPSG:32715'=>array(-1500000,0,1500000,10000000)       // UTM 15 S
117        ,'EPSG:32716'=>array(-1500000,0,1500000,10000000)       // UTM 16 S
118        ,'EPSG:32717'=>array(-1500000,0,1500000,10000000)       // UTM 17 S
119        ,'EPSG:32718'=>array(-1500000,0,1500000,10000000)       // UTM 18 S
120        ,'EPSG:32719'=>array(-1500000,0,1500000,10000000)       // UTM 19 S
121        ,'EPSG:32720'=>array(-1500000,0,1500000,10000000)       // UTM 20 S
122        ,'EPSG:32721'=>array(-1500000,0,1500000,10000000)       // UTM 21 S
123        ,'EPSG:32722'=>array(-1500000,0,1500000,10000000)       // UTM 22 S
124        ,'EPSG:32723'=>array(-1500000,0,1500000,10000000)       // UTM 23 S
125        ,'EPSG:32724'=>array(-1500000,0,1500000,10000000)       // UTM 24 S
126        ,'EPSG:32725'=>array(-1500000,0,1500000,10000000)       // UTM 25 S
127        ,'EPSG:32726'=>array(-1500000,0,1500000,10000000)       // UTM 26 S
128        ,'EPSG:32727'=>array(-1500000,0,1500000,10000000)       // UTM 27 S
129        ,'EPSG:32728'=>array(-1500000,0,1500000,10000000)       // UTM 28 S
130        ,'EPSG:32729'=>array(-1500000,0,1500000,10000000)       // UTM 29 S
131        ,'EPSG:32730'=>array(-1500000,0,1500000,10000000)       // UTM 30 S
132        ,'EPSG:32731'=>array(-1500000,0,1500000,10000000)       // UTM 31 S
133        ,'EPSG:32732'=>array(-1500000,0,1500000,10000000)       // UTM 32 S
134        ,'EPSG:32733'=>array(-1500000,0,1500000,10000000)       // UTM 33 S
135        ,'EPSG:32734'=>array(-1500000,0,1500000,10000000)       // UTM 34 S
136        ,'EPSG:32735'=>array(-1500000,0,1500000,10000000)       // UTM 35 S
137        ,'EPSG:32736'=>array(-1500000,0,1500000,10000000)       // UTM 36 S
138        ,'EPSG:32737'=>array(-1500000,0,1500000,10000000)       // UTM 37 S
139        ,'EPSG:32738'=>array(-1500000,0,1500000,10000000)       // UTM 38 S
140        ,'EPSG:32739'=>array(-1500000,0,1500000,10000000)       // UTM 39 S
141        ,'EPSG:32740'=>array(-1500000,0,1500000,10000000)       // UTM 40 S
142        ,'EPSG:32741'=>array(-1500000,0,1500000,10000000)       // UTM 41 S
143        ,'EPSG:32742'=>array(-1500000,0,1500000,10000000)       // UTM 42 S
144        ,'EPSG:32743'=>array(-1500000,0,1500000,10000000)       // UTM 43 S
145        ,'EPSG:32744'=>array(-1500000,0,1500000,10000000)       // UTM 44 S
146        ,'EPSG:32745'=>array(-1500000,0,1500000,10000000)       // UTM 45 S
147        ,'EPSG:32746'=>array(-1500000,0,1500000,10000000)       // UTM 46 S
148        ,'EPSG:32747'=>array(-1500000,0,1500000,10000000)       // UTM 47 S
149        ,'EPSG:32748'=>array(-1500000,0,1500000,10000000)       // UTM 48 S
150        ,'EPSG:32749'=>array(-1500000,0,1500000,10000000)       // UTM 49 S
151        ,'EPSG:32750'=>array(-1500000,0,1500000,10000000)       // UTM 50 S
152        ,'EPSG:32751'=>array(-1500000,0,1500000,10000000)       // UTM 51 S
153        ,'EPSG:32752'=>array(-1500000,0,1500000,10000000)       // UTM 52 S
154        ,'EPSG:32753'=>array(-1500000,0,1500000,10000000)       // UTM 53 S
155        ,'EPSG:32754'=>array(-1500000,0,1500000,10000000)       // UTM 54 S
156        ,'EPSG:32755'=>array(-1500000,0,1500000,10000000)       // UTM 55 S
157        ,'EPSG:32756'=>array(-1500000,0,1500000,10000000)       // UTM 56 S
158        ,'EPSG:32757'=>array(-1500000,0,1500000,10000000)       // UTM 57 S
159        ,'EPSG:32758'=>array(-1500000,0,1500000,10000000)       // UTM 58 S
160        ,'EPSG:32759'=>array(-1500000,0,1500000,10000000)       // UTM 59 S
161        ,'EPSG:32760'=>array(-1500000,0,1500000,10000000)       // UTM 60 S
162       
163        // Universal Polar Stereographic (WGS 84)
164        ,'EPSG:32661'=>array(-4000000,-4000000,4000000,-4000000)        // UPS north
165        ,'EPSG:32761'=>array(-4500000,-4000000,4000000,-4000000)        // UPS south
166       
167       
168        // UTM north, European ETRS89
169        ,'EPSG:25828'=>array(-1500000,0,1500000,10000000)       // UTM 28 N
170        ,'EPSG:25829'=>array(-1500000,0,1500000,10000000)       // UTM 29 N
171        ,'EPSG:25830'=>array(-1500000,0,1500000,10000000)       // UTM 30 N
172        ,'EPSG:25831'=>array(-1500000,0,1500000,10000000)       // UTM 31 N
173        ,'EPSG:25832'=>array(-1500000,0,1500000,10000000)       // UTM 32 N
174        ,'EPSG:25833'=>array(-1500000,0,1500000,10000000)       // UTM 33 N
175        ,'EPSG:25834'=>array(-1500000,0,1500000,10000000)       // UTM 34 N
176        ,'EPSG:25835'=>array(-1500000,0,1500000,10000000)       // UTM 35 N
177        ,'EPSG:25836'=>array(-1500000,0,1500000,10000000)       // UTM 36 N
178        ,'EPSG:25837'=>array(-1500000,0,1500000,10000000)       // UTM 37 N
179        ,'EPSG:25838'=>array(-1500000,0,1500000,10000000)       // UTM 38 N
180       
181);
182       
183       
184       
185
186        /**
187         * @param bbox  A bounding box, as requested via WMS
188         * @param crs   The Coordinate Reference System as requested via WMS. EPSG:3426 and CRS:84 are unprojected, any other has to be reprojected.
189         * @param nodes A reference to an arrays of nodes, which will be filled up with data.
190         * @param ways  A reference to an array of ways, which will be filled up with data.
191         *
192         * TODO: refactor the check for non-numeric bbox elements; the only entry point for the data requests is this datagactory method, so the backends shouldn't have to check for this.
193         */
194        static function get_parsed_data($bbox,$crs,&$nodes,&$ways,&$relations)
195        {
196                $backend = new self::$backend;
197               
198//              var_dump($backend);
199
200                // If the CRS is not WPSG:4326 or CRS:84, then the bbox has to be reprojected in order for the backends to fetch the appropiate set of data.
201                if ($crs != 'EPSG:4326' && $crs != 'CRS:84')
202                {
203                        $bbox = self::get_projected_bbox($bbox,$crs);
204                        $backend->get_parsed_data($bbox,&$nodes,&$ways,&$relations);
205//                      print_r($nodes);
206//                      var_dump($bbox,$nodes);
207                        // Now, convert back all nodes' coordinates to the requested CRS...
208                       
209                        self::cs2cs($nodes,'epsg:4326',strtolower($crs),true);
210                       
211//                      var_dump($bbox,$nodes);
212                }
213                else
214                {
215                        // Requested a CRS native to the backends, just get the data...
216                       
217                        $backend->get_parsed_data($bbox,&$nodes,&$ways,&$relations);
218//                      print_r($nodes);
219                       
220                }
221               
222               
223               
224        }
225       
226       
227       
228       
229
230/*      function cs2cs($x,$y,$srs_in='epsg:4258',$srs_out='epsg:4326')
231        {
232                $r = shell_exec("echo \"$x $y\" | cs2cs -f %.20f +init=$srs_in +to +init=$srs_out");
233                sscanf($r,"%f %f",$x,$y);
234                return (array($x,$y));
235        }*/
236               
237        function cs2cs(& $points,$srs_in='epsg:4326',$srs_out='epsg:4326',$invert_input=false)
238        {
239                // Open some pipes to a "cs2cs" process, feed the points, parse the resulting points...
240       
241                $descriptorspec = array(
242                        0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
243                        1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
244                        2 => array("file", "/tmp/error-output.txt", "a") // stderr is a file to write to
245                );
246               
247                $cwd = '/tmp';
248                $env = array();
249               
250                if ($invert_input)      $invert_flag = " -r -s"; else $invert_flag = '';
251               
252
253                /// FIXME: either check that the CRSs are valid CRSs supported by this class, or perform a shellescapearg on them!
254                $process = proc_open("cs2cs -f %.20f +init=$srs_in +to +init=$srs_out $invert_flag", $descriptorspec, $pipes, $cwd, $env);
255               
256//              $result = array();
257                if (is_resource($process)) {
258                       
259                        foreach($points as $id=>$point)
260                        {
261                                list($x,$y) = $point;
262                                fwrite($pipes[0], "$x $y\n");
263//                              echo "Point $id ($x,$y) passed to cs2cs\n"; ob_flush();
264                        }
265                        fclose($pipes[0]);
266                       
267                        $startedouterbounds = false;
268                        foreach($points as $id=>$point)
269                        {
270//                              echo "Recovering point $id\n"; ob_flush();
271                                fscanf($pipes[1], "%f %f\n", $x2, $y2);
272                                $points[$id] = array( $x2 , $y2 , $points[$id][2]);
273
274                                // Fill up the static outer bounds array, so later processes can know the min and max eastings and northings for the data, which are not those of the requested bbox.
275                                        /// FIXME: lat and lon are swapped?
276                                if ($startedouterbounds)
277                                {
278                                        if ($y2 < $left)   $left   = $y2;
279                                        if ($y2 > $right)  $right  = $y2;
280                                        if ($x2 < $bottom) $bottom = $x2;
281                                        if ($x2 > $top)    $top    = $x2;
282                                }
283                                else
284                                {
285                                        $left = $right = $y2;
286                                        $top  = $bottom = $x2;
287                                        $startedouterbounds = true;
288                                }
289                        }
290                       
291                        fclose($pipes[1]);
292                        // left,bottom,right,top
293                        self::$outerbounds = array($left,$bottom,$right,$top);
294                }
295       
296//              return ($result);
297        }
298
299
300        /// @return a string with the contents of a .osm file, as if the coordinates of the nodes were already projected to the destination CRS.
301        /// The 'factor' parameter is a total hack, to make osmarender render things at a good scale when using UTM. Osmarender is used to work on degrees of longitude, whereas UTM uses meters. Set the factor to 1 when the CRS is similar to platee carree; set the factor to ~ 0.00001 when using UTM. YMMV.
302        static function get_data_as_projected_osm($bbox,$crs,$factor=1)
303        {
304                self::get_parsed_data($bbox,$crs,$nodes,$ways,$relations);
305
306
307                $w = new XMLWriter();
308                $w->openMemory();
309                $w->setIndent(true);
310                $w->startDocument('1.0','UTF-8');
311                $w->startElement('osm');
312                        $w->writeAttribute('version','0.5');
313                        $w->writeAttribute('generator','OSMWMS');
314                $w->startElement('bound');
315                        $w->writeAttribute('bbox',$bbox);
316//                      $backend = new self::$backend;
317//                      $w->writeAttribute('origin',$backend->get_base_api_url());
318                $w->endElement();
319
320                foreach($nodes as $id=>$node)
321                {
322                        list ($x,$y,$ts) = $node;
323                        $w->startElement('node');
324                        $w->writeAttribute('id',$id);
325                        $w->writeAttribute('lat',$x*$factor);
326                        $w->writeAttribute('lon',$y*$factor);
327                        if (is_array($ts))
328                        foreach($ts as $tag)
329                        {
330                                $w->startElement('tag');
331                                $w->writeAttribute('k',$tag[0]);
332                                $w->writeAttribute('v',$tag[1]);
333                                $w->endElement();
334                        }
335                        $w->endElement();
336                }
337
338                foreach($ways as $id=>$way)
339                {
340                        $w->startElement('way');
341                        $w->writeAttribute('id',$id);
342                        $w->writeAttribute('user',$way['user']);
343                        $w->writeAttribute('timestamp',$way['timestamp']);
344                        foreach($way['nodes'] as $nd)
345                        {
346                                $w->startElement('nd');
347                                $w->writeAttribute('ref',$nd);
348                                $w->endElement();
349                        }
350                        if (is_array($way['tags']))
351                        foreach($way['tags'] as $tag)
352                        {
353                                $w->startElement('tag');
354                                $w->writeAttribute('k',$tag[0]);
355                                $w->writeAttribute('v',$tag[1]);
356                                $w->endElement();
357                        }
358                        $w->endElement();
359                }
360
361
362
363                /// TODO: relations
364
365                $w->endElement(); // </osm>
366                $w->endDocument();
367                return($w->outputMemory());
368        }
369
370
371
372
373        private static function get_projected_bbox($bbox,$crs)
374        {
375                        list($left,$bottom,$right,$top) = explode(',',$bbox);
376               
377                        if ( !is_numeric($left) ||
378                        !is_numeric($bottom) ||
379                        !is_numeric($right) ||
380                        !is_numeric($top))
381                                trigger_error("Coordinates of the bounding box are not numbers!",E_USER_ERROR);
382                       
383                        // cs2cs gets numbers in an x-y fashion...
384                        $corners = array ('UL' => array( $left , $top    )
385                                         ,'UR' => array( $right, $top    )
386                                         ,'BR' => array( $right, $bottom )
387                                         ,'BL' => array( $left , $bottom )
388                                         );
389                       
390                        /// TODO: check that the target CRS is valid (i.e. in the array of available CRSs)
391                        self::cs2cs($corners,strtolower($crs));
392                       
393//                      var_dump($corners);
394//                      $bbox = "$left,$bottom,$right,$top";
395                       
396                        // Get the min and max latitudes and longitudes
397                        foreach($corners as $corner)
398                        {
399                                list ($lon,$lat) = $corner;
400                                if (!isset($east))      // Init from the first set of coords
401                                {
402                                        $east = $west = $lon;
403                                        $south = $north = $lat;
404                                }
405                                else    // check maximums
406                                {
407                                        if ($lon > $west)  $west  = $lon;
408                                        if ($lon < $east)  $east  = $lon;
409                                        if ($lat > $north) $north = $lat;
410                                        if ($lat < $south) $south = $lat;
411                                }
412                        }
413                        return "$east,$south,$west,$north";
414               
415        }
416
417
418        public static function get_outerbounds()
419        {
420                return self::$outerbounds;
421        }
422
423
424}
425
426
427
428
Note: See TracBrowser for help on using the repository browser.