|
| @@ -0,0 +1,908 @@ |
| @@ -0,0 +1,908 @@ |
|
| |
1
| +<?php |
|
| |
2
| +/** |
|
| |
3
| +* Mapserver wrapper to KML/KMZ data |
|
| |
4
| +* |
|
| |
5
| +* Returns KML or KMZ representation of common OGC requests |
|
| |
6
| +* |
|
| |
7
| +* <pre> |
|
| |
8
| +* accepted parameters (case insensitive): |
|
| |
9
| +* - request = string - request type (OGC WFS like), can be kml (default), kmz, icon |
|
| |
10
| +* - map = string - path to mapfile |
|
| |
11
| +* - typename = string - (can be a csv list) - layer name(s) |
|
| |
12
| +* - filter = string - filter encoding |
|
| |
13
| +* - bbox = string - (csv) - bounding box csv |
|
| |
14
| +* - encoding = string - data and mapfile encoding, defaults to ISO-8859-1 |
|
| |
15
| +* |
|
| |
16
| +* |
|
| |
17
| +* </pre> |
|
| |
18
| +* |
|
| |
19
| +* @author Alessandro Pasotti |
|
| |
20
| +* @copyright 2007 ItOpen.it - All rights reserved |
|
| |
21
| +* @package KMLMAPSERVER |
|
| |
22
| + |
|
| |
23
| +This file is part of KMLMAPSERVER. |
|
| |
24
| + |
|
| |
25
| + KMLMAPSERVER is free software; you can redistribute it and/or modify |
|
| |
26
| + it under the terms of the GNU Lesser General Public License as published by |
|
| |
27
| + the Free Software Foundation; either version 3 of the License, or |
|
| |
28
| + (at your option) any later version. |
|
| |
29
| + |
|
| |
30
| + KMLMAPSERVER is distributed in the hope that it will be useful, |
|
| |
31
| + but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| |
32
| + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| |
33
| + GNU Lesser General Public License for more details. |
|
| |
34
| + |
|
| |
35
| + You should have received a copy of the GNU Lesser General Public License |
|
| |
36
| + along with KMLMAPSERVER; if not, write to the Free Software |
|
| |
37
| + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
| |
38
| + |
|
| |
39
| +*/ |
|
| |
40
| + |
|
| |
41
| + |
|
| |
42
| +/** Fix a GE bug for filled polygons */ |
|
| |
43
| +define('TREAT_POLY_AS_LINE', true); |
|
| |
44
| + |
|
| |
45
| +/** Enable cache */ |
|
| |
46
| +define('ENABLE_CACHE', true); |
|
| |
47
| + |
|
| |
48
| +if (!extension_loaded('MapScript')) |
|
| |
49
| +{ |
|
| |
50
| + dl( 'php_mapscript.' . PHP_SHLIB_SUFFIX ); |
|
| |
51
| +} |
|
| |
52
| + |
|
| |
53
| + |
|
| |
54
| +/** |
|
| |
55
| +* Main server class |
|
| |
56
| +*/ |
|
| |
57
| + |
|
| |
58
| +class LayerServer { |
|
| |
59
| + |
|
| |
60
| + /** map file path */ |
|
| |
61
| + var $map; |
|
| |
62
| + |
|
| |
63
| + /** request */ |
|
| |
64
| + var $request; |
|
| |
65
| + |
|
| |
66
| + /** map instance */ |
|
| |
67
| + var $map_object; |
|
| |
68
| + |
|
| |
69
| + /** layer name(s) passed on the request */ |
|
| |
70
| + var $typename; |
|
| |
71
| + |
|
| |
72
| + /** array of requested layer objects (hash with layer name as key) */ |
|
| |
73
| + var $layers; |
|
| |
74
| + |
|
| |
75
| + /** filters */ |
|
| |
76
| + var $filter; |
|
| |
77
| + |
|
| |
78
| + /** bounding box */ |
|
| |
79
| + var $bbox; |
|
| |
80
| + |
|
| |
81
| + /** error messages */ |
|
| |
82
| + var $errors; |
|
| |
83
| + |
|
| |
84
| + /** send zipped data */ |
|
| |
85
| + var $_zipped = false; |
|
| |
86
| + |
|
| |
87
| + /** internal XML buffer */ |
|
| |
88
| + var $_xml; |
|
| |
89
| + |
|
| |
90
| + /** input projection */ |
|
| |
91
| + var $in_proj; |
|
| |
92
| + |
|
| |
93
| + /** output projection */ |
|
| |
94
| + var $out_proj; |
|
| |
95
| + |
|
| |
96
| + /** debug flag */ |
|
| |
97
| + var $_debug = false; |
|
| |
98
| + |
|
| |
99
| + /** end point */ |
|
| |
100
| + var $endpoint; |
|
| |
101
| + |
|
| |
102
| + /** custom style counter */ |
|
| |
103
| + var $style_counter = 0; |
|
| |
104
| + |
|
| |
105
| + /** |
|
| |
106
| + * Mapfile and data encoding encoding |
|
| |
107
| + * XMl output must be UTF-8, attributes and METADATA based strings |
|
| |
108
| + * must be converted to UTF-8, encoding defaults to ISO-8859-1, if |
|
| |
109
| + * your encoding is different, you can set it through CGI style parameters |
|
| |
110
| + */ |
|
| |
111
| + var $encoding; |
|
| |
112
| + |
|
| |
113
| + /** |
|
| |
114
| + * send networklink |
|
| |
115
| + * wether folder should contain networklinks instead of real geometries |
|
| |
116
| + * it is automatically set when all layers are requested |
|
| |
117
| + */ |
|
| |
118
| + var $_networklink; |
|
| |
119
| + |
|
| |
120
| + |
|
| |
121
| + /** |
|
| |
122
| + * Initialize |
|
| |
123
| + * |
|
| |
124
| + */ |
|
| |
125
| + function LayerServer(){ |
|
| |
126
| + $this->errors = array(); |
|
| |
127
| + // Load request parameters |
|
| |
128
| + $this->get_request(); |
|
| |
129
| + |
|
| |
130
| + $this->style_counter = 0; |
|
| |
131
| + |
|
| |
132
| + // Load map |
|
| |
133
| + if(!$this->has_error()) { |
|
| |
134
| + $this->load_map(); |
|
| |
135
| + } |
|
| |
136
| + |
|
| |
137
| + } |
|
| |
138
| + |
|
| |
139
| + /** |
|
| |
140
| + * Run the server and sends data |
|
| |
141
| + * @return string or void |
|
| |
142
| + */ |
|
| |
143
| + function run(){ |
|
| |
144
| + // Check cache |
|
| |
145
| + if(ENABLE_CACHE){ |
|
| |
146
| + $cache_file = $this->get_cache_file_name(); |
|
| |
147
| + if(file_exists($cache_file)){ |
|
| |
148
| + // Check if is not expired |
|
| |
149
| + $layer = $this->map_object->getLayerByName($this->typename); |
|
| |
150
| + if(filectime($cache_file) + $layer->getMetadata('KML_CACHE') < (time())) { |
|
| |
151
| + error_log('removing cache ' . $cache_file); |
|
| |
152
| + //error_log('ctime : ' . filectime($cache_file) . ' , ' . time() . ' lm ' . $layer->getMetadata('KML_CACHE')); |
|
| |
153
| + @unlink($cache_file); |
|
| |
154
| + } else { |
|
| |
155
| + $this->send_header(); |
|
| |
156
| + error_log('sending cache ' . $cache_file); |
|
| |
157
| + readfile($cache_file); |
|
| |
158
| + exit; |
|
| |
159
| + } |
|
| |
160
| + } |
|
| |
161
| + } |
|
| |
162
| + |
|
| |
163
| + // If not layer are requested, send all as networklinks |
|
| |
164
| + if(!$this->typename){ |
|
| |
165
| + $this->_networklink = true; |
|
| |
166
| + $this->typename = $this->get_layer_list(); |
|
| |
167
| + } else { |
|
| |
168
| + $this->_networklink = false; |
|
| |
169
| + } |
|
| |
170
| + |
|
| |
171
| + $this->_xml = new SimpleXMLElement('<kml xmlns="http://earth.google.com/kml/2.0"><Document ></Document></kml>'); |
|
| |
172
| + // Prepare projection |
|
| |
173
| + $this->in_proj = ms_newProjectionObj($this->map_object->getProjection()); |
|
| |
174
| + // Set projection to GOOGLE earth's projection |
|
| |
175
| + $this->out_proj = ms_newProjectionObj("init=epsg:4326"); |
|
| |
176
| + // Set endpoint |
|
| |
177
| + //die($_SERVER['REQUEST_URI']); |
|
| |
178
| + $this->endpoint = ($_SERVER['HTTPS'] ? 'https' : 'http') . '://'.$_SERVER['SERVER_NAME'] . ($_SERVER['SERVER_PORT'] ? ':'.$_SERVER['SERVER_PORT'] : '') . $_SERVER['PHP_SELF']; |
|
| |
179
| + |
|
| |
180
| + |
|
| |
181
| + // Process request |
|
| |
182
| + if(!$this->has_error()) { |
|
| |
183
| + $this->process_request(); |
|
| |
184
| + } |
|
| |
185
| + if($this->has_error()){ |
|
| |
186
| + $this->add_errors(); |
|
| |
187
| + } |
|
| |
188
| + return $this->send_stream($this->get_kml()); |
|
| |
189
| + } |
|
| |
190
| + |
|
| |
191
| + /** |
|
| |
192
| + * Set debug flag |
|
| |
193
| + * @param boolean $value |
|
| |
194
| + */ |
|
| |
195
| + function set_debug($value){ |
|
| |
196
| + $this->_debug = $value; |
|
| |
197
| + } |
|
| |
198
| + |
|
| |
199
| + /** |
|
| |
200
| + * Get all request parameters |
|
| |
201
| + */ |
|
| |
202
| + function get_request(){ |
|
| |
203
| + $this->map = $this->load_parm('map'); |
|
| |
204
| + $this->bbox = $this->load_parm('bbox'); |
|
| |
205
| + $this->filter = $this->load_parm('filter'); |
|
| |
206
| + $this->typename = $this->load_parm('typename'); |
|
| |
207
| + $this->encoding = $this->load_parm('encoding', 'ISO-8859-1'); |
|
| |
208
| + $this->request = $this->load_parm('request', 'kml'); |
|
| |
209
| + |
|
| |
210
| + if($this->request == 'kmz') { |
|
| |
211
| + $this->_zipped = true; |
|
| |
212
| + } |
|
| |
213
| + |
|
| |
214
| + if(!$this->map){ |
|
| |
215
| + $this->set_error('No mapfile specified'); |
|
| |
216
| + } |
|
| |
217
| + } |
|
| |
218
| + |
|
| |
219
| + /** |
|
| |
220
| + * Apply filter |
|
| |
221
| + * @return array |
|
| |
222
| + */ |
|
| |
223
| + function apply_filter(&$layer, &$filter){ |
|
| |
224
| + if($layer->connectiontype == MS_POSTGIS){ |
|
| |
225
| + if($filter->PropertyIsEqualTo){ |
|
| |
226
| + $searchstring = '"'.$filter->PropertyIsEqualTo->PropertyName . ' = ' . '\''.addslashes($filter->PropertyIsEqualTo->Literal).'\''.'"'; |
|
| |
227
| + $searchfield = $filter->PropertyIsEqualTo->PropertyName; |
|
| |
228
| + } elseif($filter->PropertyIsLike){ |
|
| |
229
| + $searchfield = $filter->PropertyIsLike->PropertyName; |
|
| |
230
| + $searchstring ='"'.$filter->PropertyIsLike->PropertyName . ' LIKE \'%' . addslashes($filter->PropertyIsLike->Literal) . '%\''.'"'; |
|
| |
231
| + } |
|
| |
232
| + } elseif($layer->connectiontype == MS_SHAPEFILE || $layer->connectiontype == MS_OGR){ |
|
| |
233
| + if($filter->PropertyIsEqualTo){ |
|
| |
234
| + $searchstring = $filter->PropertyIsEqualTo->Literal; |
|
| |
235
| + $searchfield = $filter->PropertyIsEqualTo->PropertyName; |
|
| |
236
| + } elseif($filter->PropertyIsLike){ |
|
| |
237
| + $searchstring = $filter->PropertyIsLike->Literal; |
|
| |
238
| + $searchfield = $filter->PropertyIsLike->PropertyName; |
|
| |
239
| + } |
|
| |
240
| + } |
|
| |
241
| + return array($searchfield, $searchstring); |
|
| |
242
| + } |
|
| |
243
| + |
|
| |
244
| + /** |
|
| |
245
| + * Process request |
|
| |
246
| + */ |
|
| |
247
| + function process_request(){ |
|
| |
248
| + // Get layer(s) |
|
| |
249
| + $layers = split(',', $this->typename); |
|
| |
250
| + if($this->_networklink){ |
|
| |
251
| + foreach($layers as $layer){ |
|
| |
252
| + $this->add_networklink($layer); |
|
| |
253
| + } |
|
| |
254
| + } else { |
|
| |
255
| + foreach($layers as $layer){ |
|
| |
256
| + $this->process_layer_request($layer); |
|
| |
257
| + } |
|
| |
258
| + } |
|
| |
259
| + } |
|
| |
260
| + |
|
| |
261
| + /** |
|
| |
262
| + * Add a networklink |
|
| |
263
| + */ |
|
| |
264
| + function add_networklink(&$layer_name){ |
|
| |
265
| + $nl =& $this->_xml->Document->addChild('NetworkLink'); |
|
| |
266
| + |
|
| |
267
| + $layer = @$this->map_object->getLayerByName($layer_name); |
|
| |
268
| + $nl->addChild('name', $this->get_layer_description($layer)); |
|
| |
269
| + $nl->addChild('visibility', 0); |
|
| |
270
| + $link =& $nl->addChild('Link'); |
|
| |
271
| + $link->addChild('href', $this->endpoint . '?map=' . $this->map . '&typename=' . urlencode($layer_name) . '&request=' . ($this->_zipped ? 'kmz' : 'kml')); |
|
| |
272
| + } |
|
| |
273
| + |
|
| |
274
| + |
|
| |
275
| + /** |
|
| |
276
| + * Process a single layer |
|
| |
277
| + * @return boolean false on error |
|
| |
278
| + */ |
|
| |
279
| + function process_layer_request(&$layer_name){ |
|
| |
280
| + |
|
| |
281
| + $layer = @$this->map_object->getLayerByName($layer_name); |
|
| |
282
| + |
|
| |
283
| + if(!$layer){ |
|
| |
284
| + $this->set_error('Layer not found ' . $layer_name, $layer_name); |
|
| |
285
| + return false; |
|
| |
286
| + } |
|
| |
287
| + |
|
| |
288
| + // Add to layer list |
|
| |
289
| + $this->layers[$layer_name] =& $layer; |
|
| |
290
| + |
|
| |
291
| + // Get custom template if any |
|
| |
292
| + $description_template = $layer->getMetadata('DESCRIPTION_TEMPLATE'); |
|
| |
293
| + |
|
| |
294
| + // Set on |
|
| |
295
| + $layer->set( 'status', MS_ON ); |
|
| |
296
| + |
|
| |
297
| + // Set kml title from layer description (default to layer name) |
|
| |
298
| + $layer_desc = $this->get_layer_description($layer); |
|
| |
299
| + |
|
| |
300
| + // Now switch raster layers |
|
| |
301
| + //var_dump($layer->type == MS_LAYER_RASTER); |
|
| |
302
| + if($layer->type == MS_LAYER_RASTER){ |
|
| |
303
| + // Check if wms_onlineresource metadata is set |
|
| |
304
| + $wms_link = $this->map_object->getMetadata('wms_onlineresource'); |
|
| |
305
| + if(!$wms_link){ |
|
| |
306
| + $wms_link = $this->map_object->getMetadata('ows_onlineresource'); |
|
| |
307
| + } |
|
| |
308
| + if(!$wms_link){ |
|
| |
309
| + $this->set_error('No WMS server available for ' . $layer_name, $layer_name); |
|
| |
310
| + return false; |
|
| |
311
| + } |
|
| |
312
| + // Add parameters to OGC server call |
|
| |
313
| + // Fix & |
|
| |
314
| + $wms_link = preg_replace('/&/', '&', $wms_link); |
|
| |
315
| + $wms_link .= 'VERSION=1.1.1&REQUEST=GetMap&SRS=EPSG:4326&STYLES=&BGCOLOR=0xFFFFFF&FORMAT=image/png&TRANSPARENT=TRUE&'; |
|
| |
316
| + // Link ok, create folder |
|
| |
317
| + $folder =& $this->_xml->Document->addChild('GroundOverlay'); |
|
| |
318
| + $folder->addChild('description', $this->get_layer_description($layer)); |
|
| |
319
| + $folder->addChild('name', $layer_desc); |
|
| |
320
| + $this->add_wms_link($folder, $layer, $wms_link); |
|
| |
321
| + } else { |
|
| |
322
| + |
|
| |
323
| + // Apply filter |
|
| |
324
| + if($this->filter){ |
|
| |
325
| + // Try loading as XML |
|
| |
326
| + try { |
|
| |
327
| + $filter = @new SimpleXMLElement($this->filter); |
|
| |
328
| + list($searchfield, $searchstring) = $this->apply_filter($layer, $filter); |
|
| |
329
| + if(! ($searchfield && $searchstring)){ |
|
| |
330
| + $this->set_error('Error parsing filter', $layer_name); |
|
| |
331
| + return false; |
|
| |
332
| + } |
|
| |
333
| + } catch (Exception $e) { |
|
| |
334
| + $this->set_error('Wrong XML filter', $layer_name); |
|
| |
335
| + $this->filter = null; |
|
| |
336
| + return false; |
|
| |
337
| + } |
|
| |
338
| + } |
|
| |
339
| + |
|
| |
340
| + // Get results |
|
| |
341
| + if(MS_SUCCESS == $layer->open()){ |
|
| |
342
| + // Search which column to use to identify the feature |
|
| |
343
| + $namecol = $layer->getMetadata('RESULT_FIELDS'); |
|
| |
344
| + if(!$namecol){ |
|
| |
345
| + $cols = array_values($layer->getItems()); |
|
| |
346
| + $namecol = $cols[0]; |
|
| |
347
| + } |
|
| |
348
| + // Add classes |
|
| |
349
| + $folder =& $this->_xml->Document->addChild('Folder'); |
|
| |
350
| + $class_list = $this->parse_classes($layer, $folder, $namecol, $title_field, $description_template); |
|
| |
351
| + |
|
| |
352
| + //die(print_r($class_list, true)); |
|
| |
353
| + $folder->addChild('description', $this->get_layer_description($layer)); |
|
| |
354
| + $folder->addChild('name', $layer_desc); |
|
| |
355
| + |
|
| |
356
| + //print("$searchfield && $searchstring"); |
|
| |
357
| + if($searchfield && $searchstring){ |
|
| |
358
| + if(@$layer->queryByAttributes($searchfield, $searchstring, MS_MULTIPLE) == MS_SUCCESS){ |
|
| |
359
| + $layer->open(); |
|
| |
360
| + //var_dump($layer->getItems()); die(); |
|
| |
361
| + for ($j=0; $j < $layer->getNumResults(); $j++) |
|
| |
362
| + { |
|
| |
363
| + // get next shape row |
|
| |
364
| + $result = $layer->getResult($j); |
|
| |
365
| + $shape = $layer->getShape($result->tileindex, $result->shapeindex); |
|
| |
366
| + $this->process_shape($layer, $shape, $class_list, $folder, $namecol); |
|
| |
367
| + // end for loop |
|
| |
368
| + } |
|
| |
369
| + } else { |
|
| |
370
| + $this->set_error('Query returned no data', $layer_name); |
|
| |
371
| + return false; |
|
| |
372
| + } |
|
| |
373
| + } else { // Get all shapes |
|
| |
374
| + $status = $layer->whichShapes($this->map_object->extent); |
|
| |
375
| + while ($shape = $layer->nextShape()) { |
|
| |
376
| + $this->process_shape($layer, $shape, $class_list, $folder,$namecol ); |
|
| |
377
| + } |
|
| |
378
| + } |
|
| |
379
| + $layer->close(); |
|
| |
380
| + } else { |
|
| |
381
| + $this->set_error('Layer cannot be opened', $layer_name); |
|
| |
382
| + return false; |
|
| |
383
| + } |
|
| |
384
| + } |
|
| |
385
| + return true; |
|
| |
386
| + } |
|
| |
387
| + |
|
| |
388
| + /** |
|
| |
389
| + * Process the shape |
|
| |
390
| + */ |
|
| |
391
| + function process_shape(&$layer, &$shape, &$class_list, &$folder, &$namecol){ |
|
| |
392
| + $shape->project($this->in_proj, $this->out_proj); |
|
| |
393
| + // Assign style |
|
| |
394
| + if($layer->classitem){ |
|
| |
395
| + $style_id = $this->get_shape_class($layer->classitem, $shape->values, $class_list); |
|
| |
396
| + } |
|
| |
397
| + if(!$style_id){ |
|
| |
398
| + // Get first class |
|
| |
399
| + $class_keys = array_keys($class_list); |
|
| |
400
| + $style_id = $class_keys[0]; |
|
| |
401
| + } |
|
| |
402
| + // Add the feature |
|
| |
403
| + if(array_key_exists('folder', $class_list[$style_id])) { |
|
| |
404
| + $feature_folder =& $class_list[$style_id]['folder']; |
|
| |
405
| + } else { |
|
| |
406
| + //die('missing folder for ' . $style_id); |
|
| |
407
| + $feature_folder =& $folder; |
|
| |
408
| + } |
|
| |
409
| + if(!is_object($feature_folder)){ |
|
| |
410
| + $folder_name = $feature_folder; |
|
| |
411
| + $feature_folder =& $folder ->addChild('Folder'); |
|
| |
412
| + $feature_folder->addChild('name', $folder_name); |
|
| |
413
| + } |
|
| |
414
| + // Add style class |
|
| |
415
| + $style_url =& $this->add_style($layer, $feature_folder, $style_id, $class_list[$style_id], $namecol, $shape->values); |
|
| |
416
| + |
|
| |
417
| + $wkt = $shape->toWkt(); |
|
| |
418
| + $placemark =& $this->add_feature($feature_folder, $wkt, $shape->values[$namecol], $shape->values, $description_template, $class_list[$style_id]); |
|
| |
419
| + |
|
| |
420
| + $placemark->addChild('styleUrl', '#'. $style_url); |
|
| |
421
| + |
|
| |
422
| + } |
|
| |
423
| + |
|
| |
424
| + /** |
|
| |
425
| + * Add the feature to the result set |
|
| |
426
| + * @return reference to placemark object |
|
| |
427
| + */ |
|
| |
428
| + function &add_feature(&$folder, &$wkt, $featurename, $attributes, $description_template, $style_data){ |
|
| |
429
| + $pm = $folder->addChild('Placemark'); |
|
| |
430
| + //if($featurename == 'VERCELLI') {var_dump($wkt); die();} |
|
| |
431
| + $pm->addChild('name', iconv($this->encoding, 'utf-8', $featurename)); |
|
| |
432
| + $pm->addChild('description', $this->get_feature_description($featurename, $attributes, $description_template)); |
|
| |
433
| + // Now parse the wkt |
|
| |
434
| + if(strpos($wkt, 'MULTILINESTRING') !== false){ |
|
| |
435
| + $this->add_multilinestring($wkt, $pm, $featurename, $style_data['icon']); |
|
| |
436
| + } elseif(strpos($wkt, 'LINESTRING') !== false){ |
|
| |
437
| + $this->add_linestring($wkt, $pm, $featurename, $style_data['icon']); |
|
| |
438
| + } elseif(strpos($wkt, 'POINT') !== false){ |
|
| |
439
| + $this->add_point($wkt, $pm, $featurename); |
|
| |
440
| + } elseif(strpos($wkt, 'MULTIPOLYGON') !== false){ |
|
| |
441
| + if(TREAT_POLY_AS_LINE){ |
|
| |
442
| + $ml = $pm->addChild('MultiGeometry'); |
|
| |
443
| + foreach(split('\), \(', $wkt) as $line){ |
|
| |
444
| + $this->add_multilinestring($line, $ml, $featurename ); |
|
| |
445
| + } |
|
| |
446
| + } else { |
|
| |
447
| + $this->add_multipolygon($wkt, $pm, $featurename); |
|
| |
448
| + } |
|
| |
449
| + } elseif(strpos($wkt, 'POLYGON') !== false){ |
|
| |
450
| + if(TREAT_POLY_AS_LINE){ |
|
| |
451
| + $this->add_multilinestring($wkt, $pm, $featurename); |
|
| |
452
| + } else { |
|
| |
453
| + $this->add_polygon($wkt, $pm, $featurename); |
|
| |
454
| + } |
|
| |
455
| + } else { |
|
| |
456
| + // Error? |
|
| |
457
| + } |
|
| |
458
| + return $pm; |
|
| |
459
| + } |
|
| |
460
| + |
|
| |
461
| + /** |
|
| |
462
| + * Add a linestring |
|
| |
463
| + */ |
|
| |
464
| + function add_linestring(&$wkt, &$element, $featurename, $add_points){ |
|
| |
465
| + preg_match('/(\d+[^\(\)]*\d)/', $wkt, $data); |
|
| |
466
| + $data = str_replace(', ', '#', $data[1]); |
|
| |
467
| + $data = str_replace(' ', ',', $data); |
|
| |
468
| + $data = str_replace('#', ' ', $data); |
|
| |
469
| + if($add_points){ |
|
| |
470
| + preg_match('/^(\d+\.\d+,\d+\.\d+).*(\d+\.\d+,\d+\.\d+)$/', $data, $points); |
|
| |
471
| + if(count($points) == 3){ |
|
| |
472
| + $mg = $element->addChild('MultiGeometry'); |
|
| |
473
| + $ls = $mg->addChild('LineString'); |
|
| |
474
| + $pt1 = $mg->addChild('Point'); |
|
| |
475
| + $pt1->addChild('coordinates', $points[1]); |
|
| |
476
| + $pt2 = $mg->addChild('Point'); |
|
| |
477
| + $pt2->addChild('coordinates', $points[2]); |
|
| |
478
| + } else { |
|
| |
479
| + die('errore'); |
|
| |
480
| + $ls = $element->addChild('LineString'); |
|
| |
481
| + } |
|
| |
482
| + //print_r($points);die(); |
|
| |
483
| + } else { |
|
| |
484
| + $ls = $element->addChild('LineString'); |
|
| |
485
| + } |
|
| |
486
| + $ls->addChild('coordinates', $data); |
|
| |
487
| + } |
|
| |
488
| + |
|
| |
489
| + /** |
|
| |
490
| + * Add a multilinestring |
|
| |
491
| + */ |
|
| |
492
| + function add_multilinestring(&$wkt, &$element, $featurename, $add_points){ |
|
| |
493
| + $ml = $element->addChild('MultiGeometry'); |
|
| |
494
| + foreach(split('\), \(', $wkt) as $line){ |
|
| |
495
| + $this->add_linestring($line, $ml, $featurename, $add_points ); |
|
| |
496
| + } |
|
| |
497
| + } |
|
| |
498
| + |
|
| |
499
| + /** |
|
| |
500
| + * Add a point |
|
| |
501
| + */ |
|
| |
502
| + function add_point(&$wkt, &$element, $featurename){ |
|
| |
503
| + $pt = $element->addChild('Point'); |
|
| |
504
| + preg_match('/(\d\.?\d+\s\d+\.?\d+)/', $wkt, $data); |
|
| |
505
| + $data = str_replace(' ', ',', $data[1]); |
|
| |
506
| + $pt->addChild('coordinates', $data); |
|
| |
507
| + } |
|
| |
508
| + |
|
| |
509
| + |
|
| |
510
| + /** |
|
| |
511
| + * Add a polygon |
|
| |
512
| + */ |
|
| |
513
| + function add_polygon(&$wkt, &$element, $featurename){ |
|
| |
514
| + $ml = $element->addChild('Polygon'); |
|
| |
515
| + foreach(split('\), \(', $wkt) as $line){ |
|
| |
516
| + preg_match('/(\d+[^\(\)]*\d)/', $wkt, $data); |
|
| |
517
| + $data = str_replace(', ', '#', $data[1]); |
|
| |
518
| + $data = str_replace(' ', ',', $data); |
|
| |
519
| + // Add 1 meter height |
|
| |
520
| + $data = str_replace('#', ',1 ', $data) . ',1'; |
|
| |
521
| + $ml->addChild('tessellate', 1); |
|
| |
522
| + //$element->addChild('altitudeMode', 'relativeToGround'); |
|
| |
523
| + $element->addChild('altitudeMode', 'clampToGround'); |
|
| |
524
| + $ob = $ml->addChild('outerBoundaryIs'); |
|
| |
525
| + $ls = $ob->addChild('LinearRing'); |
|
| |
526
| + $ls->addChild('coordinates', $data); |
|
| |
527
| + } |
|
| |
528
| + } |
|
| |
529
| + |
|
| |
530
| + |
|
| |
531
| + /** |
|
| |
532
| + * Add a multipolygon |
|
| |
533
| + * FIXME: untested, should take holes into account |
|
| |
534
| + */ |
|
| |
535
| + function add_multipolygon(&$wkt, &$element, $featurename){ |
|
| |
536
| + $ml = $element->addChild('MultiGeometry'); |
|
| |
537
| + foreach(split('\), \(', $wkt) as $line){ |
|
| |
538
| + $this->add_polygon($line, $ml, $featurename ); |
|
| |
539
| + } |
|
| |
540
| + } |
|
| |
541
| + |
|
| |
542
| + |
|
| |
543
| + /** |
|
| |
544
| + * Get the feature description |
|
| |
545
| + */ |
|
| |
546
| + function get_feature_description($featurename, $attributes, $description_template){ |
|
| |
547
| + // Compute hyperlink |
|
| |
548
| + if($description_template){ |
|
| |
549
| + $description = $description_template; |
|
| |
550
| + foreach($attributes as $k => $val){ |
|
| |
551
| + $description = str_replace("%$k%", iconv($this->encoding, 'utf-8', $val), $description); |
|
| |
552
| + } |
|
| |
553
| + } else { |
|
| |
554
| + $description = iconv($this->encoding, 'utf-8', $featurename); |
|
| |
555
| + } |
|
| |
556
| + return htmlentities($description); |
|
| |
557
| + } |
|
| |
558
| + |
|
| |
559
| + |
|
| |
560
| + /** |
|
| |
561
| + * Parse classes |
|
| |
562
| + * @return array hash of 'style_id' => style_data) |
|
| |
563
| + */ |
|
| |
564
| + function parse_classes(&$layer, &$folder, &$namecol, &$title_field, &$description_template ){ |
|
| |
565
| + $style_ar = array(); |
|
| |
566
| + $numclasses = $layer->numclasses; |
|
| |
567
| + for($i = 0; $i < $numclasses; $i++){ |
|
| |
568
| + $class = $layer->getClass($i); |
|
| |
569
| + $label = $class->label; |
|
| |
570
| + if($label){ |
|
| |
571
| + $style['label_color'] = $label->color; |
|
| |
572
| + $style['label_size'] = $label->size; |
|
| |
573
| + } |
|
| |
574
| + // Get styles |
|
| |
575
| + for($j = 0; $j < $class->numstyles; $j++){ |
|
| |
576
| + $_style = $class->getStyle($j); |
|
| |
577
| + $style['color'] = $_style->color; |
|
| |
578
| + $style['outlinecolor'] = $_style->outlinecolor; |
|
| |
579
| + $style['width'] = $_style->size; // Lines |
|
| |
580
| + $style['backgroundcolor'] = $_style->backgroundcolor; |
|
| |
581
| + $style['icon'] = $this->get_icon_url($layer, $class->name); |
|
| |
582
| + $style['icon_width'] = $_style->size; // Points |
|
| |
583
| + |
|
| |
584
| + } |
|
| |
585
| + $style['expression'] = $class->getExpression(); |
|
| |
586
| + // Set description_template if any |
|
| |
587
| + $style['description_template'] = $description_template; |
|
| |
588
| + // Get icon for lines if any |
|
| |
589
| + if($icon = $layer->getMetadata('KML_ADD_POINT')){ |
|
| |
590
| + $style['icon'] = $icon; |
|
| |
591
| + $style['icon_width'] = 32; |
|
| |
592
| + } |
|
| |
593
| + // Create style element |
|
| |
594
| + $style_id = preg_replace('/[^A-z0-9]/', '_', $layer->name . $class->name); |
|
| |
595
| + //$this->add_style($layer, $folder, $style_id, $style, $namecol, $title_field ); |
|
| |
596
| + // create folder if more than one class |
|
| |
597
| + if($numclasses > 1){ |
|
| |
598
| + $style['folder'] =& $class->name; |
|
| |
599
| + //$folder->addChild('Folder'); |
|
| |
600
| + //$style['folder']->addChild('name', $class->name); |
|
| |
601
| + } |
|
| |
602
| + $style_ar[$style_id] = $style; |
|
| |
603
| + } |
|
| |
604
| + return $style_ar; |
|
| |
605
| + } |
|
| |
606
| + |
|
| |
607
| + /** |
|
| |
608
| + * Return a CSV list of all layer names in the mapfile |
|
| |
609
| + * FIXME: filter out ANNOTATIONS and other "strange" layers |
|
| |
610
| + */ |
|
| |
611
| + function get_layer_list(){ |
|
| |
612
| + $layer_list = array(); |
|
| |
613
| + for($i = 0; $i < $this->map_object->numlayers; $i++){ |
|
| |
614
| + $layer =& $this->map_object->getLayer($i); |
|
| |
615
| + $kml_skip = $layer->getMetadata('KML_SKIP'); |
|
| |
616
| + if(strtolower($kml_skip) !== 'true'){ |
|
| |
617
| + $layer_list[] = $layer->name; |
|
| |
618
| + } |
|
| |
619
| + } |
|
| |
620
| + return join(',', $layer_list); |
|
| |
621
| + } |
|
| |
622
| + |
|
| |
623
| + |
|
| |
624
| + /** |
|
| |
625
| + * Return the class for the shape, default to last class if not match |
|
| |
626
| + */ |
|
| |
627
| + function get_shape_class(&$classitem, &$values, &$class_list){ |
|
| |
628
| + //var_dump($class_list); die(); |
|
| |
629
| + foreach($class_list as $style_id => $style_data){ |
|
| |
630
| + if($style_data['expression'] && preg_match($style_data['expression'], $values[$classitem])){ |
|
| |
631
| + //print "get_shape_class($classitem) ".$values[$classitem]." matches<br>"; |
|
| |
632
| + return $style_id; |
|
| |
633
| + } |
|
| |
634
| + } |
|
| |
635
| + //print "get_shape_class($classitem) ".$values[$classitem]." no matches<br>"; |
|
| |
636
| + return $style_id; |
|
| |
637
| + } |
|
| |
638
| + |
|
| |
639
| + /** |
|
| |
640
| + * Add the style |
|
| |
641
| + * @return the style URL |
|
| |
642
| + */ |
|
| |
643
| + function add_style(&$layer, &$folder, $style_id, &$style_data){ |
|
| |
644
| + // Calculare style URL |
|
| |
645
| + /* |
|
| |
646
| + if($style_data['description_template']){ |
|
| |
647
| + $this->style_counter++; |
|
| |
648
| + $style_id .= '_'.$this->style_counter; |
|
| |
649
| + $balloon_data = $this->get_feature_description($attributes[$namecol], $attributes, $style_data['description_template']); |
|
| |
650
| + } |
|
| |
651
| + */ |
|
| |
652
| + // Check if the style already exists |
|
| |
653
| + $expr = '//*[@id=\''.$style_id.'\']'; |
|
| |
654
| + if($folder->xpath($expr)) { |
|
| |
655
| + return $style_id; |
|
| |
656
| + } |
|
| |
657
| + $new_style =& $folder->addChild('Style'); |
|
| |
658
| + $new_style['id'] = $style_id; |
|
| |
659
| + // Switch layer type |
|
| |
660
| + switch($layer->type){ |
|
| |
661
| + case MS_LAYER_POINT: |
|
| |
662
| + $this->add_style_point($new_style, $style_data); |
|
| |
663
| + break; |
|
| |
664
| + case MS_LAYER_POLYGON: |
|
| |
665
| + $this->add_style_polygon($new_style, $style_data); |
|
| |
666
| + break; |
|
| |
667
| + case MS_LAYER_LINE: |
|
| |
668
| + $this->add_style_line($new_style, $style_data); |
|
| |
669
| + // Add KML_ADD_POINT icon |
|
| |
670
| + if($style_data['icon']){ |
|
| |
671
| + $this->add_style_point($new_style, $style_data); |
|
| |
672
| + } |
|
| |
673
| + break; |
|
| |
674
| + } |
|
| |
675
| + return $style_id; |
|
| |
676
| + } |
|
| |
677
| + |
|
| |
678
| + /** |
|
| |
679
| + * Add style for lines |
|
| |
680
| + */ |
|
| |
681
| + function add_style_line(&$new_style, &$style_data){ |
|
| |
682
| + if($style_data['outlinecolor']->red != -1){ |
|
| |
683
| + $st =& $new_style->addChild('LineStyle'); |
|
| |
684
| + $st->addChild('color', sprintf('FF%02X%02X%02X', $style_data['outlinecolor']->blue, $style_data['outlinecolor']->green, $style_data['outlinecolor']->red)); |
|
| |
685
| + if($width) { |
|
| |
686
| + $st->addChild('width', $width); |
|
| |
687
| + } |
|
| |
688
| + } elseif($style_data['color']->red != -1){ |
|
| |
689
| + $st =& $new_style->addChild('LineStyle'); |
|
| |
690
| + $st->addChild('color', sprintf('FF%02X%02X%02X', $style_data['color']->blue, $style_data['color']->green, $style_data['color']->red)); |
|
| |
691
| + if($width) { |
|
| |
692
| + $st->addChild('width', $width); |
|
| |
693
| + } |
|
| |
694
| + } |
|
| |
695
| + } |
|
| |
696
| + |
|
| |
697
| + /** |
|
| |
698
| + * Add style for points |
|
| |
699
| + */ |
|
| |
700
| + function add_style_point(&$new_style, &$style_data){ |
|
| |
701
| + if($style_data['icon']){ |
|
| |
702
| + $st =& $new_style->addChild('IconStyle'); |
|
| |
703
| + if($style_data['width'] && $style_data['icon_width'] != 32){ |
|
| |
704
| + $st->addChild('scale', $style_data['icon_width'] / 32); |
|
| |
705
| + } |
|
| |
706
| + $icon =& $st->addChild('Icon'); |
|
| |
707
| + $icon->addChild('href', htmlentities($style_data['icon'])); |
|
| |
708
| + } |
|
| |
709
| + /*/ Add the balloon style if description_template is set |
|
| |
710
| + if($style_data['description_template']){ |
|
| |
711
| + $this->add_balloon_style($new_style, $balloon_data); |
|
| |
712
| + } |
|
| |
713
| + */ |
|
| |
714
| + // Label size and color |
|
| |
715
| + if($style_data['label_size'] || $style_data['label_color']){ |
|
| |
716
| + $ls =& $new_style->addChild('LabelStyle'); |
|
| |
717
| + if($style_data['label_size'] != -1 && $style_data['label_size'] != 32){ |
|
| |
718
| + $ls->addChild('scale', $style_data['label_size'] / 32); |
|
| |
719
| + } |
|
| |
720
| + if($style_data['label_color']->red != -1){ |
|
| |
721
| + $ls->addChild('color', sprintf('FF%02X%02X%02X', $style_data['label_color']->blue, $style_data['label_color']->green, $style_data['label_color']->red)); |
|
| |
722
| + } |
|
| |
723
| + } |
|
| |
724
| + } |
|
| |
725
| + |
|
| |
726
| + /** |
|
| |
727
| + * Add style for polygons |
|
| |
728
| + */ |
|
| |
729
| + function add_style_polygon(&$new_style, &$style_data){ |
|
| |
730
| + // Get also outline styles |
|
| |
731
| + $this->add_style_line($new_style, $style_data); |
|
| |
732
| + $st =& $new_style->addChild('PolyStyle'); |
|
| |
733
| + //die(print_r($backgroundcolor, true)); |
|
| |
734
| + if($style_data['backgroundcolor']->red != -1){ |
|
| |
735
| + $st->addChild('color', sprintf('FF%02X%02X%02X', $style_data['backgroundcolor']->blue, $style_data['backgroundcolor']->green, $style_data['backgroundcolor']->red)); |
|
| |
736
| + $st->addChild('fill', 0); |
|
| |
737
| + } else { |
|
| |
738
| + $st->addChild('fill', 0); |
|
| |
739
| + } |
|
| |
740
| + $st->addChild('outline', 1); |
|
| |
741
| + } |
|
| |
742
| + |
|
| |
743
| + /** |
|
| |
744
| + * Add a WMS raster link |
|
| |
745
| + */ |
|
| |
746
| + function add_wms_link(&$folder, &$layer, &$link){ |
|
| |
747
| + // Build up the KML response document. |
|
| |
748
| + $icon =& $folder->addChild('Icon'); |
|
| |
749
| + $icon->addChild('href', $link . 'layers=' . $layer->name); |
|
| |
750
| + //$icon->addChild('viewBoundScale', 1.5); |
|
| |
751
| + $icon->addChild('viewRefreshMode', 'onStop'); |
|
| |
752
| + $llbox =& $folder->addChild('LatLonBox'); |
|
| |
753
| + $ext = $this->map_object->extent; |
|
| |
754
| + $ext->project($this->in_proj, $this->out_proj); |
|
| |
755
| + $llbox->north = $ext->maxy; |
|
| |
756
| + $llbox->south = $ext->miny; |
|
| |
757
| + $llbox->east = $ext->maxx; |
|
| |
758
| + $llbox->west = $ext->minx; |
|
| |
759
| + // Reset original projection |
|
| |
760
| + $ext->project($this->out_proj, $this->in_proj); |
|
| |
761
| + } |
|
| |
762
| + |
|
| |
763
| + /** |
|
| |
764
| + * Get the url for a point icon |
|
| |
765
| + */ |
|
| |
766
| + function get_icon_url($layer, $classname){ |
|
| |
767
| + return $this->endpoint . '?service=icon&map=' . $this->map . '&typename=' . urlencode($layer->name) . '&classname=' . urlencode($classname); |
|
| |
768
| + } |
|
| |
769
| + |
|
| |
770
| + /** |
|
| |
771
| + * Get the layer description |
|
| |
772
| + */ |
|
| |
773
| + function get_layer_description(&$layer){ |
|
| |
774
| + $description = $layer->getMetadata('DESCRIPTION'); |
|
| |
775
| + if(!$description){ |
|
| |
776
| + $description = $layer->getMetadata('OWS_TITLE'); |
|
| |
777
| + } |
|
| |
778
| + if(!$description){ |
|
| |
779
| + $description = $layer->getMetadata('WFS_TITLE'); |
|
| |
780
| + } |
|
| |
781
| + if(!$description){ |
|
| |
782
| + $description = $layer->getMetadata('WMS_TITLE'); |
|
| |
783
| + } |
|
| |
784
| + if(!$description){ |
|
| |
785
| + $description = $layer->name; |
|
| |
786
| + } |
|
| |
787
| + return $description; |
|
| |
788
| + } |
|
| |
789
| + |
|
| |
790
| + |
|
| |
791
| + |
|
| |
792
| + /** |
|
| |
793
| + * Add style for balloon |
|
| |
794
| + * @param string style XML id |
|
| |
795
| + * @param string column name for title |
|
| |
796
| + */ |
|
| |
797
| + function add_balloon_style(&$style, $balloon_data){ |
|
| |
798
| + $balloon =& $style->addChild('BalloonStyle'); |
|
| |
799
| + $balloon->addChild('text', htmlentities($balloon_data)); |
|
| |
800
| + } |
|
| |
801
| + |
|
| |
802
| + |
|
| |
803
| + /** |
|
| |
804
| + * Get a request parameter |
|
| |
805
| + * @param string $name |
|
| |
806
| + * @param string $default parameter optional |
|
| |
807
| + * @return string the parameter value or empty string if null |
|
| |
808
| + */ |
|
| |
809
| + function load_parm($name, $default = ''){ |
|
| |
810
| + if(!isset($_REQUEST[$name])) return $default; |
|
| |
811
| + $value = $_REQUEST[$name]; |
|
| |
812
| + if(get_magic_quotes_gpc() != 1) $value = addslashes($value); |
|
| |
813
| + //$value = escapeshellcmd($value); |
|
| |
814
| + return $value; |
|
| |
815
| + } |
|
| |
816
| + |
|
| |
817
| + /** |
|
| |
818
| + * Set error message |
|
| |
819
| + * @param string $message |
|
| |
820
| + * @param string $layer name |
|
| |
821
| + */ |
|
| |
822
| + function set_error($message, $layer = 'Error'){ |
|
| |
823
| + $this->errors[$layer][] = $message; |
|
| |
824
| + } |
|
| |
825
| + |
|
| |
826
| + |
|
| |
827
| + /** |
|
| |
828
| + * Load the map and create the map instance |
|
| |
829
| + */ |
|
| |
830
| + function load_map(){ |
|
| |
831
| + if(!file_exists($this->map) && is_readable($this->map)){ |
|
| |
832
| + $this->set_error('Cannot read mapfile '. $this->map); |
|
| |
833
| + } else { |
|
| |
834
| + $this->map_object = ms_newMapObj($this->map); |
|
| |
835
| + if(!$this->map_object){ |
|
| |
836
| + $this->set_error('Cannot load mapfile '. $this->map); |
|
| |
837
| + } |
|
| |
838
| + } |
|
| |
839
| + } |
|
| |
840
| + |
|
| |
841
| + /** |
|
| |
842
| + * Test if has errors |
|
| |
843
| + * @return boolean |
|
| |
844
| + */ |
|
| |
845
| + function has_error(){ |
|
| |
846
| + return count($this->errors) > 0; |
|
| |
847
| + } |
|
| |
848
| + |
|
| |
849
| + /** |
|
| |
850
| + * Add error messages to folders TAGS |
|
| |
851
| + */ |
|
| |
852
| + function add_errors(){ |
|
| |
853
| + foreach($this->errors as $layer => $errors){ |
|
| |
854
| + $folder =& $this->_xml->Document->addChild('Folder'); |
|
| |
855
| + $folder->addChild('name', $layer); |
|
| |
856
| + $folder->addChild('description', '<p>' . join("</p>\n<p>", $errors) . "</p>"); |
|
| |
857
| + } |
|
| |
858
| + return $errorxml; |
|
| |
859
| + } |
|
| |
860
| + |
|
| |
861
| + /** |
|
| |
862
| + * Fetch XML and format it |
|
| |
863
| + */ |
|
| |
864
| + function get_kml(){ |
|
| |
865
| + $doc = new DOMDocument('1.0'); |
|
| |
866
| + $doc->formatOutput = true; |
|
| |
867
| + $domnode = dom_import_simplexml($this->_xml); |
|
| |
868
| + $domnode = $doc->importNode($domnode, true); |
|
| |
869
| + $domnode = $doc->appendChild($domnode); |
|
| |
870
| + return $doc->saveXML(); |
|
| |
871
| + } |
|
| |
872
| + |
|
| |
873
| + /** |
|
| |
874
| + * Send header |
|
| |
875
| + */ |
|
| |
876
| + function send_header(){ |
|
| |
877
| + header('Content-type: application/vnd.google-earth.km'.($this->_zipped ? 'z' : 'l').'+XML'); |
|
| |
878
| + } |
|
| |
879
| + |
|
| |
880
| + /** |
|
| |
881
| + * Calculate cache file name |
|
| |
882
| + */ |
|
| |
883
| + function get_cache_file_name(){ |
|
| |
884
| + return 'cache/'. md5($_SERVER['QUERY_STRING']) . ($this->_zipped ? '.kmz' : '.kml'); |
|
| |
885
| + } |
|
| |
886
| + |
|
| |
887
| + /** |
|
| |
888
| + * Send stream |
|
| |
889
| + */ |
|
| |
890
| + function send_stream($data){ |
|
| |
891
| + $this->send_header(); |
|
| |
892
| + // Compress data |
|
| |
893
| + if($this->_zipped){ |
|
| |
894
| + include("zip.class.php"); |
|
| |
895
| + $ziper = new zipfile(); |
|
| |
896
| + $ziper->addFile($data, 'doc.kml'); |
|
| |
897
| + $data = $ziper->file(); |
|
| |
898
| + } |
|
| |
899
| + // Create cache if needed |
|
| |
900
| + if(ENABLE_CACHE && count($this->layers) == 1 && $this->layers[$this->typename]->getMetadata('KML_CACHE')) { |
|
| |
901
| + error_log( 'creating cache ' . $this->get_cache_file_name() ); |
|
| |
902
| + file_put_contents($this->get_cache_file_name(), $data); |
|
| |
903
| + } |
|
| |
904
| + print $data; |
|
| |
905
| + exit(); |
|
| |
906
| + } |
|
| |
907
| +} |
|
| |
908
| +?> |
|
0
| \ No newline at end of file |
909
| \ No newline at end of file |