...
...
@@ -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
909
\ No newline at end of file
...
...