/[drupal]/contributions/modules/nicemap/nicemap.module
ViewVC logotype

Contents of /contributions/modules/nicemap/nicemap.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.3 - (show annotations) (download) (as text)
Tue Dec 2 02:57:43 2008 UTC (11 months, 3 weeks ago) by jmiccolis
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-6--1
Changes since 1.2: +2 -2 lines
File MIME type: text/x-php
Using proper epsg code for the mercator projection
1 <?php
2 // $Id: nicemap.module,v 1.2 2008/11/14 20:23:59 jmiccolis Exp $
3
4 /**
5 * Nicemap - A WMS client and map generator for Drupal
6 *
7 * This module stores a few variables
8 *
9 * nicemap_map_url The base URL, with no querystring
10 * nicemap_c A serialized array of pertinent information gleaned
11 * from ?request=GetCapabilities
12 *
13 */
14
15 /**
16 * Implementation of hook_menu().
17 */
18 function nicemap_menu() {
19 $items = array();
20 $items['admin/settings/nicemap'] = array(
21 'title' => 'Nicemap Configuration',
22 'description' => 'Test WMS servers with the Nicemap module.',
23 'page callback' => 'drupal_get_form',
24 'page arguments' => array('nicemap_settings'),
25 'access arguments' => array('administer site configuration'),
26 'file' => 'nicemap_admin.inc',
27 'type' => MENU_NORMAL_ITEM,
28 );
29 $items['admin/settings/nicemap/settings'] = array(
30 'type' => MENU_DEFAULT_LOCAL_TASK,
31 'title' => 'Settings',
32 'weight' => -1,
33 );
34 $items['admin/settings/nicemap/cache'] = array(
35 'type' => MENU_LOCAL_TASK,
36 'title' => 'Clear cache',
37 'page callback' => 'drupal_get_form',
38 'page arguments' => array('nicemap_cache_clear'),
39 'access arguments' => array('administer site configuration'),
40 'file' => 'nicemap_admin.inc',
41 );
42 return $items;
43 }
44
45 /**
46 * Implementation of hook_theme
47 */
48 function nicemap_theme($existing, $type, $theme, $path) {
49 return array(
50 'nicemap_map' => array(
51 'file' => 'nicemap.theme.inc',
52 'arguments' => array('points' => array(), 'map' => array(), 'width' => 200, 'height' => 200),
53 ),
54 'nicemap_point' => array(
55 'file' => 'nicemap.theme.inc',
56 'arguments' => array('point' => array()),
57 ),
58 'nicemap_content' => array(
59 'file' => 'nicemap.theme.inc',
60 'arguments' => array('point' => array()),
61 ),
62 'nicemap_settings_layers' => array(
63 'file' => 'nicemap_admin.inc',
64 'arguments' => array('form' => array()),
65 ),
66 );
67 }
68
69 /**
70 * Implementation of hook_views_api
71 */
72 function nicemap_views_api() {
73 return array(
74 'api' => 2,
75 'path' => drupal_get_path('module', 'nicemap') . '/views',
76 );
77 }
78
79 /**
80 * The raw nicemap compatibilities parsing function
81 * @param $base_url the base URL of the WMS server
82 * @return array on success, exception on error
83 */
84 function nicemap_capabilities($base_url) {
85 $capabilities = $base_url .'?request=GetCapabilities&service=WMS';
86
87 // Supress errors caused by offline servers
88 $cxml = @simplexml_load_file($capabilities);
89
90 if (!$cxml) {
91 drupal_set_message('The WMS server could not be reached.');
92 return array();
93 }
94
95 //if ((float) $cxml['version'] < '1.1.1') {
96 // throw new Exception('WMS Server verson must be 1.1.1. The specified server defined '.
97 // $cxml['version'] .' instead.');
98 //}
99 //TODO: This could use more error checking
100 foreach ($cxml->Capability->Request->GetMap->Format as $f) {
101 $file_types[] = (String) $f;
102 }
103
104 // Overall info
105 $c['info'] = array(
106 'name' => (String) $cxml->Service->Name,
107 'title' => (String) $cxml->Service->Title,
108 'abstract' => (String) $cxml->Service->Abstract,
109 'filetypes' => (String) $file_types);
110
111 if ($layers = $cxml->Capability->Layer->Layer) {
112 // Master layer info
113 if (isset($cxml->Capability->Layer->CRS)) {
114 $c['crs'][] = (String) $cxml->Capability->Layer->CRS;
115 }
116 // Grab information particular to each layer
117 foreach ($layers as $l) {
118 $c['layers'][(String) $l->Name] = array(
119 'title' => (String) $l->Title,
120 'bounds' =>
121 array(
122 'minx' => (float) $l->LatLonBoundingBox['minx'],
123 'miny' => (float) $l->LatLonBoundingBox['miny'],
124 'maxx' => (float) $l->LatLonBoundingBox['maxx'],
125 'maxy' => (float) $l->LatLonBoundingBox['maxy']),
126 );
127 if ((String) $l->SRS) {
128 $c['layers'][(String) $l->Name]['srs'] = (String) $l->SRS;
129 }
130 if ($l->Style) {
131 foreach ($l->Style as $style) {
132 $c['layers'][(String) $l->Name]['styles'][(String) $style->Name] = (String) $style->Title;
133 }
134 // Sort styles
135 ksort($c['layers'][(String) $l->Name]['styles']);
136 }
137 }
138 }
139 return $c;
140 }
141
142 /**
143 * A proxy to the nicemap compatibilities cache
144 * @param $base_url the base URL of the WMS server
145 * @return array on success, false on error
146 */
147 function nicemap_capabilities_cache($base_url = NULL, $reset = false) {
148 $base_url = $base_url ? $base_url : variable_get('nicemap_wms_url', '');
149 $cache = variable_get('nicemap_cache', array());
150 if (!isset($cache[$base_url]) || $reset) {
151 $cache[$base_url] = nicemap_capabilities($base_url);
152 variable_set('nicemap_cache', $cache);
153 }
154 return $cache[$base_url];
155 }
156
157 /**
158 * Helper function to retrieve layers & style information and order by custom weight.
159 */
160 function _nicemap_get_layers($spec = NULL) {
161 $layers = array();
162 if ($spec) {
163 if (count($spec['layers'])) {
164 $layers = $spec['layers'];
165 $weights = variable_get('nicemap_wms_weights', array());
166 foreach ($weights as $layer => $weight) {
167 if (isset($layers[$layer])) {
168 $layers[$layer]['#weight'] = $weight;
169 }
170 }
171 }
172 }
173 else {
174 $styles = variable_get('nicemap_wms_styles', array());
175 $weights = variable_get('nicemap_wms_weights', array());
176 foreach (variable_get('nicemap_wms_layers', array()) as $layer => $enabled) {
177 if ($enabled) {
178 $layers[$layer] = array(
179 'style' => $styles[$layer],
180 '#weight' => isset($weights[$layer]) ? $weights[$layer] : 0,
181 );
182 }
183 }
184 }
185 uasort($layers, 'element_sort');
186 return $layers;
187 }
188
189 /**
190 * Helper function that provides a new projection object.
191 */
192 function nicemap_map_projection($type) {
193 switch ($type) {
194 case 'equirectangular':
195 $projection = new nicemap_equirectangular_projection();
196 return $projection;
197 case 'mercator':
198 $projection = new nicemap_mercator_projection();
199 return $projection;
200 }
201 return false;
202 }
203
204 /**
205 * Helper function that determines the correct projection.
206 */
207 function nicemap_get_projection() {
208 $projections = nicemap_projections();
209
210 switch (variable_get('nicemap_mode', '')) {
211 case 'wms':
212 $p = variable_get('nicemap_wms_crs', false);
213 break;
214 case 'file':
215 $p = variable_get('nicemap_file_projection', false);
216 break;
217 }
218
219 if ($p && isset($projections[$p])) {
220 return new $projections[$p]['class']();
221 }
222 return false;
223 }
224
225 /**
226 * Provides an array of projection options -- usable for form selects.
227 */
228 function nicemap_projections() {
229 return array(
230 'EPSG:3395' => array(
231 'mercator' => t('Mercator'),
232 'class' => 'nicemap_mercator_projection',
233 ),
234 'EPSG:4326' => array(
235 'name' => t('Equirectangular'),
236 'class' => 'nicemap_equirectangular_projection',
237 ),
238 'EPSG:900913' => array(
239 'mercator' => t('Google Mercator'),
240 'class' => 'nicemap_mercator_projection', // Don't have a definition yet, so we fall back.
241 ),
242 );
243 }
244
245 /**
246 * A map object.
247 */
248 class nicemap_map {
249
250 public $server;
251 public $bgcolor;
252 public $layers = array();
253 public $styles = array();
254 public $bounds = array();
255 public $projection;
256
257 function __construct($load_layers = true) {
258 $this->bgcolor = variable_get('nicemap_wms_bgcolor', '');
259 $this->server = variable_get('nicemap_wms_url', '');
260 $this->projection = nicemap_get_projection();
261
262 if ($load_layers) {
263 foreach (_nicemap_get_layers() as $layer => $info) {
264 $this->layers[] = $layer;
265 $this->styles[] = $info['style'];
266 }
267 }
268 }
269
270 /**
271 * Expand or contract map as needed given coordinates and the desired pixel size
272 *
273 * @param $items
274 * Array of items to display on the map
275 * @param $target
276 * Array, target size of map
277 *
278 * @return
279 * Trimmed array of items to display on the map.
280 */
281 function process($items, $target = array()) {
282 // Preserve original map bounds.
283 $orig_map = $this->bounds;
284 $this->bounds = $this->projection->getmap($this->bounds, $target);
285
286 $points = array();
287 foreach ($items as $item) {
288 if (
289 $item['lat'] > $orig_map['miny'] &&
290 $item['lat'] < $orig_map['maxy'] &&
291 $item['lon'] > $orig_map['minx'] &&
292 $item['lon'] < $orig_map['maxx']
293 ) {
294 list($x, $y) = $this->projection->getpoint($this->bounds, $item);
295 $ratio['y'] = $y / $this->bounds['h'];
296 $ratio['x'] = $x / $this->bounds['w'];
297 $y = 100 - (100 * $ratio['y']);
298 $x = 100 * $ratio['x'];
299 $item['x'] = $x;
300 $item['y'] = $y;
301 $points[] = $item;
302 }
303 }
304 return $points;
305 }
306
307 /**
308 * Generate a new map URL
309 *
310 * TODO Allow options to override.
311 */
312 function url($options = array()) {
313 if ($this->server) {
314 if ($this->styles) {
315 $options['styles'] = implode(',', $this->styles);
316 }
317 if ($this->bgcolor) {
318 $options['bgcolor'] = '0x'. $this->bgcolor;
319 }
320 $options['request'] = 'GetMap';
321 $options['version'] = '1.1.1';
322 $options['format'] = 'image/png';
323 $options['layers'] = implode(',', $this->layers);
324
325 // For now, grab SRS from variables
326 $options['srs'] = variable_get('nicemap_wms_crs', 'EPSG:900913');
327 $options['EXCEPTIONS'] = 'application/vnd.ogc.se_inimage';
328 $options['bbox'] = $this->bounds['minx'].','.$this->bounds['miny'].','.$this->bounds['maxx'].','.$this->bounds['maxy'];
329 $url = array();
330 foreach ($options as $flag => $val) {
331 $url[] = $flag ."=". $val;
332 }
333 $url = implode('&', $url);
334 return url($this->server, array('query' => $url, 'fragment' => null, 'absolute' => TRUE));
335 }
336 else {
337 return;
338 }
339 }
340 }
341
342 /**
343 * Interface for nicemap map projections.
344 */
345 interface nicemap_projection {
346 /**
347 * Get map takes a boundry array and a target size and generates usable coordinates.
348 *
349 * @param $map
350 * @param $target
351 *
352 * @return
353 * a treated map array
354 */
355 public function getmap($map, $target);
356
357 /**
358 * Get point plots a point on a map.
359 *
360 * @param $map
361 * @param $item
362 *
363 * @return
364 * a two element array of x, y.
365 */
366 public function getpoint($map, $item);
367 }
368
369 /**
370 * Provides a mercator projection for nicemap.
371 */
372 class nicemap_mercator_projection implements nicemap_projection {
373
374 public function getmap($map, $target) {
375 // create height in terms of mercator projection
376 // and set an origin height coordinate
377 $map['w'] = deg2rad($map['maxx']) - deg2rad($map['minx']);
378 $map['h'] = asinh(tan(deg2rad($map['maxy']))) - asinh(tan(deg2rad($map['miny'])));
379 $map['o'] = asinh(tan(deg2rad($map['miny'])));
380 $map['ratio'] = $map['w'] / $map['h'];
381
382 if (count($target)) {
383 $target['ratio'] = $target['width'] / $target['height'];
384 // target is wider than map
385 if ($target['ratio'] >= $map['ratio']) {
386 // calculate new width from ratio
387 $new_w = $map['h'] * $target['ratio'];
388
389 // Adjust longitudes
390
391 // We resize the map sides in radians
392 $map['minx'] = deg2rad($map['minx']) - ($new_w - $map['w']) * .5;
393 $map['maxx'] = deg2rad($map['maxx']) + ($new_w - $map['w']) * .5;
394
395 // Check to see whether the boundaries are past +-180deg bounds (pi radians)
396 // If they are, pull it in to 180deg and compensate the difference on the other bound.
397
398 // If both bounds are over the limit, we set to -180 to 180 -- at least the map will not totally break...
399 if (($map['minx'] < (M_PI * -1)) && ($map['maxx'] > M_PI)) {
400 $map['minx'] = M_PI * -1;
401 $map['maxx'] = M_PI;
402 }
403 else if ($map['minx'] < (M_PI * -1)) {
404 $diff = $map['minx'] - (M_PI * -1);
405 $map['minx'] = $map['minx'] - $diff;
406 $map['maxx'] = $map['maxx'] - $diff;
407 }
408 else if ($map['maxx'] > M_PI) {
409 $diff = ($map['maxx'] - M_PI);
410 $map['maxx'] = $map['maxx'] - $diff;
411 $map['minx'] = $map['minx'] - $diff;
412 }
413
414 // Convert bounds to degrees
415 $map['miny'] = rad2deg($map['miny']);
416 $map['maxy'] = rad2deg($map['maxy']);
417
418 // overwrite width + ratio
419 $map['w'] = $new_w;
420 $map['ratio'] = $target['ratio'];
421 }
422 // target is taller than map
423 else {
424 // 1. we know the map is wide enough
425 // 2. we need to find the necessary radian height to match ratios
426 // 3. once we have the radian height, we need to adjust the bounding latitudes
427 // @TODO: make sure that we don't adjust past the poles...
428
429 // subtract half the height from top to find midpoint
430 $midpoint = asinh(tan(deg2rad($map['maxy']))) - ($map['h']*.5);
431
432 // set map height to match target height
433 $map['h'] = $map['w'] / $target['ratio']; // 2
434
435 // find origin in radians
436 $map['o'] = $midpoint - ($map['h']*.5);
437
438 // find new latitude bounds by converting radian bounds to mercator degrees
439 $map['maxy'] = rad2deg(atan(sinh($midpoint + ($map['h']*.5))));
440 $map['miny'] = rad2deg(atan(sinh($midpoint - ($map['h']*.5))));
441 }
442 }
443 $map['ratio'] = $map['w'] / $map['h'];
444
445 return $map;
446 }
447
448 public function getpoint($map, $item) {
449 $y = asinh(tan(deg2rad($item['lat']))) - $map['o'];
450 $x = deg2rad($item['lon']) - deg2rad($map['minx']);
451
452 return array($x, $y);
453 }
454 }
455
456 /**
457 * Provides a equirectangular projection for nicemap.
458 *
459 * Simply convert all degrees into radians and press "ALL SYSTEMS GO"
460 *
461 * TODO test!
462 */
463 class nicemap_equirectangular_projection implements nicemap_projection {
464
465 public function getmap($map, $target) {
466 $map['w'] = deg2rad($map['maxx']) - deg2rad($map['minx']);
467 $map['h'] = deg2rad($map['maxy']) - deg2rad($map['miny']);
468
469 $map['o'] = deg2rad($map['miny']);
470 $map['ratio'] = $map['w'] / $map['h'];
471
472 if (count($target)) {
473 $target['ratio'] = $target['width'] / $target['height'];
474 // target is wider than map
475 if ($target['ratio'] >= $map['ratio']) {
476
477 // calculate new width from ratio
478 $new_w = $map['h'] * $target['ratio'];
479
480 // adjust longitudes
481 $map['minx'] = $map['minx'] - rad2deg(($new_w - $map['w']) * .5);
482 $map['maxx'] = $map['maxx'] + rad2deg(($new_w - $map['w']) * .5);
483 $map['minx'] = ($map['minx'] < -180) ? $map['minx'] = -179.99 : $map['minx'];
484 $map['maxx'] = ($map['maxx'] > 180) ? $map['maxx'] = 179.99 : $map['maxx'];
485
486 // overwrite width + ratio
487 $map['w'] = $new_w;
488 $map['ratio'] = $target['ratio'];
489 }
490 // target is taller than map
491 else {
492 // 1. we know the map is wide enough
493 // 2. we need to find the necessary radian height to match ratios
494 // 3. once we have the radian height, we need to adjust the bounding latitudes
495
496 // subtract half the height from top to find midpoint
497 $midpoint = $map['maxy'] - (rad2deg($map['h'])*.5);
498
499 // set map height to match target height
500 $map['h'] = $map['w'] / $target['ratio']; // 2
501
502 // find origin in radians
503 $map['o'] = deg2rad($midpoint) - ($map['h']*.5);
504
505 // find new latitude bounds by converting radian bounds to mercator degrees
506 $map['maxy'] = $midpoint + rad2deg($map['h']*.5);
507 $map['miny'] = $midpoint - rad2deg($map['h']*.5);
508 }
509 }
510 $map['ratio'] = $map['w'] / $map['h'];
511 return $map;
512 }
513
514 public function getpoint($map, $item) {
515 $x = deg2rad($item['lon']) - deg2rad($map['minx']);
516 $y = deg2rad($item['lat']) - $map['o'];
517
518 return array($x, $y);
519 }
520 }

  ViewVC Help
Powered by ViewVC 1.1.2