line
stmt
bran
cond
sub
pod
time
code
1
package Geo::Google::MapObject;
2
3
4
4
138688
use warnings;
4
9
4
154
4
4
4
18
use strict;
4
7
4
111
5
4
4
19
use Carp;
4
13
4
2814
6
7
=head1 NAME
8
9
Geo::Google::MapObject - Code to help with managing the server side of the Google Maps API
10
11
=head1 VERSION
12
13
Version 0.06
14
15
=cut
16
17
our $VERSION = '0.06';
18
19
=head1 SYNOPSIS
20
21
use HTML::Template::Pluggable;
22
use HTML::Template::Plugin::Dot;
23
use Geo::Google::MapObject;
24
my $map = Geo::Google::MapObject->new(
25
key => 'ABQFbHAATHwok56Qe3MBtg0s7lgkHBS9HKneet7v0OIFhIwnBhTEGCHLTRRRBa_lUOCy1fDamS5PQt8qULYfYQ',
26
zoom => 13,
27
size => '512x400',
28
maptype => 'terrain',
29
markers=>
30
[
31
{
32
location=>'51.242844,0.011716',
33
color=>'green',
34
label=>'P',
35
title=>'Periapt Technologies',
36
href=>'http://www.periapt.co.uk'
37
},
38
{
39
location=>'51.243757,0.006051',
40
color=>'red',
41
label=>'C',
42
title=>'Crown Roast Butchers',
43
href=>'http://www.crownroast.co.uk/'
44
},
45
]
46
);
47
my $template = HTML::Template::Pluggable->new(file=>'map.tmpl');
48
$template->param(map=>$map);
49
return $template->output;
50
51
=head1 DESCRIPTION
52
53
This module is intended to provide a server side solution to working with the Google Maps API.
54
In particular an object of this class encapsulates a "map" object that provides support
55
for the static maps API, the javascript maps API, AJAX calls and non-javascript fallback data;
56
but without making many assumptions about the surrounding framework.
57
We do assume that a template framework with support for a "dot" notation is being used, for example L.
58
An important commitment of the module is support for graceful and consistent fallback to a functional non-javascript web page.
59
60
The javascript and static Google map APIs do not behave in quite the same way when zoom and center are not specified.
61
Specifically it works quite well with the static maps (L)
62
but not so well with the javascript API.
63
To compensate for this the module gives a choice between: specifying the center and zoom levels; allowing the APIs and client side code to
64
do whatever they think best; using a built in algorithm to calculate a sensible zoom and center; and finally supplying ones
65
own algorithm to calculate a sensible zoom and center.
66
67
=head1 INTERFACE
68
69
=head2 new
70
71
Supported arguments are
72
73
=over
74
75
=item autozoom
76
77
If no center and/or zoom is specified this parameter can be used to calculate suitable
78
values by a process of averaging the markers. If this parameter is an integer between 0
79
and 21 then that number is taken as a maximum zoom level and the builtin
80
algorithm, C<< calculate_zoom_and_center >>, is used with that maximum zoom level.
81
If the parameter is a CODE ref, then that function is used instead. The CODE ref must
82
take an ARRAY ref of marker specifications as an input, and must return a pair consisting of the zoom
83
level as an integer followed by a latitude-longitude string representing the center.
84
Finally if the argument is blessed and has a C<< calculate_zoom_and_center >> method,
85
that will be used. For AUTO calculation of the center or zoom all
86
the markers must be in the form "decimal,decimal". The C<< json >> method will delete this
87
as it has no meaning once the zoom level and center have been determined.
88
89
=item center
90
91
This must be a location that would be recognized by the Google maps API.
92
If absent, and if no autozoom has been set, the Google maps API and client side javascript code
93
must between them work out a center.
94
95
=item zoom
96
97
This represents the zoom level (L), which
98
is a number between 0 and 21 inclusive. If absent (with no autozoom set) the API and client will need to set it.
99
100
=item size
101
102
The size (L) must either be a string
103
consisting of two numbers joined by the symbol C, or a hash ref with width and height parameters. In either
104
case the first number is the width and the second the height. If absent the Google API will be allowed to set
105
the size as it sees fit.
106
107
=item format
108
109
The optional format (L) must be one of
110
'png8', 'png', 'png32', 'gif', 'jpg', 'jpg-baseline'. If absent the Google API will be allowed to set
111
the format as it sees fit. Note that although the C<< json >> method will pass this on, it seems to have no meaning
112
in the dynamic API.
113
114
=item maptype
115
116
This must be one of the following: 'roadmap', 'satellite', 'terrain', 'hybrid's described in L. The C<< json >> method will translate these into numerical codes roadmap => 0, satellite => 1, terrain => 2 and hybrid => 3
117
as this makes it easy for the javascript code (L) to translate this numerical code into the client side codes. For example:
118
119
var mapping = new Array(G_NORMAL_MAP, G_SATELLITE_MAP, G_PHYSICAL_MAP, G_HYBRID_MAP);
120
var maptype = mapping[data.maptype];
121
122
=item mobile
123
124
This parameter should be either the string 'true' or 'false' and defaults to 'false'. The static API uses it to provide more
125
robust tiles as described in L. However it seems to have no meaning
126
for the dynamic API.
127
128
=item key
129
130
The mandatory API key. You can sign up for one at L.
131
132
=item sensor
133
134
This parameter should be either the string 'true' or 'false' and defaults to 'false'. Both versions of the Google API
135
require it (L) and this is reflected in this module
136
by it being used by the C<< static_map_url >> and C<< javascript_url >> methods.
137
138
=item markers
139
140
If present the markers should be an array ref of marker specifications so that it can be used in a TMPL_LOOP.
141
Each marker specification should be a hash ref and may contain whatever keys are required
142
by the javascript client side code. It must however have a C<< location >> field which must be interpretable
143
by the Google API as a location. In general this means the location must be a string of the form "decimal,decimal",
144
where the first decimal is the latitude and the second longitude.
145
Other fields might include for example: id, title, href, icon. Generally these just get passed through by the C<< json >>
146
method and should be used by the client side code as it sees fit.
147
Three fields that have a special meaning, C<< color >>, C<< size >>, C<< label >> are those described by the static maps API
148
(L).
149
The GIcon class (L) contains lots of ways of specialising
150
the look and feel of the markers in the dynamic API; but I can find no easy way of consistently replicating the
151
the static marker styles in the dynamic API. The only other field which gets special treatment is C<< title >>. The C<< json >>
152
method will encode the title field. So for example if a marker has a title field of C<< 'Scloß' >>, then
153
if the javascript code uses this as the title attribute when creating a GMarker, then hovering the mouse over that marker
154
will display the German word for castle.
155
156
=item hl
157
158
This parameter specifies the language to be used. If absent the API will select the language. Only the dynamic javascript API
159
seems to use this parameter as described in L. As such this
160
is used in the C<< javascript_url >> method.
161
162
=back
163
164
=cut
165
166
my %MAPTYPE = (
167
roadmap=>0,
168
satellite=>1,
169
terrain=>2,
170
hybrid=>3,
171
);
172
173
sub new {
174
27
27
1
14780
my $class = shift;
175
27
138
my %args = @_;
176
27
50
33
140
$args{mobile} = 'false' unless exists $args{mobile} && $args{mobile} eq 'true';
177
27
50
33
131
$args{sensor} = 'false' unless exists $args{sensor} && $args{sensor} eq 'true';
178
27
100
75
if (exists $args{markers}) {
179
12
50
50
croak "markers should be an ARRAY" unless ref($args{markers}) eq "ARRAY";
180
}
181
27
50
72
croak "no API key" unless exists $args{key};
182
27
100
100
134
if (exists $args{markers} && exists $args{autozoom}) {
183
7
31
my ($zoom, $center) = _autocalculate(%args);
184
7
33
44
$args{zoom} ||= $zoom;
185
7
33
39
$args{center} ||= $center;
186
7
21
delete $args{autozoom};
187
}
188
27
50
76
if (exists $args{zoom}) {
189
27
50
33
342
croak "zoom not a number: $args{zoom}" unless ($args{zoom} =~ /^\d{1,2}$/) && $args{zoom} < 22;
190
}
191
27
50
73
if (exists $args{maptype}) {
192
0
0
0
croak "maptype $args{maptype} not recognized" unless exists $MAPTYPE{$args{maptype}};
193
}
194
27
100
73
if (exists $args{size}) {
195
26
76
my ($width, $height) = _parse_size($args{size});
196
17
78
$args{size} = {width=>$width,height=>$height};
197
}
198
18
80
return bless \%args, $class;
199
}
200
201
sub _parse_size {
202
26
26
39
my $size = shift;
203
26
36
my ($width, $height);
204
26
100
135
if (ref($size) eq "HASH") {
100
205
7
66
299
$width = $size->{width} || croak "no width";
206
6
66
170
$height = $size->{height} || croak "no height";
207
}
208
elsif($size =~ /^(\d{1,3})x(\d{1,3})$/) {
209
16
37
$width = $1;
210
16
29
$height = $2;
211
}
212
else {
213
3
469
croak "cannot recognize size";
214
}
215
21
100
100
516
croak "width should positive and be no more than 640" unless ($width > 0 && $width <= 640);
216
19
100
100
380
croak "height should positive and be no more than 640" unless ($height > 0 && $height <= 640);
217
17
51
return ($width, $height);
218
}
219
220
sub _autocalculate {
221
7
7
25
my %args = @_;
222
7
12
my $autozoom = $args{autozoom};
223
7
33
27
my $markers = $args{markers} || croak "cannot calculate autozoom without markers";
224
4
4
25
use Scalar::Util qw(blessed looks_like_number);
4
12
4
1110
225
7
50
33
69
if (looks_like_number($autozoom) && $autozoom >= 0 && $autozoom <= 21) {
0
33
0
0
226
7
22
return calculate_zoom_and_center($markers, $autozoom);
227
}
228
elsif (ref($autozoom) eq "CODE") {
229
0
0
return &$autozoom($markers);
230
}
231
elsif (blessed($autozoom) && $autozoom->can('calculate_zoom_and_center')) {
232
0
0
return $autozoom->calculate_zoom_and_center($markers);
233
}
234
0
0
croak "$autozoom not recognized as autozoom";
235
}
236
237
=head2 calculate_zoom_and_center
238
239
This function tales a reference to an array of marker specifications
240
and a maximum zoom level and returns a pair consisting of a suggested zoom level
241
and a center.
242
243
The algorithm works by iteratively building the following objects:
244
245
=over
246
247
=item A set of points, starting with the first element of the array and adding the next one at each iteration.
248
249
Actually we don't actually have a variable for this, but without thinking about this set conceptually
250
I cannot state the algorithm. The set starts of consisting of just the first marker location.
251
252
=item C<< ($ctheta, $cphi) >>
253
254
These numbers represent a central point of the set of points represented as radians. The algorithm does not guarantee
255
that the point is the true centre but we treat it as the centre of a circle. This starts of by taking the first element
256
and converting its latitude and longitude to radians.
257
258
=item C<< $radius >>
259
260
This variable is initialised to 0.
261
262
=item The contract
263
264
The algorithm requires that at each iteration all the points in the set are at most $radius radians from
265
($ctheta, $cphi).
266
267
=item The iteration
268
269
We look at the next point. We look at the distance (C<< $distance >>) between the new point and C<< ($ctheta, $cphi) >>. If this is less than or
270
equal to C<< $radius >> the iteration is over. Otherwise the following happens:
271
272
($ctheta, $cphi) = mid point of ($ctheta, $cphi) and the new point
273
$radius = $radius + 0.5 * distance between ($ctheta, $cphi) and the new point
274
275
=item At the end
276
277
We convert C<< ($ctheta, $cphi) >> back to latitude and longitude and logarithmically convert the radius to a zoom level.
278
279
=back
280
281
I doubt that this algorithm is optimal but it seems quick, uses only one pass through the markers and can be seen in action
282
at L. Also if you have a better one you can specify it by passing a suitable CODE ref or blessed
283
scalar to the C<< autozoom >> parameter of the constructor.
284
285
=cut
286
287
sub calculate_zoom_and_center {
288
7
7
1
11
my $markers = shift;
289
7
11
my $maxautozoom = shift;
290
291
4
4
4413
use Math::Trig qw(deg2rad great_circle_distance great_circle_midpoint rad2deg);
4
80236
4
3980
292
# At the end we guarantee that any two points are less than $distance apart
293
# and that ($ctheta, $cphi) is (more or less) in the middle.
294
7
11
my ($ctheta, $cphi);
295
7
8
my $distance = 0; # 2*$radius
296
7
12
my $firstpoint = 1;
297
298
7
11
foreach my $l (@{$markers}) {
7
14
299
18
50
50
croak "location missing" unless exists $l->{location};
300
18
50
104
if ($l->{location} =~ /^(\-?\d+\.?\d*),(\-?\d+\.?\d*)$/) {
301
18
84
my $phi = deg2rad(90-$1);
302
18
204
my $theta = deg2rad($2);
303
304
18
100
146
if ($firstpoint) {
305
7
10
$cphi = $phi;
306
7
9
$ctheta = $theta;
307
7
15
$firstpoint = 0;
308
}
309
else {
310
11
33
my $new_distance = great_circle_distance($theta, $phi, $ctheta, $cphi);
311
11
100
291
if ($new_distance > $distance) {
312
8
14
$distance = $new_distance + $distance/2;
313
8
26
my ($mtheta, $mphi) = great_circle_midpoint($theta, $phi, $ctheta, $cphi);
314
8
50
33
469
if (defined $mtheta && defined $mphi ) {
315
8
12
$ctheta = $mtheta;
316
8
19
$cphi = $mphi;
317
}
318
else {
319
0
0
return (0, "0,0");
320
}
321
}
322
}
323
}
324
}
325
7
15
my $zoom = $maxautozoom;
326
7
100
42
$zoom = int -(log $distance)/(log 2) if $distance > 0; ## no cr itic
327
7
50
20
$zoom = 0 if $zoom < 0;
328
7
100
16
$zoom = $maxautozoom if $zoom > $maxautozoom;
329
7
24
my $longitude = rad2deg($ctheta);
330
7
165
my $latitude = 90-rad2deg($cphi);
331
7
192
return ($zoom, "$latitude,$longitude");
332
}
333
334
=head2 static_map_url
335
336
Returns a URL suitable for use with the static maps API based upon the arguments passed to the constructor.
337
338
=cut
339
340
sub static_map_url {
341
18
18
1
6872
my $self = shift;
342
18
35
my $url = "http://maps.google.com/maps/api/staticmap?";
343
18
25
my @params;
344
345
# First the easy parameters
346
18
45
foreach my $i (qw(center zoom format mobile key sensor)) {
347
108
100
387
push @params, "$i=$self->{$i}" if exists $self->{$i};
348
}
349
18
100
117
push @params, "size=$self->{size}->{width}x$self->{size}->{height}" if exists $self->{size};
350
351
18
100
54
if (exists $self->{markers}) {
352
# Now sort the markers
353
12
20
my %markers;
354
12
14
foreach my $m (@{$self->{markers}}) {
12
27
355
34
34
my @style;
356
34
100
66
149
push @style, "color:$m->{color}" if exists $m->{color} && $m->{color} =~
357
/^0x[A-F0-9]{6}|black|brown|green|purple|yellow|blue|gray|orange|red|white$/;
358
34
100
66
110
push @style, "size:$m->{size}" if exists $m->{size} && $m->{size} =~ /^tiny|mid|small$/;
359
34
100
66
100
push @style, "label:$m->{label}" if exists $m->{label} && $m->{label} =~ /^[A-Z0-9]$/;
360
34
55
my $style = join "|", @style;
361
34
33
33
push @{$markers{$style}}, $m->{location} || croak "no location for $style";
34
172
362
}
363
12
49
foreach my $m (sort keys %markers) {
364
19
30
my $param = "markers=";
365
19
100
47
$param .= "$m|" if $m;
366
19
22
$param .= join '|', @{$markers{$m}};
19
50
367
19
64
push @params, $param;
368
}
369
}
370
371
18
103
$url .= join "&", @params;
372
18
113
return $url;
373
}
374
375
=head2 javascript_url
376
377
Returns a URL suitable for use in loading the dynamic map API.
378
379
=cut
380
381
sub javascript_url {
382
18
18
1
1491
my $self = shift;
383
18
66
my $url = "http://maps.google.com/maps?file=api&v=2&key=$self->{key}&sensor=$self->{sensor}";
384
18
100
60
$url .= "&hl=$self->{hl}" if exists $self->{hl};
385
18
102
return $url;
386
}
387
388
=head2 markers
389
390
This returns the marker array ref.
391
392
=cut
393
394
sub markers {
395
6
6
1
8470
my $self = shift;
396
6
100
41
return $self->{markers} || [];
397
}
398
399
=head2 json
400
401
This function uses the L module to return a JSON representation of the object.
402
It removes the API key as that should not be required by any javascript client side code.
403
If any marker object has a title attribute, then that attribute is encoded so it will display
404
correctly during mouse overs.
405
406
=cut
407
408
sub json {
409
17
17
1
11379
my $self = shift;
410
4
4
5012
use JSON;
4
55188
4
22
411
17
112
my %args = %$self;
412
17
47
delete $args{key};
413
4
4
5125
use HTML::Entities;
4
28746
4
1612
414
17
50
58
$args{maptype} = $MAPTYPE{$args{maptype}} if exists $args{maptype};
415
17
27
foreach my $i (0..$#{$args{markers}}) {
17
70
416
34
50
104
$args{markers}[$i]->{title} = decode_entities($args{markers}[$i]->{title}) if exists $args{markers}[$i]->{title};
417
}
418
17
139
return to_json(\%args, {utf8 => 1, allow_blessed => 1});
419
}
420
421
=head2 width
422
423
This returns the width of the image or undef if none has been set.
424
425
=cut
426
427
sub width {
428
18
18
1
71139
my $self = shift;
429
18
100
83
return undef unless exists $self->{size};
430
17
104
return $self->{size}->{width};
431
}
432
433
=head2 height
434
435
This returns the height of the image or undef if none has been set.
436
437
=cut
438
439
sub height {
440
18
18
1
1501
my $self = shift;
441
18
100
69
return undef unless exists $self->{size};
442
17
80
return $self->{size}->{height};
443
}
444
445
=head1 TUTORIAL
446
447
This module only covers the server side of a two way conversation between server and a talkative client.
448
As such the tutorial must also consider HTML and javascript.
449
Also to understand this tutorial we assume at least passing familiarity with the following:
450
451
=over
452
453
=item L or some other templating framework using the "dot" notation.
454
455
=item The Google maps API: L.
456
457
=item The static Google maps API: L
458
459
=item Javascript, AJAX. Specifically we are using the yui framework (L ) though any other framework will do.
460
461
=back
462
463
=head2 The markup
464
465
We probably have a template file, map.tmpl, some thing like the following:
466
467
468
469
470
Test Map Tutorial
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
494
495
alt="Test map"
496
src=""
497
width=""
498
height=""
499
/>
500
501
502
506
507
523
524
525
526
527
528
529
530
=head2 The perl
531
532
The code to actually produce the web page must look something like this:
533
534
use HTML::Template::Pluggable;
535
use HTML::Template::Plugin::Dot;
536
use Geo::Google::MapObject;
537
my $t = HTML::Template::Pluggable->new(filename => 'map.tmpl');
538
my $map = Geo::Google::MapObject->new(....);
539
$t->param(maps=>$map);
540
print $t->output;
541
542
This is all that would be needed in a javascript free environment. With javascript
543
enabled the client is going to ask for the same data in a JSON format (at least
544
we are only supporting JSON). The code to service that request will look something like:
545
546
use Geo::Google::MapObject;
547
my $map = Geo::Google::MapObject->new(....);
548
print $map->json;
549
550
=head2 The javascript
551
552
As indicated in the markup above we are expecting the javascript to be in a file called maps.js.
553
We expect it to look something like the following:
554
555
function mapInitialize() {
556
557
/* Read the "link" element that tells us how to contact the server. See markup above. */
558
var get_init_data = YAHOO.util.Dom.get('get_init_data').href;
559
560
/* Build the AJAX callback object consisting of three parts. */
561
var callback =
562
{
563
564
/* Part I: Function called in the event of a successful call. */
565
success: function(o) {
566
567
/* Convert the JSON response into a javascript object.
568
This structure corresponds to the Geo::Google::MapObject in perl
569
as intermediated by its "json" method.
570
*/
571
var data = YAHOO.lang.JSON.parse(o.responseText);
572
573
if (GBrowserIsCompatible()) {
574
575
/* Build up the Map object which will replace #map_canvas in the above markup. */
576
var mapopt = {};
577
if (data.size) {
578
mapopt.size = new GSize(parseInt(data.size.width), parseInt(data.size.height));
579
}
580
var markers = data.markers;
581
var map = new GMap2(YAHOO.util.Dom.get("map_canvas"), mapopt);
582
var maptype = null;
583
if (data.maptype) {
584
maptype = o.argument[parseInt(data.maptype)];
585
}
586
var zoom = null;
587
if (data.zoom) {
588
zoom = parseInt(data.zoom);
589
}
590
var center = markers[0].location;
591
if (data.center) {
592
center = data.center;
593
}
594
map.setCenter(GLatLng.fromUrlValue(center), zoom, maptype);
595
map.setUIToDefault();
596
597
/* Now for each marker build and add a GMarker object */
598
for(var i = 0; i < markers.length; i++) {
599
600
var opt = {title: markers[i].title};
601
if (!markers[i].href) {
602
opt.clickable = false;
603
}
604
if (markers[i].icon) {
605
opt.icon = new GIcon(G_DEFAULT_ICON, markers[i].icon);
606
if (markers[i].shadow) {
607
opt.icon.shadow = markers[i].shadow;
608
}
609
}
610
var mark = new GMarker(GLatLng.fromUrlValue(markers[i].location), opt);
611
if (markers[i].href) {
612
mark.href = markers[i].href;
613
GEvent.addListener(mark, "click", function(){window.location=this.href;});
614
}
615
map.addOverlay(mark);
616
}
617
}
618
}, /* End of Part I */
619
620
/* Part II: Function called in the event of failure */
621
failure: function(o) {alert(o.statusText);},
622
623
/* Part III: Data that is passed to the success function.
624
We are using this to map from numerical maptypes that Geo::Google::MapObject has passed us to the
625
forms used in Google maps API.
626
*/
627
argument: [G_NORMAL_MAP, G_SATELLITE_MAP, G_PHYSICAL_MAP, G_HYBRID_MAP]
628
629
}; /* End of constructing the AJAX callback object. */
630
631
/* This calls the server which should hopefully kick off callback.success with lots of lovely data. */
632
var transaction = YAHOO.util.Connect.asyncRequest('GET', get_init_data, callback, null);
633
634
} /* end of mapInitialize function */
635
636
/* This will make sure that we call the above function at a good time. */
637
YAHOO.util.Event.onDOMReady(mapInitialize);
638
639
/* We still have to do the memory cleanup ourselves. */
640
YAHOO.util.Event.addListener(window, 'unload', 'GUnload');
641
642
=head2 Taking it further
643
644
There are lots of variations that can be done with this. The Geo::Google::MapObject object will pass
645
on all fields it does not recognize via the C<< json >> function and these can be used by the javascript
646
in whatever way required. Similarly fields added to the marker specification will be passed by the C<< markers >>
647
function and can be used in the template in whatever way required.
648
649
You can also make the markers draggable and have the act of dragging a marker update data on the server.
650
There is an example of where this has been done at L.
651
The details of how this is done depend intrinsically on the implementation of the data storage on the server.
652
I would suggest that in general the steps required to make the markers draggable might be as follows:
653
654
=over
655
656
=item Clean interface to the data storage.
657
658
I think this is a clear case where an ORM (L) offers some advantages.
659
You could look at L or write your own interface using L.
660
661
=item Derive a class from Geo::Google::MapObject.
662
663
Your derived class constructor should get the data you need from the data storage interface layer, use it to
664
create a Geo::Google::MapObject and bless that into your derived class.
665
666
=item Provide URLs that update the data underlying your map objects.
667
668
These will probably be POST methods and need not return anything other than an "OK" string.
669
They should probably call the data storage interface layer directly to update the data.
670
There is no point creating a map object merely to update the underlying data.
671
672
=item Add link elements to your markup indicating which URL is needed to update the marker data.
673
674
675
676
An advantage of using these link elements is that you might have multiple maps to manage.
677
By using the link elements to communicate with the client side, a lot more code and templates can be reused.
678
679
=item Get the javascript side to read your new link elements.
680
681
var move_marker = YAHOO.util.Dom.get('move_marker').href;
682
683
=item Add a C<< draggable >> flag to the GMarker constructor
684
685
var opt = {title: markers[i].title, draggable: true};
686
687
=item Make sure your GMarkers are labelled in a way that your server can understand
688
689
Just after the GMarker object, mark, has been created add
690
691
mark.label = markers[i].label;
692
693
=item Make your GMarker object subscribe to the C<< dragend >> event.
694
695
GEvent.addListener(mark,
696
"dragend",
697
function(latlng) {
698
var location = latlng.toUrlValue(12);
699
var postdata = "id="+this.label+"&location="+location;
700
YAHOO.util.Connect.asyncRequest('POST',
701
move_marker,
702
{
703
success: function(o){},
704
failure: function(o) {alert(o.statusText);},
705
argument: []
706
},
707
postdata);
708
});
709
710
=back
711
712
=head1 DIAGNOSTICS
713
714
=over
715
716
=item C<< markers should be an ARRAY >>
717
718
The markers parameter of the constructor must be an ARRAY ref of marker configuration data.
719
720
=item C<< no API key >>
721
722
To use this module you must sign up for an API key (http://code.google.com/apis/maps/signup.html) and supply it as
723
the key parameter.
724
725
=item C<< zoom not a number: %s >>
726
727
There must be a zoom parameter which is a number from 0 to 21.
728
729
=item C<< maptype %s not recognized >>
730
731
The maptype must be one of roadmap, satellite, terrain, hybrid.
732
733
=item C<< no width >>
734
735
=item C<< no height >>
736
737
=item C<< width should be positive and no more than 640 >>
738
739
=item C<< height should be positive and no more than 640 >>
740
741
=item C<< cannot recognize size >>
742
743
The size parameter must either be a string like "300x500" or a hash array like {width=>300,height=>500}.
744
And both width and height must be between 1 and 640 inclusive.
745
746
=item C<< cannot calculate autozoom without markers >>
747
748
If you pass an C<< autozoom >> parameter to the constructor you must also pass at least one marker.
749
750
=item C<< % not recognized as autozoom >>
751
752
An autozoom parameter was passed to the constructor that was not recognized as such.
753
754
=item C<< location missing >>
755
756
A marker specification was missing a location during autozoom calculation.
757
758
=item C<< no location for %s >>
759
760
Every marker object must have a location.
761
762
=back
763
764
=head1 CONFIGURATION AND ENVIRONMENT
765
766
Geo::Google::MapObject requires no configuration files or environment variables.
767
768
=head1 DEPENDENCIES
769
770
=over
771
772
=item Templating framework
773
774
We assume the use of L and L
775
though other template frameworks may work.
776
777
=item Google Maps API
778
779
You need to have one of these which can be obtained from L.
780
781
=item Javascript and AJAX
782
783
We assume a degree of familiarity with javascript, AJAX and client side programming.
784
For the purposes of documentation we assume YUI: L , but this
785
choice of framework is not mandated.
786
787
=item L
788
789
The autozoom algorithm optionally used by this module depends upon the L for its mathematical
790
calculations. Since alternative algorithms or no algorithm at all can be used, I think it might be better to have this as a separate module.
791
792
=back
793
794
=head1 INCOMPATIBILITIES
795
796
None reported.
797
798
=head1 BUGS AND LIMITATIONS
799
800
=over
801
802
=item paths etc
803
804
Currently there is no support for paths, polygons or viewports.
805
806
=item version 3
807
808
We are currently only supporting version 2 of the API.
809
810
=item testing
811
812
We encode the title attributes of markers in the C<< json >> function as this seems to be necessary.
813
However I have not yet managed to get a decent test script for this behaviour.
814
There are many other cases that should be tested though I think the most critical cases
815
have been tested.
816
817
=item character encoding
818
819
This module is only tested against UTF-8 web pages. I have no intention
820
of changing this as I cannot think of why anyone would consciously choose to encode
821
web pages in any other way. I am open to persuasion however.
822
823
=back
824
825
Please report any bugs or feature requests to
826
C, or through the web interface at
827
L.
828
829
830
=head1 ACKNOWLEDGEMENTS
831
832
Thanks to Andreas Koenig for pointing out that this module had an issue with C<< Build.PL >>.
833
834
=head1 AUTHOR
835
836
Nicholas Bamber C<< >>
837
838
839
=head1 LICENCE AND COPYRIGHT
840
841
Copyright (c) 2009, Nicholas Bamber C<< >>. All rights reserved.
842
843
This module is free software; you can redistribute it and/or
844
modify it under the same terms as Perl itself. See L.
845
846
847
=head1 DISCLAIMER OF WARRANTY
848
849
BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
850
FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
851
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
852
PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
853
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
854
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
855
ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
856
YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
857
NECESSARY SERVICING, REPAIR, OR CORRECTION.
858
859
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
860
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
861
REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
862
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
863
OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
864
THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
865
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
866
FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
867
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
868
SUCH DAMAGES.
869
870
=cut
871
872
1; # End of Geo::Google::MapObject