package HTML::GMap::Files; our $VERSION = '0.06'; # $Id: Files.pm,v 1.13 2007/09/19 01:49:12 canaran Exp $ use warnings; use strict; use Carp; use Storable; sub new { my ($class, %params) = @_; my $self = bless {}, $class; eval { exists $params{temp_dir} or croak("A temp_dir param is required!"); $self->temp_dir($params{temp_dir}); }; $self->croak($@) if $@; $self->create_files; return $self; } sub create_files { my ($self) = @_; my $files_ref = $self->files; my $temp_dir = $self->temp_dir; foreach my $file_name (keys %$files_ref) { my $file_content = $files_ref->{$file_name}->{content}; my $file_type = $files_ref->{$file_name}->{type}; if ($file_type eq 'binary') { $file_content =~ s/\n//g; my $binary_content = pack('H*', $file_content); open(OUT, ">$temp_dir/$file_name") or croak("Cannot write file ($temp_dir/$file_name): $!"); binmode OUT; print OUT $binary_content; close OUT; } else { open(OUT, ">$temp_dir/$file_name") or croak("Cannot write file ($temp_dir/$file_name): $!"); $file_content =~ s/^\s+//; $file_content =~ s/\s+$/\n/; print OUT $file_content; close OUT; } } return 1; } sub temp_dir { my ($self, $value) = @_; $self->{temp_dir} = $value if @_ > 1; return $self->{temp_dir}; } sub files { my ($self) = @_; my %files; $files{'gmap-main.css'}{'type'} = 'ascii'; $files{'gmap-main.css'}{'content'} = <<'CONTENT'; /* Author: Payan Canaran (pcanaran@cpan.org) Copyright 2006-2007 Cold Spring Harbor Laboratory $Id: Files.pm,v 1.13 2007/09/19 01:49:12 canaran Exp $ */ body { text-align: center; font-family: arial; } th { font-size: 12px; } td { font-size: 12px; vertical-align: top; } table.container { border: 1px dashed #333; background-color: #F5F5DC; text-align: left; font-size: 13px; } div.sub_header { padding: 4px; font-size: 14px; font-weight: bold; } div.status { padding: 4px; width: 250px; } div.filter { padding: 4px; width: 240px; } div.messages { padding: 4px; width: 250px; overflow: auto; } div.details_info { padding: 4px; height: 110px; width: 240px; overflow: auto; } div.legend_info { padding: 4px; height: 490px; width: 180px; font-weight: bold; overflow: auto; } div.float_right { clear: right; display: inline; } div.hidden { visibility: hidden; display: inline; } div.visible { visibility: visible; display: inline; } font.bold { font-weight: bold; } CONTENT $files{'gmap-main.html'}{'type'} = 'ascii'; $files{'gmap-main.html'}{'content'} = <<'CONTENT'; [% cgi_header %] [% page_title %] [% header %]

[% page_title %]

Legend:

Loading map ...
Status:
Initializing ...
Filter:
[% FOREACH field = param_fields_with_values %] [% END %]
[% field.display %]:
[% IF display_cluster_slices %]
Cluster rare values:
(less than per tile)
[% END %]
Details:
Messages:
[% messages %]
[% footer %] CONTENT $files{'gmap-main.js'}{'type'} = 'ascii'; $files{'gmap-main.js'}{'content'} = <<'CONTENT'; // Author: Payan Canaran (pcanaran@cpan.org) // Copyright 2006-2007 Cold Spring Harbor Laboratory // $Id: Files.pm,v 1.13 2007/09/19 01:49:12 canaran Exp $ // Create and initialize map var map = new GMap2(document.getElementById('map')); var centerLat = varStore.centerLat; var centerLng = varStore.centerLng; var centerZoom = varStore.centerZoom; // map.addControl(new GSmallZoomControl()); map.addControl(new GLargeMapControl()); map.addControl(new GMapTypeControl()); map.addControl(new GScaleControl()); map.setCenter(new GLatLng(centerLat, centerLng), centerZoom); // Register map events GEvent.addListener(map, "movestart", startRefresh); GEvent.addListener(map, "zoomend", limitZoom); GEvent.addListener(map, "moveend", limitVerticalMove); GEvent.addListener(map, "moveend", doRefresh); // Register non-map events var clusterField = varStore.clusterField; if (clusterField && clusterField != '_default') { Event.observe(document.getElementById(clusterField), 'change', toggleClusterSlicesGroup); } Event.observe(window, 'load', doRefresh); // [End of main script] // Create a URL from form variables function constructUrl() { var queryParams = varStore.queryParams; var urlTemplate = varStore.urlTemplate; // Determine geographic boundaries of the map in view var mapBounds = map.getBounds(); var boundSouthWest = mapBounds.getSouthWest(); var boundNorthEast = mapBounds.getNorthEast(); var latNorth = boundNorthEast.lat(); var lngEast = boundNorthEast.lng(); var latSouth = boundSouthWest.lat(); var lngWest = boundSouthWest.lng(); // Make a copy of urlTemplate, will be overwritten var url = urlTemplate; // Retrieve and substitute query param values for (var i = 0; i < queryParams.length; i++) { var paramName = queryParams[i]; var paramValue = document.getElementById(paramName).value; url += ';' + escape(paramName) + "=" + escape(paramValue); } // Add coordinates url += "&latitude_north=" + escape(latNorth); url += "&longitude_east=" + escape(lngEast); url += "&latitude_south=" + escape(latSouth); url += "&longitude_west=" + escape(lngWest); // Add clustering (slices) params if (document.getElementById('cluster_slices_group') && Element.hasClassName(document.getElementById('cluster_slices_group'), 'visible') && document.getElementById('cluster_slices') ) { url += "&cluster_slices=" + escape(document.getElementById('cluster_slices').checked); url += "&cluster_slices_value=" + escape(document.getElementById('cluster_slices_value').value); url += "&cluster_slices_by=" + escape(document.getElementById('cluster_slices_by').value); }; //Add zoom level url += "&zoom_level=" + map.getZoom() // document.getElementById("debug").innerHTML = url; return url; } // Add latitude-longitude grid to map function addLatLngLines() { var imageHeightPix = varStore.imageHeightPix; var tileHeightPix = varStore.tileHeightPix; var imageWidthPix = varStore.imageWidthPix; var tileWidthPix = varStore.tileWidthPix; var numberOfVerticalTiles = imageHeightPix / tileHeightPix; var numberOfHorizontalTiles = imageWidthPix / tileWidthPix; // Determine geographic boundaries of the map in view var mapBounds = map.getBounds(); var boundSouthWest = mapBounds.getSouthWest(); var boundNorthEast = mapBounds.getNorthEast(); var latNorth = boundNorthEast.lat(); var lngEast = boundNorthEast.lng(); var latSouth = boundSouthWest.lat(); var lngWest = boundSouthWest.lng(); var lngMid = (lngWest < lngEast) ? lngWest + (lngEast - lngWest) / 2 : lngWest + ((lngEast - (-180)) + (180 - lngWest)) / 2; if (lngMid > 180) { // Longitude +180 continues with -180 lngMid = -180 + (lngMid - 180); // (lngMid - 180) is overflow over 180' } // Longitude +180 continues with -180 tileSideLng = (lngWest < lngEast) ? (lngEast - lngWest) / numberOfHorizontalTiles : ((lngEast - (-180)) + (180 - lngWest)) / numberOfHorizontalTiles; tileSideLat = (latNorth - latSouth) / numberOfVerticalTiles; // document.getElementById("debug").innerHTML = '> latNorth:' + latNorth // + ' latSouth:' + latSouth // + '
' // + ' lngWest:' + lngWest // + ' lngEast:' + lngEast // + ' tileSideLng:' + tileSideLng // + '
'; for (var n = 1; n < numberOfHorizontalTiles; n++) { // Draw longitudes var lngNth = lngWest + n * tileSideLng; if (lngNth > 180) { // Longitude +180 continues with -180 lngNth = -180 + (lngNth - 180); // (lngNth - 180) is overflow over 180' } var polyline = new GPolyline([ new GLatLng(latNorth, lngNth), new GLatLng(latSouth, lngNth) ], "#ff8b04", 2); map.addOverlay(polyline); map.addOverlay(polyline); // Every other polyline is skipped, a potential bug; observe 8/10/07 } for (var n = 1; n < numberOfVerticalTiles; n++) { // Draw latitudes var latNth = latSouth + n * tileSideLat; var polyline = new GPolyline([ new GLatLng(latNth, lngWest), new GLatLng(latNth, lngMid), // A mid point is needed to ensure direction of the line is correct new GLatLng(latNth, lngEast) ], "#ff8b04", 2); map.addOverlay(polyline); map.addOverlay(polyline); // Every other polyline is skipped, a potential bug; observe 8/10/07 } // document.getElementById("debug").innerHTML = 'zoom:' + map.getZoom(); return 1; } // Start refresh (clear overlays and messages) function startRefresh() { // Clear existing overlays map.clearOverlays(); document.getElementById("status").innerHTML = 'Moving ...'; document.getElementById("legend").innerHTML = ''; document.getElementById("legend_message").innerHTML = ''; document.getElementById("details").innerHTML = ''; } // Refresh display (clear overlays, send an AJAX request, process request) function doRefresh(event) { var drawGrid = varStore.drawGrid; // Clear existing overlays map.clearOverlays(); document.getElementById("status").innerHTML = "Refreshing ..."; // Construct URL based on coordinates and form values var requestUrl = constructUrl(); // Query database, pass results on for parsing GDownloadUrl(requestUrl, processRequest); // No parentheses // Draw grid if (drawGrid > 0) { addLatLngLines(); } } // Place markers on the map function processRequest(data, responseCode) { var xml = GXml.parse(data); var markers = xml.documentElement.getElementsByTagName("marker"); for (var i = 0; i < markers.length; i++) { var lat = parseFloat(markers[i].getAttribute("latitude")); var lng = parseFloat(markers[i].getAttribute("longitude")); var iconUrl = markers[i].getAttribute("icon_url"); var iconSize = markers[i].getAttribute("icon_size"); var messagesOnClick = markers[i].getAttribute("messages_on_click"); var detailsOnClick = markers[i].getAttribute("details_on_click"); var legendOnClick = markers[i].getAttribute("legend_on_click"); var point = new GLatLng(lat, lng); addMarker(point, iconUrl, iconSize, legendOnClick, detailsOnClick, messagesOnClick); document.getElementById("details").innerHTML = '[Click on an icon on the map for details ...]'; } var metaData = xml.documentElement.getElementsByTagName("meta_data"); // Only one present var legendByDefault = metaData[0].getAttribute("legend_by_default"); var detailsByDefault = metaData[0].getAttribute("details_by_default") var messagesByDefault = metaData[0].getAttribute("messages_by_default") if (legendByDefault) { document.getElementById("legend").innerHTML = legendByDefault; } if (detailsByDefault) { document.getElementById("details").innerHTML = detailsByDefault; } if (messagesByDefault) { document.getElementById("messages").innerHTML = messagesByDefault; } document.getElementById("status").innerHTML = "Ready"; } // Utility function to add a marker function addMarker(point, iconUrl, iconSize, legendOnClick, detailsOnClick, messagesOnClick) { var icon = createIcon(iconUrl, iconSize); var marker = new GMarker(point, icon); if (legendOnClick) { GEvent.addListener(marker, "click", function() { document.getElementById("legend").innerHTML = legendOnClick; }) }; if (detailsOnClick) { GEvent.addListener(marker, "click", function() { document.getElementById("details").innerHTML = detailsOnClick; }) }; if (messagesOnClick) { GEvent.addListener(marker, "click", function() { document.getElementById("message").innerHTML = messagesOnClick; }) }; map.addOverlay(marker); } // Utility function to create an icon object to be used as a marker function createIcon(imageUrl, size) { var icon = new GIcon(); icon.image = imageUrl; icon.shadow = ""; icon.iconSize = new GSize(size, size); icon.shadowSize = ""; icon.iconAnchor = new GPoint(Math.floor(size/2)+1, Math.floor(size/2)+1); icon.infoWindowAnchor = new GPoint(Math.floor(size/2)+1, Math.floor(size/2)+1);; return icon; } // Utility function to toggle cluster slices group function toggleClusterSlicesGroup(event) { var element = Event.element(event); var elementValue = element.value; var clusterSlicesGroup = document.getElementById("cluster_slices_group"); if (elementValue == 'all') { clusterSlicesGroup.className = 'visible'; } else { clusterSlicesGroup.className = 'hidden'; } } // Utility function to limit zoom function limitZoom() { var zoom = map.getZoom(); if (zoom < 2) { map.setZoom(2); } } // Utility function to limit vertical move function limitVerticalMove() { var mapBounds = map.getBounds(); var boundSouthWest = mapBounds.getSouthWest(); var boundNorthEast = mapBounds.getNorthEast(); var latNorth = boundNorthEast.lat(); var lngEast = boundNorthEast.lng(); var latSouth = boundSouthWest.lat(); var lngWest = boundSouthWest.lng(); var latDelta = latNorth - latSouth; var mapCenter = map.getCenter(); var mapCenterLng = mapCenter.lng(); if (latNorth > 84.5) { var newMapCenterLat = 84.5 - latDelta / 2; map.panTo(new GLatLng(newMapCenterLat, mapCenterLng)); } if (latSouth < -84.5) { var newMapCenterLat = -84.5 + latDelta / 2; map.panTo(new GLatLng(newMapCenterLat, mapCenterLng)); } } // End of Functions CONTENT return \%files; } 1; __END__ =head1 NAME HTML::GMap::Files - File storage for HTML::GMap =head1 SYNOPSIS HTML::GMap::Files->new(temp_dir => '/tmp'); =head1 DESCRIPTION This file is used by HTML::GMap as a storage for HTML template, CSS and Javascript files. =head1 USAGE This file is not intended to be used directly. Please refer to HTML::GMap::Tutorial for detailed usage information. =head1 AUTHOR Payan Canaran =head1 BUGS =head1 VERSION Version 0.06 =head1 ACKNOWLEDGEMENTS =head1 COPYRIGHT & LICENSE Copyright (c) 2006-2007 Cold Spring Harbor Laboratory This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See DISCLAIMER.txt for disclaimers of warranty. =cut