From b1cbb3d1b426ab1cf5fd10eb078ea022e71b7fca Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 8 Dec 2012 23:55:15 +0700 Subject: [PATCH 001/354] BUGFIX: Ensure latitude, longitude and zoom are reable when rendered --- css/lat_long_field.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/css/lat_long_field.css b/css/lat_long_field.css index e69de29..9ced324 100755 --- a/css/lat_long_field.css +++ b/css/lat_long_field.css @@ -0,0 +1,7 @@ +#LatitudeLongitudeZoom .fieldholder-small { + margin: 0px; +} + +#LatitudeLongitudeZoom .fieldholder-small-label { + margin: 0px; +} \ No newline at end of file From 9265742808f7308e89e971d01b1c10be2464b35c Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 8 Dec 2012 23:55:39 +0700 Subject: [PATCH 002/354] UPGRADE: Fixed API calls to ss3 --- code/LatLongField.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/code/LatLongField.php b/code/LatLongField.php index 8d4e1b4..6f8245c 100755 --- a/code/LatLongField.php +++ b/code/LatLongField.php @@ -21,11 +21,11 @@ public function __construct($children = array(), $addressFields = array(), $butt parent::__construct($children); $this->addressFields = $addressFields; $this->buttonText = $buttonText ? $buttonText : _t('LatLongField.LOOKUP','Look up'); - $this->latField = $children[0]->Name(); - $this->longField = $children[1]->Name(); + $this->latField = $children[0]->getName(); + $this->longField = $children[1]->getName(); $name = ""; foreach($children as $field) { - $name .= $field->Name(); + $name .= $field->getName(); } @@ -35,12 +35,12 @@ public function __construct($children = array(), $addressFields = array(), $butt public function hasData() {return true;} - public function FieldHolder() { + public function FieldHolder($properties = array()) { Requirements::javascript(THIRDPARTY_DIR.'/jquery/jquery.js'); Requirements::javascript(THIRDPARTY_DIR.'/jquery-metadata/jquery.metadata.js'); Requirements::javascript('mappable/javascript/lat_long_field.js'); - Requirements::css('mappable/css/lat_long_field.css'); - $this->FieldSet()->push(new LiteralField('geocode_'.$this->id(), sprintf(''. + Requirements::css(MAPPABLE_MODULE_PATH.'/css/lat_long_field.css'); + $this->FieldList()->push(new LiteralField('geocode_'.$this->id(), sprintf(''. $this->buttonText. '', implode(',',$this->addressFields), $this->latField, $this->longField))); $map = GoogleMapUtil::instance(); @@ -49,7 +49,7 @@ public function FieldHolder() { $mapHtml = $map->forTemplate(); - $this->FieldSet()->push(new LiteralField ('geocode_map_field'.$this->id(),$mapHtml)); + $this->FieldList()->push(new LiteralField ('geocode_map_field'.$this->id(),$mapHtml)); return parent::FieldHolder(); } From bccda51358115309af3ee644abd8b5cd5183fc4c Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sun, 9 Dec 2012 00:25:03 +0700 Subject: [PATCH 003/354] merge --- README.md | 7 ++ code/GoogleMapAPI.php | 231 +++++++++++++++++++++++++++++------ code/GoogleMapUtil.php | 22 +++- code/LatLongField.php | 35 ++++-- code/MapField.php | 52 ++++++++ code/Mappable.php | 4 +- code/MappableData.php | 7 +- css/mapField.css | 7 ++ javascript/clusterer.js | 22 +++- javascript/lat_long_field.js | 60 --------- javascript/mapField.js | 231 +++++++++++++++++++++++++++++++++++ 11 files changed, 562 insertions(+), 116 deletions(-) create mode 100644 README.md create mode 100755 code/MapField.php create mode 100644 css/mapField.css delete mode 100644 javascript/lat_long_field.js create mode 100755 javascript/mapField.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..6502ab1 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +== Mappable + + +== Configuring Load Time Event +By default the map invocation is called immediately. In some cases this does not work, as such the load event can be delayed as follows: + + diff --git a/code/GoogleMapAPI.php b/code/GoogleMapAPI.php index b7c52ec..aee7f0f 100644 --- a/code/GoogleMapAPI.php +++ b/code/GoogleMapAPI.php @@ -26,6 +26,9 @@ class GoogleMapAPI extends ViewableData /** GoogleMap Direction ID for the HTML DIV **/ protected $googleMapDirectionId = 'route'; + /* Additional CSS classes to use when rendering the map */ + protected $set_additional_css_classes = ''; + /** Width of the gmap **/ protected $width = 800; @@ -33,10 +36,10 @@ class GoogleMapAPI extends ViewableData protected $height = 600; /** Icon width of the gmarker **/ - protected $iconWidth = 24; + protected $iconWidth = 20; /** Icon height of the gmarker **/ - protected $iconHeight = 24; + protected $iconHeight = 34; /** * @var int Infowindow width of the gmarker @@ -58,17 +61,76 @@ class GoogleMapAPI extends ViewableData /**Center of the gmap **/ protected $center = 'Paris, France'; - protected $latLongCenter = null; + /* + Additional CSS classes to render as a class attribute for the div of the map. Use this if you want more + fine grained control over your map using CSS. If blank it will be ignored + */ + protected $additional_css_classes = ''; + + + /* Decided whether or not to show the inline map css style on div creation */ + protected $show_inline_map_div_style = true; + + protected $latLongCenter = null; + + + /* + Map styles for google maps, ignored if null +
+var styles = [
+       {
+               featureType: 'water',
+               elementType: 'all',
+               stylers: [
+                       { hue: '#B6C5CF' },
+                       { saturation: -54 },
+                       { lightness: 1 },
+                       { visibility: 'on' }
+               ]
+       },{
+               featureType: 'landscape',
+               elementType: 'all',
+               stylers: [
+                       { hue: '#D9D4C8' },
+                       { saturation: -32 },
+                       { lightness: -8 },
+                       { visibility: 'on' }
+               ]
+       },{
+               featureType: 'road',
+               elementType: 'all',
+               stylers: [
+                       { hue: '#A69D97' },
+                       { saturation: -92 },
+                       { lightness: -3 },
+                       { visibility: 'on' }
+               ]
+       },{
+               featureType: 'poi',
+               elementType: 'all',
+               stylers: [
+                       { hue: '#E7E6DB' },
+                       { saturation: -53 },
+                       { lightness: 47 },
+                       { visibility: 'on' }
+               ]
+       }
+];
+    
+ */ + protected $jsonMapStyles = null; + + protected $delayLoadMapFunction = false; /** * Type of the gmap, can be: - * 'G_NORMAL_MAP' (roadmap), + * 'google.maps.MapTypeId.ROADMAP' (roadmap), * 'G_SATELLITE_MAP' (sattelite) * 'G_HYBRID_MAP' (hybrid) * 'G_PHYSICAL_MAP' (terrain) */ - protected $mapType = 'G_NORMAL_MAP'; + protected $mapType = 'google.maps.MapTypeId.ROADMAP'; /** Content of the HTML generated **/ protected $content = ''; @@ -145,6 +207,26 @@ public function setKey($googleMapKey) $this->googleMapKey = $googleMapKey; } + + public function setShowInlineMapDivStyle($new_show_inline_map_div_style) { + $this->show_inline_map_div_style = $new_show_inline_map_div_style; + } + + public function setAdditionalCSSClasses($new_additional_css_classes) { + $this->additional_css_classes = $new_additional_css_classes; + } + + + public function setMapStyles($newStyles) { + $this->jsonMapStyles = $newStyles; + } + + + + public function setDelayLoadMapFunction($newDelay) { + $this->delayLoadMapFunction = $newDelay; + } + /** * Set the useClusterer parameter (optimization to display a lot of marker) * @@ -316,7 +398,7 @@ public function setCenter($center) /** * Set the type of the gmap * - * @param string $mapType ( can be 'G_NORMAL_MAP', 'G_SATELLITE_MAP', 'G_HYBRID_MAP', 'G_PHYSICAL_MAP') + * @param string $mapType ( can be 'google.maps.MapTypeId.ROADMAP', 'G_SATELLITE_MAP', 'G_HYBRID_MAP', 'G_PHYSICAL_MAP') * * @return void */ @@ -437,6 +519,7 @@ public function geocoding($address) public function addMarkerByCoords($lat,$lng,$html='',$category='',$icon='') { + // Save the lat/lon to enable the automatic center/zoom $this->maxLng = (float) max((float)$lng, $this->maxLng); $this->minLng = (float) min((float)$lng, $this->minLng); @@ -495,10 +578,10 @@ public function addArrayMarkerByCoords($coordtab,$category='',$icon='') */ public function addMarkerAsObject(ViewableData $obj) { if($obj instanceof Mappable) { - if(($obj->getLatitude() > 0) || ($obj->getLongitude() > 0)) { + //if(($obj->getMappableLatitude() > 0) || ($obj->getMappableLongitude() > 0)) { $cat = $obj->hasMethod('getMapCategory') ? $obj->getMapCategory() : "default"; - $this->addMarkerByCoords($obj->getLatitude(), $obj->getLongitude(), $obj->getMapContent(), $cat, $obj->getMapPin()); - } + $this->addMarkerByCoords($obj->getMappableLatitude(), $obj->getMappableLongitude(), $obj->getMapContent(), $cat, $obj->getMapPin()); + //} } } @@ -512,8 +595,8 @@ public function addMarkerAsObject(ViewableData $obj) { */ public function connectPoints(ViewableData $one, ViewableData $two, $color = "#FF3300") { $this->addLine( - array($one->getLatitude(), $one->getLongitude()), - array($two->getLatitude(), $two->getLongitude()), + array($one->getMappableLatitude(), $one->getMappableLongitude()), + array($two->getMappableLatitude(), $two->getMappableLongitude()), $color ); } @@ -580,7 +663,7 @@ public function addKML ($url,$category='',$icon='') public function addLine($from = array(), $to = array(), $color = "#FF3300") { - $this->contentLines .= "var points = [new GLatLng({$from[0]},{$from[1]}), new GLatLng({$to[0]},{$to[1]})];\n"; + $this->contentLines .= "var points = [new google.maps.LatLng({$from[0]},{$from[1]}), new google.maps.LatLng({$to[0]},{$to[1]})];\n"; $this->contentLines .= "map.addOverlay(new GPolyline(points,'{$color}',4,0.6));\n"; } /** @@ -592,7 +675,7 @@ public function addLine($from = array(), $to = array(), $color = "#FF3300") { public function includeGMapsJS() { if(self::$jsIncluded) return; // Google map JS - $this->content .= ''."\n"; // Clusterer JS @@ -621,31 +704,64 @@ public function init() $this->content .= "\t".'var trafficInfo = null;'."\n"; $this->content .= "\t".'var directions = null;'."\n"; $this->content .= "\t".'var geocoder = null;'."\n"; + $this->content .= "\tvar infoWindow = new google.maps.InfoWindow({ content: 'test', maxWidth: 300 });"; + // JS public function to add a marker to the map - $this->content .= "\t".'function createMarker(lat,lng,html,category,icon) {'."\n"; + $this->content .= "\n\n\t".'function createMarker(lat,lng,html,category,icon) {'."\n"; + /* $this->content .= "\t\t".'if (icon=="") gicon = new GIcon(G_DEFAULT_ICON);'."\n"; $this->content .= "\t\t".'else { gicon = new GIcon(G_DEFAULT_ICON,icon); gicon.iconSize = new GSize('. $this->iconWidth.','. $this->iconHeight.'); gicon.shadowSize = new GSize(0,0); }'."\n"; - $this->content .= "\t\t".'var marker = new GMarker(new GLatLng(lat,lng),gicon);'."\n"; + + var myLatlng = new google.maps.LatLng(-25.363882,131.044922); + var myOptions = { + zoom: 4, + center: myLatlng, + mapTypeId: google.maps.MapTypeId.ROADMAP + } + var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions); + + var marker = new google.maps.Marker({ + position: myLatlng, + map: map, + title:"Hello World!" + }); + */ + + + + $this->content .= "\n\t\t".'var marker = new google.maps.Marker();'."\n"; + $this->content .= "\t\tmarker.setPosition(new google.maps.LatLng(lat,lng));\n"; + $this->content .= "\t\t".'marker.mycategory = category;'."\n"; + $this->content .= "\t\t\tif (icon != '') {"; + + $this->content .= "\t\t\t\t".'var image = new google.maps.MarkerImage(icon);'; + $this->content .= "\t\t\t\t".'marker.setIcon(image);'."\n"; + $this->content .= "\t\t\t};\n"; + // Use clusterer optimisation or not if ($this->useClusterer==true) { // nothing } else { - $this->content .= "\t\t".'map.addOverlay(marker);'."\n"; + $this->content .= "\t\t".'marker.setMap(map);'."\n"; } + $this->content .= "\t\t".'html = \'
\'+html+\'
\''."\n"; - $this->content .= "\t\t".'GEvent.addListener(marker,"click",function() { '; + $this->content .= "\t\t".'google.maps.event.addListener(marker,"click",function() { '; // Enable the zoom when you click on a marker if ($this->enableWindowZoom==true) { - $this->content .= 'map.setCenter(new GLatLng(lat,lng),'.$this->infoWindowZoom.'); '; + $this->content .= 'map.setCenter(new google.maps.LatLng(lat,lng),'.$this->infoWindowZoom.'); '; } - $this->content .= 'marker.openInfoWindowHtml(html); });'."\n"; + //$this->content .= 'marker.openInfoWindowHtml(html); });'."\n"; + $this->content .= "\n\t\t".'infoWindow.setContent(html);'; + $this->content .= "\t\t".'infoWindow.open(map, this);'; + $this->content .= ' });'."\n"; $this->content .= "\t\t".'gmarkers.push(marker);'."\n"; // Hide marker by default @@ -656,17 +772,17 @@ public function init() $this->content .= "\t".'}'."\n"; // JS public function to get current Lat & Lng - $this->content .= "\t".'function getCurrentLat() {'."\n"; + $this->content .= "\n\t".'function getCurrentLat() {'."\n"; $this->content .= "\t\t".'return current_lat;'."\n"; $this->content .= "\t".'}'."\n"; - $this->content .= "\t".'function getCurrentLng() {'."\n"; + $this->content .= "\n\t".'function getCurrentLng() {'."\n"; $this->content .= "\t\t".'return current_lng;'."\n"; $this->content .= "\t".'}'."\n"; // JS public function to center the gmaps dynamically - $this->content .= "\t".'function showAddress(address) {'."\n"; + $this->content .= "\n\t".'function showAddress(address) {'."\n"; $this->content .= "\t\t".'if (geocoder) {'."\n"; $this->content .= "\t\t\t".'geocoder.getLatLng('."\n"; $this->content .= "\t\t\t\t".'address,'."\n"; @@ -683,7 +799,18 @@ public function init() $this->content .= "\t".''."\n"; // Google map DIV - $this->content .= "\t".'
'."\n"; + $this->content .= "\t".'
show_inline_map_div_style) { + $this->content .= 'style="width:'.$this->width.'px;height:'.$this->height.'px"'; + } + + if ($this->additional_css_classes != '') { + $this->content .= ' class="'.$this->additional_css_classes.'"'; + } + + + $this->content .= ">
\n"; } /** @@ -697,7 +824,7 @@ public function generate() $this->init(); - $this->content .= "\t".''."\n"; + // THIS SEEMS ROGUE $this->content .= "\t".''."\n"; // Center of the GMap $geocodeCentre = ($this->latLongCenter) ? $this->latLongCenter : $this->geocoding($this->center); @@ -710,14 +837,31 @@ public function generate() $this->content .= "\t".' + '; + + + + // $literalFieldJS = new LiteralField('fieldNames',$js); + // $this->FieldList()->push($literalFieldJS); + + +// Requirements::css(MAPPABLE_MODULE_PATH.'/css/lat_long_field.css'); //Requirements::css('mappable/css/lat_long_field.css'); + + $fieldNames = array( + 'LatFieldName' => $this->latField, + 'LonFieldName' => $this->longField, + 'ZoomFieldName' => $this->zoomField + ); + + + Requirements::javascriptTemplate(MAPPABLE_MODULE_PATH.'/javascript/mapField.js', $fieldNames); $this->FieldList()->push(new MapField('GoogleMap','GoogleMap')); From 5cc17205167af5ba186ef72a748169da235516e1 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 10 Dec 2012 23:38:06 +0700 Subject: [PATCH 011/354] BUGFIX: Moved mapfield.js to latlong field --- code/MapField.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/code/MapField.php b/code/MapField.php index a757c43..0b4bb69 100755 --- a/code/MapField.php +++ b/code/MapField.php @@ -34,7 +34,14 @@ function Field($properties = array()) { Requirements::javascript('framework/thirdparty/jquery/jquery.js'); Requirements::javascript('framework/thirdparty/jquery-livequery/jquery.livequery.js'); // Requirements::javascript('http://maps.google.com/maps/api/js?sensor=false'); - Requirements::javascript('mappable/javascript/mapField.js'); + + + // Requirements::javascript('mappable/javascript/mapField.js'); + + + + + $attributes = array( 'class' => 'middleColumn', 'id' => $this->divId, From 2d91a9c19a56a242d606ccb0f64d3fe1dc13eccc Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 10 Dec 2012 23:38:44 +0700 Subject: [PATCH 012/354] BUGFIX: JS now a template file to allow for different file names --- javascript/mapField.js | 49 ++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/javascript/mapField.js b/javascript/mapField.js index 7e708a5..808e407 100755 --- a/javascript/mapField.js +++ b/javascript/mapField.js @@ -1,13 +1,14 @@ var marker; -var latFieldName = 'Lat'; -var lonFieldName = 'Lon'; -var zoomFieldName = 'ZoomLevel'; - - (function($) { - var latField = $('input[name='+latFieldName+']'); - var lonField = $('input[name='+lonFieldName+']'); - })(jQuery); +/* +The following variables are set up by a literal field int he map field, as they names of these fields can of course vary +- latFieldName: latittude field name +- lonFieldName: longitutude field name +- zoomFieldName: zoom field name +*/ + + + function gmloaded() { @@ -29,9 +30,9 @@ var zoomFieldName = 'ZoomLevel'; }; (function($) { - var latField = $('input[name='+latFieldName+']'); - var lonField = $('input[name='+lonFieldName+']'); - var zoomField = $('input[name='+zoomFieldName+']'); + var latField = $('input[name="$LatFieldName"]'); + var lonField = $('input[name="$LonFieldName"]'); + var zoomField = $('input[name="$ZoomFieldName"]'); myOptions.center = new google.maps.LatLng(latField.val(), lonField.val()); @@ -65,8 +66,8 @@ var zoomFieldName = 'ZoomLevel'; google.maps.event.addListener(map, "zoom_changed", function(e) { - if ($('input[name='+zoomFieldName+']').length) { - $('input[name='+zoomFieldName+']').val(map.getZoom()); + if ($('input[name="$ZoomFieldName"]').length) { + $('input[name="$ZoomFieldName"]').val(map.getZoom()); } }); @@ -127,9 +128,9 @@ var zoomFieldName = 'ZoomLevel'; function setCoordByMarker(event) { (function($) { - var latField = $('input[name='+latFieldName+']'); - var lonField = $('input[name='+lonFieldName+']'); - var zoomField = $('input[name='+zoomFieldName+']'); + var latField = $('input[name="$LatFieldName"]'); + var lonField = $('input[name="$LonFieldName"]'); + var zoomField = $('input[name="$ZoomFieldName"]'); latField.val(event.latLng.lat()); lonField.val(event.latLng.lng()); @@ -182,8 +183,7 @@ var zoomFieldName = 'ZoomLevel'; $('#mapSearchResults').html(html); - // latField.val(results[0].geometry.location.lat()); - // lonField.val(results[0].geometry.location.lng()); + // setMarker(results[0].geometry.location.lat); } else { errorMessage("Unable to find any geocoded results"); @@ -224,6 +224,8 @@ var zoomFieldName = 'ZoomLevel'; $('.geocodedSearchResults li').livequery('click', function(e) { // get the data needed to ask coords var t = $(this); + console.log("ENTRY CLICKED"); + console.log(t); var lat = t.attr("lat"); var lon = t.attr("lon"); var address = t.html(); @@ -233,7 +235,16 @@ var zoomFieldName = 'ZoomLevel'; $('#Form_EditForm_Latitude').val(lat); $('#Form_EditForm_Longitude').val(lon); - $('#Form_EditForm_Location').val(address); + var latField = $('input[name="$LatFieldName"]'); + var lonField = $('input[name="$LonFieldName"]'); + var zoomField = $('input[name="$ZoomFieldName"]'); + + latField.val(lat); + lonField.val(lon); + + // zoom in to an appropriate level + map.setZoom(12); + setMarker(latlng, true); return false; }); From ab459afd2ce8359d87b338a42f48e3f401bc61f9 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 11 Dec 2012 10:20:54 +0700 Subject: [PATCH 013/354] ENHANCEMENT: Hide text fields and label, just show map --- code/LatLongField.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/LatLongField.php b/code/LatLongField.php index 0973314..445692c 100755 --- a/code/LatLongField.php +++ b/code/LatLongField.php @@ -35,6 +35,12 @@ public function __construct($children = array(), $addressFields = array(), $butt $name .= $field->getName(); } + // hide the lat long and zoom fields from the interface + foreach ($this->FieldList() as $fieldToHide) { + $fieldToHide->addExtraClass('hide'); + } + + $this->name = $name; } From 003af3650266209a63965c55a090c3facd2c2e76 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 11 Dec 2012 10:21:59 +0700 Subject: [PATCH 014/354] BUGFIX: Recenter as well as resize after tab shown --- javascript/mapField.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/javascript/mapField.js b/javascript/mapField.js index 808e407..6515a7b 100755 --- a/javascript/mapField.js +++ b/javascript/mapField.js @@ -1,3 +1,4 @@ +// FIXME avoid global var marker; /* @@ -54,6 +55,7 @@ The following variables are set up by a literal field int he map field, as they + google.maps.event.addListener(map, "rightclick", function(event) { var lat = event.latLng.lat(); var lng = event.latLng.lng(); @@ -88,7 +90,8 @@ The following variables are set up by a literal field int he map field, as they $('a[href="#Root_Location"]').click(function() { - google.maps.event.trigger(map, 'resize'); + google.maps.event.trigger(map, 'resize'); + map.setCenter(marker.getPosition()); }); From 35d93cbc371c894f026cb25ada838b8b6f2e7623 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 14 Dec 2012 15:09:03 +0700 Subject: [PATCH 015/354] WIP: Moving to generic maps, not just google --- code/{GoogleMapAPI.php => MapAPI.php} | 4 ++-- code/{GoogleMapUtil.php => MapUtil.php} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename code/{GoogleMapAPI.php => MapAPI.php} (99%) rename code/{GoogleMapUtil.php => MapUtil.php} (99%) diff --git a/code/GoogleMapAPI.php b/code/MapAPI.php similarity index 99% rename from code/GoogleMapAPI.php rename to code/MapAPI.php index aee7f0f..9fb00af 100644 --- a/code/GoogleMapAPI.php +++ b/code/MapAPI.php @@ -14,7 +14,7 @@ * @ version 18:13 26/05/2009 */ -class GoogleMapAPI extends ViewableData +class MapAPI extends ViewableData { /** GoogleMap key **/ @@ -789,7 +789,7 @@ public function init() $this->content .= "\t\t\t\t".'function(point) {'."\n"; $this->content .= "\t\t\t\t\t".'if (!point) { alert(address + " not found"); }'."\n"; $this->content .= "\t\t\t\t\t".'else {'."\n"; - $this->content .= "\t\t\t\t\t\t".'map.setCenter(point, '.$this->zoom.');'."\n"; + $this->content .= "\t\t\t\t\t\t".'map.setCenter(point, '.$this->zoom.');'."//T1\n"; $this->content .= "\t\t\t\t\t".'}'."\n"; $this->content .= "\t\t\t\t".'}'."\n"; $this->content .= "\t\t\t".');'."\n"; diff --git a/code/GoogleMapUtil.php b/code/MapUtil.php similarity index 99% rename from code/GoogleMapUtil.php rename to code/MapUtil.php index f1f0a59..0fc0e83 100644 --- a/code/GoogleMapUtil.php +++ b/code/MapUtil.php @@ -1,6 +1,6 @@ Date: Mon, 17 Dec 2012 14:34:30 +0700 Subject: [PATCH 016/354] WIP: Google map javascript in a template --- javascript/google/MapGoogle.ss | 59 ++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 javascript/google/MapGoogle.ss diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogle.ss new file mode 100644 index 0000000..e3a3fff --- /dev/null +++ b/javascript/google/MapGoogle.ss @@ -0,0 +1,59 @@ +MAPPING TEMPLATE 4: + + +<% if UseClusterer %> + + <% end_if %> + + +Main JS below + + + +End of main JS \ No newline at end of file From d48dfc78f268daf788d98022a0e62138f4d3b7a0 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 18 Dec 2012 11:39:59 +0700 Subject: [PATCH 017/354] WIP passing in template variables --- code/MapAPI.php | 1434 +++++++++++++++++++++-------------------------- 1 file changed, 652 insertions(+), 782 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index 9fb00af..8de347a 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -17,64 +17,68 @@ class MapAPI extends ViewableData { - /** GoogleMap key **/ - protected $googleMapKey = ''; - - /** GoogleMap ID for the HTML DIV **/ - protected $googleMapId = 'googlemapapi'; - - /** GoogleMap Direction ID for the HTML DIV **/ - protected $googleMapDirectionId = 'route'; - - /* Additional CSS classes to use when rendering the map */ - protected $set_additional_css_classes = ''; - - /** Width of the gmap **/ - protected $width = 800; - - /** Height of the gmap **/ - protected $height = 600; - - /** Icon width of the gmarker **/ - protected $iconWidth = 20; - - /** Icon height of the gmarker **/ - protected $iconHeight = 34; - - /** - * @var int Infowindow width of the gmarker - **/ - protected $infoWindowWidth = 250; - - /** Default zoom of the gmap **/ - protected $zoom = 9; - - /** Enable the zoom of the Infowindow **/ - protected $enableWindowZoom = false; - - /** Default zoom of the Infowindow **/ - protected $infoWindowZoom = 13; - - /** Lang of the gmap **/ - protected $lang = 'en'; - - /**Center of the gmap **/ - protected $center = 'Paris, France'; - - /* + /** GoogleMap key **/ + protected $googleMapKey = ''; + + /** GoogleMap ID for the HTML DIV **/ + protected $googleMapId = 'googlemapapi'; + + /** GoogleMap Direction ID for the HTML DIV **/ + protected $googleMapDirectionId = 'route'; + + /* Additional CSS classes to use when rendering the map */ + protected $set_additional_css_classes = ''; + + /** Width of the gmap **/ + protected $width = 800; + + /** Height of the gmap **/ + protected $height = 600; + + /** Icon width of the gmarker **/ + protected $iconWidth = 20; + + /** Icon height of the gmarker **/ + protected $iconHeight = 34; + + /** + * + * + * @var int Infowindow width of the gmarker + * */ + protected $infoWindowWidth = 250; + + /** Default zoom of the gmap **/ + protected $zoom = 9; + + /** Enable the zoom of the Infowindow **/ + protected $enableWindowZoom = false; + + /** Default zoom of the Infowindow **/ + protected $infoWindowZoom = 13; + + /** Lang of the gmap **/ + protected $lang = 'en'; + + /**Center of the gmap **/ + protected $center = 'Paris, France'; + + /* Additional CSS classes to render as a class attribute for the div of the map. Use this if you want more fine grained control over your map using CSS. If blank it will be ignored */ - protected $additional_css_classes = ''; + protected $additional_css_classes = ''; - /* Decided whether or not to show the inline map css style on div creation */ - protected $show_inline_map_div_style = true; + /* Decided whether or not to show the inline map css style on div creation */ + protected $show_inline_map_div_style = true; protected $latLongCenter = null; + protected $mappingService = 'Google'; - /* + + /* Map styles for google maps, ignored if null
 var styles = [
@@ -122,792 +126,658 @@ class MapAPI extends ViewableData
 
   protected $delayLoadMapFunction = false;
 
-    /**
-     * Type of the gmap, can be:
-     *  'google.maps.MapTypeId.ROADMAP' (roadmap),
-     *  'G_SATELLITE_MAP' (sattelite)
-     *  'G_HYBRID_MAP' (hybrid)
-     *  'G_PHYSICAL_MAP' (terrain)
-     */
-
-    protected $mapType = 'google.maps.MapTypeId.ROADMAP';
-	
-    /** Content of the HTML generated **/
-   protected $content = '';
-
-    /** Add the direction button to the infowindow **/
-   protected $displayDirectionFields = false;
-
-    /** Hide the marker by default **/
-   protected $defaultHideMarker = false;
-
-    /** Extra content (marker, etc...) **/
-   protected $contentMarker = '';
-
-    /** Use clusterer to display a lot of markers on the gmap **/
-   protected $useClusterer = false;
-   protected $gridSize = 100;
-   protected $maxZoom = 9;
-   protected $clustererLibrarypath = 'markerclusterer_packed.js';
-
-    /** Enable automatic center/zoom **/
-   protected $enableAutomaticCenterZoom = false;
-	
-    /** maximum longitude of all markers **/
-   protected $maxLng = -1000000;
-    
-    /** minimum longitude of all markers **/
-   protected $minLng = 1000000;
-    
-    /** max latitude of all markers **/
-   protected $maxLat = -1000000;
-    
-    /** min latitude of all markers **/
-   protected $minLat = 1000000;
-   
-    /** map center latitude (horizontal), calculated automatically as markers are added to the map **/
-   protected $centerLat = null;
-
-    /** map center longitude (vertical),  calculated automatically as markers are added to the map **/
-   protected $centerLng = null;
-   
-    /** factor by which to fudge the boundaries so that when we zoom encompass, the markers aren't too close to the edge **/
-   protected $coordCoef = 0.01;
-   
-   protected $lines = array ();
-
-   protected $contentLines = '';
-   
-   protected static $jsIncluded = false;
-   
-
-    /**
-          * Class constructor
-          * 
-          * @param string $googleMapKey the googleMapKey
-          * 
-          * @return void
-          */
-
-    public function __construct($googleMapKey='') 
-    {
-        $this->googleMapKey = $googleMapKey;
-    }
+  /**
+   * Type of the gmap, can be:
+   *  'google.maps.MapTypeId.ROADMAP' (roadmap),
+   *  'G_SATELLITE_MAP' (sattelite)
+   *  'G_HYBRID_MAP' (hybrid)
+   *  'G_PHYSICAL_MAP' (terrain)
+   */
 
-    /**
-          * Set the key of the gmap
-          * 
-          * @param string $googleMapKey the googleMapKey
-          * 
-          * @return void
-          */
-
-    public function setKey($googleMapKey) 
-    {
-        $this->googleMapKey = $googleMapKey;
-    }
+  protected $mapType = 'google.maps.MapTypeId.ROADMAP';
 
+  /** Content of the HTML generated **/
+  protected $content = '';
 
-    public function setShowInlineMapDivStyle($new_show_inline_map_div_style) {
-      $this->show_inline_map_div_style = $new_show_inline_map_div_style;
-    }
+  protected $mapService = 'google';
 
-    public function setAdditionalCSSClasses($new_additional_css_classes) {
-      $this->additional_css_classes = $new_additional_css_classes;
-    }
+  /** Add the direction button to the infowindow **/
+  protected $displayDirectionFields = false;
 
+  /** Hide the marker by default **/
+  protected $defaultHideMarker = false;
 
-    public function setMapStyles($newStyles) {
-      $this->jsonMapStyles = $newStyles;
-    }
+  /** Extra content (marker, etc...) **/
+  //protected $contentMarker = '';
 
+  // a list of markers, markers being associative arrays
+  protected $markers = array();
 
+  /** Use clusterer to display a lot of markers on the gmap **/
+  protected $useClusterer = false;
+  protected $gridSize = 100;
+  protected $maxZoom = 9;
+  protected $clustererLibrarypath = 'markerclusterer_packed.js';
 
-    public function setDelayLoadMapFunction($newDelay) {
-      $this->delayLoadMapFunction = $newDelay;
-    }
+  /** Enable automatic center/zoom **/
+  protected $enableAutomaticCenterZoom = false;
 
-    /**
-          * Set the useClusterer parameter (optimization to display a lot of marker)
-          * 
-          * @param boolean $useClusterer use cluster or not
-          * @param string $clusterIcon the cluster icon
-          * @param int $maxVisibleMarkers max visible markers
-          * @param int $gridSize grid size
-          * @param int $minMarkersPerClusterer minMarkersPerClusterer
-          * @param int $maxLinesPerInfoBox maxLinesPerInfoBox
-          * 
-          * @return void
-          */
-
-    public function setClusterer($useClusterer,$gridSize=100,$maxZoom=9,$clustererLibraryPath='mappable/javascript/clusterer.js') 
-    {
-        $this->useClusterer = $useClusterer;
-        $this->gridSize = $gridSize;
-        $this->maxZoom = $maxZoom;
-		$this->clustererLibraryPath = $clustererLibraryPath;
-    }
+  /** maximum longitude of all markers **/
+  protected $maxLng = -1000000;
 
-    /**
-          * Set the ID of the default gmap DIV
-          * 
-          * @param string $googleMapId the google div ID
-          * 
-          * @return void
-          */
-
-    public function setDivId($googleMapId) 
-    {
-        $this->googleMapId = $googleMapId;
-    }
-    
-    /**
-          * Set the ID of the default gmap direction DIV 
-          * 
-          * @param string $googleMapDirectionId GoogleMap  Direction ID for the HTML DIV
-          * 
-          * @return void
-          */
-
-    public function setDirectionDivId($googleMapDirectionId) 
-    {
-        $this->googleMapDirectionId = $googleMapDirectionId;
-    }
+  /** minimum longitude of all markers **/
+  protected $minLng = 1000000;
 
-    /**
-          * Set the size of the gmap
-          * 
-          * @param int $width GoogleMap  width
-          * @param int $height GoogleMap  height
-          * 
-          * @return void
-          */
-
-    public function setSize($width,$height) 
-    {
-        $this->width = $width;
-        $this->height = $height;
-    }
+  /** max latitude of all markers **/
+  protected $maxLat = -1000000;
 
-    /**
-          * Set the with of the gmap infowindow (on marker clik)
-          * 
-          * @param int $infoWindowWidth GoogleMap  info window width
-          * 
-          * @return void
-          */
-    
-    public function setInfoWindowWidth ($infoWindowWidth) 
-    {
-        $this->infoWindowWidth = $infoWindowWidth;
-    }
-    
-    /**
-          * Set the size of the icon markers
-          * 
-          * @param int $iconWidth GoogleMap  marker icon width
-          * @param int $iconHeight GoogleMap  marker icon height
-          * 
-          * @return void
-          */
-
-    public function setIconSize($iconWidth,$iconHeight) 
-    {
-        $this->iconWidth = $iconWidth;
-        $this->iconHeight = $iconHeight;
-    }
+  /** min latitude of all markers **/
+  protected $minLat = 1000000;
 
-    /**
-          * Set the lang of the gmap
-          * 
-          * @param string $lang GoogleMap  lang : fr,en,..
-          * 
-          * @return void
-          */
-
-    public function setLang($lang) 
-    {
-        $this->lang = $lang;
-    }
-    
-    /**
-          * Set the zoom of the gmap
-          * 
-          * @param int $zoom GoogleMap  zoom.
-          * 
-          * @return void
-          */
-
-    public function setZoom($zoom) 
-    {
-        $this->zoom = $zoom;
-    }
-	
-	/**
-          * Set the zoom of the infowindow
-          * 
-          * @param int $zoom GoogleMap  zoom.
-          * 
-          * @return void
-          */
-
-    public function setInfoWindowZoom($infoWindowZoom) 
-    {
-        $this->infoWindowZoom = $infoWindowZoom;
-    }
-	
-	/**
-          * Enable the zoom on the marker when you click on it
-          * 
-          * @param int $zoom GoogleMap  zoom.
-          * 
-          * @return void
-          */
-
-    public function setEnableWindowZoom($enableWindowZoom) 
-    {
-        $this->enableWindowZoom = $enableWindowZoom;
-    }
-	
-	/**
-          * Enable theautomatic center/zoom at the gmap load
-          * 
-          * @param int $zoom GoogleMap  zoom.
-          * 
-          * @return void
-          */
-
-    public function setEnableAutomaticCenterZoom($enableAutomaticCenterZoom) 
-    {
-        $this->enableAutomaticCenterZoom = $enableAutomaticCenterZoom;
-    }
-	
-    /**
-          * Set the center of the gmap (an address)
-          * 
-          * @param string $center GoogleMap  center (an address)
-          * 
-          * @return void
-          */
-
-    public function setCenter($center) 
-    {
-        $this->center = $center;
-    }
+  /** map center latitude (horizontal), calculated automatically as markers are added to the map **/
+  protected $centerLat = null;
 
-    /**
-      * Set the type of the gmap
-      *
-      * @param string $mapType ( can be 'google.maps.MapTypeId.ROADMAP', 'G_SATELLITE_MAP', 'G_HYBRID_MAP', 'G_PHYSICAL_MAP')
-      *
-      * @return void
-      */
-
-    public function setMapType($mapType)
-    {
-        $this->mapType = $mapType;
-    }
+  /** map center longitude (vertical),  calculated automatically as markers are added to the map **/
+  protected $centerLng = null;
 
+  /** factor by which to fudge the boundaries so that when we zoom encompass, the markers aren't too close to the edge **/
+  protected $coordCoef = 0.01;
 
+  protected $lines = array ();
 
-    public function setLatLongCenter($center) 
-    {
-        $this->latLongCenter = $center;
-    }
+  protected $contentLines = '';
 
-    /**
-          * Set the center of the gmap
-          * 
-          * @param boolean $displayDirectionFields display directions or not in the info window
-          * 
-          * @return void
-          */
-
-    public function setDisplayDirectionFields($displayDirectionFields) 
-    {
-        $this->displayDirectionFields = $displayDirectionFields;
-    }
+  protected static $jsIncluded = false;
 
-    /**
-          * Set the defaultHideMarker
-          * 
-          * @param boolean $defaultHideMarker hide all the markers on the map by default
-          * 
-          * @return void
-          */
-
-    public function setDefaultHideMarker($defaultHideMarker) 
-    {
-        $this->defaultHideMarker = $defaultHideMarker;
-    }
 
-    /**
-          * Get the google map content
-          * 
-          * @return string the google map html code
-          */
+  /**
+   * Class constructor
+   *
+   * @param string  $googleMapKey the googleMapKey
+   *
+   * @return void
+   */
 
-    public function getGoogleMap() 
-    {
-        return $this->content;
-    }
-    
-    /**
-           * Get URL content using cURL.
-          * 
-          * @param string $url the url 
-          * 
-          * @return string the html code
-          *
-          * @todo add proxy settings
-          */
-		  
-    public function getContent($url)
-    {
-        $curl = curl_init();
-        curl_setopt($curl, CURLOPT_TIMEOUT, 10);
-        curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5);
-        curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
-        curl_setopt($curl, CURLOPT_URL, $url);
-        $data = curl_exec($curl);
-        curl_close ($curl);
-        return $data;
-    }
-    
-    /**
-          * Geocoding an address (address -> lat,lng)
-          * 
-          * @param string $address an address
-          * 
-          * @return array array with precision, lat & lng
-          */
-
-    public function geocoding($address) 
-    {
-        $encodeAddress = urlencode($address);
-        $url = "http://maps.google.com/maps/geo?q=".$encodeAddress."&output=csv&key=".$this->googleMapKey;
-        
-        if(function_exists('curl_init')) {
-            $data = $this->getContent($url);
-        } else {
-            $data = file_get_contents($url);
-        }
-        
-		$csvSplit = preg_split("/,/",$data);
-        $status = $csvSplit[0];
-
-        if (strcmp($status, "200") == 0) {
-            $return = $csvSplit; // successful geocode, $precision = $csvSplit[1],$lat = $csvSplit[2],$lng = $csvSplit[3];
-        } else {
-            $return = null; // failure to geocode
-        }
-
-        return $return;
-    }
+  public function __construct( $googleMapKey='' ) {
+    $this->googleMapKey = $googleMapKey;
+  }
 
-    /**
-          * Add marker by his coord
-          * 
-          * @param string $lat lat
-          * @param string $lng lngs
-          * @param string $html html code display in the info window
-          * @param string $category marker category
-          * @param string $icon an icon url
-          * 
-          * @return void
-          */
-
-    public function addMarkerByCoords($lat,$lng,$html='',$category='',$icon='') 
-    {
-
-		// Save the lat/lon to enable the automatic center/zoom
-		$this->maxLng = (float) max((float)$lng, $this->maxLng);
-        $this->minLng = (float) min((float)$lng, $this->minLng);
-        $this->maxLat = (float) max((float)$lat, $this->maxLat);
-        $this->minLat = (float) min((float)$lat, $this->minLat);
-        $this->centerLng = (float) ($this->minLng + $this->maxLng) / 2;
-        $this->centerLat = (float) ($this->minLat + $this->maxLat) / 2;
-		
-        $this->contentMarker .= "\t\t\t".'createMarker('.$lat.','.$lng.',"'.$html.'","'.$category.'","'.$icon.'");'."\n";
-    }
+  /**
+   * Set the key of the gmap
+   *
+   * @param string  $googleMapKey the googleMapKey
+   *
+   * @return void
+   */
 
-    /**
-          * Add marker by his address
-          * 
-          * @param string $address an ddress
-          * @param string $content html code display in the info window
-          * @param string $category marker category
-          * @param string $icon an icon url
-          * 
-          * @return void
-          */
-
-    public function addMarkerByAddress($address,$content='',$category='',$icon='') 
-    {
-        $point = $this->geocoding($address);
-		if ($point!==null) {
-        $this->addMarkerByCoords($point[2], $point[3], $content, $category, $icon);
-		} else {
-			// throw new Exception('Adress not found : '.$address);
-		}
-    }
+  public function setKey( $googleMapKey ) {
+    $this->googleMapKey = $googleMapKey;
+  }
 
-    /**
-          * Add marker by an array of coord
-          * 
-          * @param string $coordtab an array of lat,lng,content
-          * @param string $category marker category
-          * @param string $icon an icon url
-          * 
-          * @return void
-          */
-
-    public function addArrayMarkerByCoords($coordtab,$category='',$icon='') 
-    {
-        foreach ($coordtab as $coord) {
-            $this->addMarkerByCoords($coord[0], $coord[1], $coord[2], $category, $icon);
-        }
-    }
-    
-    
-    /**
-     * Adds a {@link ViewableData} object that implements {@link Mappable}
-     * to the map.
-     *
-     * @param ViewableData $obj
-     */
-    public function addMarkerAsObject(ViewableData $obj) {    
-    	if($obj instanceof Mappable) {
-        	//if(($obj->getMappableLatitude() > 0) || ($obj->getMappableLongitude() > 0)) {
-        		$cat = $obj->hasMethod('getMapCategory') ? $obj->getMapCategory() : "default";
-		        $this->addMarkerByCoords($obj->getMappableLatitude(), $obj->getMappableLongitude(), $obj->getMapContent(), $cat, $obj->getMapPin());
-	        //}
-        }    
-    }
-    
-    
-    /**
-     * Draws a line between two {@link ViewableData} objects
-     *
-     * @param ViewableData $one The first point
-     * @param ViewableData $two The second point
-     * @param string $color The hexidecimal color of the line
-     */
-    public function connectPoints(ViewableData $one, ViewableData $two, $color = "#FF3300") {
-		$this->addLine(
-			array($one->getMappableLatitude(), $one->getMappableLongitude()),
-			array($two->getMappableLatitude(), $two->getMappableLongitude()),
-			$color
-		);    
-    }
-    
-    
-    public function forTemplate() {
-    	$this->generate();
-    	return $this->getGoogleMap();
-    }
-    
-    
-
-    /**
-          * Add marker by an array of address
-          * 
-          * @param string $coordtab an array of address
-          * @param string $category marker category
-          * @param string $icon an icon url
-          * 
-          * @return void
-          */
-
-    public function addArrayMarkerByAddress($coordtab,$category='',$icon='') 
-    {
-        foreach ($coordtab as $coord) {
-            $this->addMarkerByAddress($coord[0], $coord[1], $category, $icon);
-        }
-    }
 
-    /**
-          * Set a direction between 2 addresss and set a text panel
-          * 
-          * @param string $from an address
-          * @param string $to an address
-          * @param string $idpanel id of the div panel
-          * 
-          * @return void
-          */
-
-    public function addDirection($from,$to,$idpanel='') 
-    {
-        $this->contentMarker .= 'addDirection("'.$from.'","'.$to.'","'.$idpanel.'");';
-    }
+  public function setShowInlineMapDivStyle( $new_show_inline_map_div_style ) {
+    $this->show_inline_map_div_style = $new_show_inline_map_div_style;
+  }
 
-    /**
-          * Parse a KML file and add markers to a category
-          * 
-          * @param string $url url of the kml file compatible with gmap and gearth
-          * @param string $category marker category
-          * @param string $icon an icon url
-          * 
-          * @return void
-          */
-
-    public function addKML ($url,$category='',$icon='') 
-    {
-        $xml = new SimpleXMLElement($url, null, true);
-        foreach ($xml->Document->Folder->Placemark as $item) {
-            $coordinates = explode(',', (string) $item->Point->coordinates);
-            $name = (string) $item->name;
-            $this->addMarkerByCoords($coordinates[1], $coordinates[0], $name, $category, $icon);
-        }
-    }
+  public function setAdditionalCSSClasses( $new_additional_css_classes ) {
+    $this->additional_css_classes = $new_additional_css_classes;
+  }
 
 
-	public function addLine($from = array(), $to = array(), $color = "#FF3300") {
-		$this->contentLines .= "var points = [new google.maps.LatLng({$from[0]},{$from[1]}), new google.maps.LatLng({$to[0]},{$to[1]})];\n";
-		$this->contentLines .= "map.addOverlay(new GPolyline(points,'{$color}',4,0.6));\n";
-	}
-    /**
-          * Initialize the javascript code
-          * 
-          * @return void
-          */
-
-	public function includeGMapsJS() {
-		if(self::$jsIncluded) return;
-        // Google map JS
-        $this->content .= ''."\n";
-        
-        // Clusterer JS
-        if ($this->useClusterer==true) {
-			// Source: http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/src/
-			$this->content .= ''."\n";
-        }
-        
-        self::$jsIncluded = true;
-	
-	}
-
-    public function init() 
-    {
-		$this->includeGMapsJS();
-        // JS variable init
-        $this->content .= "\t".''."\n";
-
-        // Google map DIV
-        $this->content .= "\t".'
show_inline_map_div_style) { - $this->content .= 'style="width:'.$this->width.'px;height:'.$this->height.'px"'; - } - - if ($this->additional_css_classes != '') { - $this->content .= ' class="'.$this->additional_css_classes.'"'; - } - - - $this->content .= ">
\n"; - } + public function setMapStyles( $newStyles ) { + $this->jsonMapStyles = $newStyles; + } + + + + public function setDelayLoadMapFunction( $newDelay ) { + $this->delayLoadMapFunction = $newDelay; + } + + /** + * Set the useClusterer parameter (optimization to display a lot of marker) + * + * @param boolean $useClusterer use cluster or not + * @param string $clusterIcon the cluster icon + * @param int $maxVisibleMarkers max visible markers + * @param int $gridSize grid size + * @param int $minMarkersPerClusterer minMarkersPerClusterer + * @param int $maxLinesPerInfoBox maxLinesPerInfoBox + * + * @return void + */ + + public function setClusterer( $useClusterer, $gridSize=100, $maxZoom=9, $clustererLibraryPath='mappable/javascript/clusterer.js' ) { + $this->useClusterer = $useClusterer; + $this->gridSize = $gridSize; + $this->maxZoom = $maxZoom; + $this->clustererLibraryPath = $clustererLibraryPath; + } + + /** + * Set the ID of the default gmap DIV + * + * @param string $googleMapId the google div ID + * + * @return void + */ - /** - * Generate the gmap - * - * @return void - */ + public function setDivId( $googleMapId ) { + $this->googleMapId = $googleMapId; + } - public function generate() - { + /** + * Set the ID of the default gmap direction DIV + * + * @param string $googleMapDirectionId GoogleMap Direction ID for the HTML DIV + * + * @return void + */ + + public function setDirectionDivId( $googleMapDirectionId ) { + $this->googleMapDirectionId = $googleMapDirectionId; + } + + /** + * Set the size of the gmap + * + * @param int $width GoogleMap width + * @param int $height GoogleMap height + * + * @return void + */ + + public function setSize( $width, $height ) { + $this->width = $width; + $this->height = $height; + } + + /** + * Set the with of the gmap infowindow (on marker clik) + * + * @param int $infoWindowWidth GoogleMap info window width + * + * @return void + */ - $this->init(); + public function setInfoWindowWidth( $infoWindowWidth ) { + $this->infoWindowWidth = $infoWindowWidth; + } + + /** + * Set the size of the icon markers + * + * @param int $iconWidth GoogleMap marker icon width + * @param int $iconHeight GoogleMap marker icon height + * + * @return void + */ - // THIS SEEMS ROGUE $this->content .= "\t".''."\n"; + public function setIconSize( $iconWidth, $iconHeight ) { + $this->iconWidth = $iconWidth; + $this->iconHeight = $iconHeight; + } + + /** + * Set the lang of the gmap + * + * @param string $lang GoogleMap lang : fr,en,.. + * + * @return void + */ - // Center of the GMap - $geocodeCentre = ($this->latLongCenter) ? $this->latLongCenter : $this->geocoding($this->center); + public function setLang( $lang ) { + $this->lang = $lang; + } - if ($geocodeCentre[0]=="200") { // success - $latlngCentre = $geocodeCentre[2].",".$geocodeCentre[3]; - } else { // Paris - $latlngCentre = "48.8792,2.34778"; - } + /** + * Set the zoom of the gmap + * + * @param int $zoom GoogleMap zoom. + * + * @return void + */ - $this->content .= "\t".''."\n"; + public function setDefaultHideMarker( $defaultHideMarker ) { + $this->defaultHideMarker = $defaultHideMarker; + } + /** + * Get the google map content + * + * @return string the google map html code + */ + + public function getGoogleMap() { + return $this->content; + } + + /** + * Get URL content using cURL. + * + * @param string $url the url + * + * @return string the html code + * + * @todo add proxy settings + */ + + public function getContent( $url ) { + $curl = curl_init(); + curl_setopt( $curl, CURLOPT_TIMEOUT, 10 ); + curl_setopt( $curl, CURLOPT_CONNECTTIMEOUT, 5 ); + curl_setopt( $curl, CURLOPT_RETURNTRANSFER, TRUE ); + curl_setopt( $curl, CURLOPT_URL, $url ); + $data = curl_exec( $curl ); + curl_close( $curl ); + return $data; + } + + /** + * Geocoding an address (address -> lat,lng) + * + * @param string $address an address + * + * @return array array with precision, lat & lng + */ + + public function geocoding( $address ) { + $encodeAddress = urlencode( $address ); + $url = "http://maps.google.com/maps/geo?q=".$encodeAddress."&output=csv&key=".$this->googleMapKey; + + if ( function_exists( 'curl_init' ) ) { + $data = $this->getContent( $url ); + } else { + $data = file_get_contents( $url ); } + + $csvSplit = preg_split( "/,/", $data ); + $status = $csvSplit[0]; + + if ( strcmp( $status, "200" ) == 0 ) { + $return = $csvSplit; // successful geocode, $precision = $csvSplit[1],$lat = $csvSplit[2],$lng = $csvSplit[3]; + } else { + $return = null; // failure to geocode + } + + return $return; + } + + /** + * Add marker by his coord + * + * @param string $lat lat + * @param string $lng lngs + * @param string $html html code display in the info window + * @param string $category marker category + * @param string $icon an icon url + * + * @return void + */ + + public function addMarkerByCoords( $lat, $lng, $html='', $category='', $icon='' ) { + + error_log("Add marker by coords"); + + // Save the lat/lon to enable the automatic center/zoom + $this->maxLng = (float) max( (float)$lng, $this->maxLng ); + $this->minLng = (float) min( (float)$lng, $this->minLng ); + $this->maxLat = (float) max( (float)$lat, $this->maxLat ); + $this->minLat = (float) min( (float)$lat, $this->minLat ); + $this->centerLng = (float) ( $this->minLng + $this->maxLng ) / 2; + $this->centerLat = (float) ( $this->minLat + $this->maxLat ) / 2; + + //$this->contentMarker .= "\t\t\t".'createMarker('.$lat.','.$lng.',"'.$html.'","'.$category.'","'.$icon.'");'."\n"; + $m = array( + 'latitude' => $lat, + 'longitude' => $lng, + 'html' => $html, + 'category' => $category, + 'icon' => $icon + ); + array_push($this->markers, $m); + } + + /** + * Add marker by his address + * + * @param string $address an ddress + * @param string $content html code display in the info window + * @param string $category marker category + * @param string $icon an icon url + * + * @return void + */ + + public function addMarkerByAddress( $address, $content='', $category='', $icon='' ) { + error_log("Add marker by address"); + + $point = $this->geocoding( $address ); + if ( $point!==null ) { + $this->addMarkerByCoords( $point[2], $point[3], $content, $category, $icon ); + } else { + // throw new Exception('Adress not found : '.$address); + } + } + + /** + * Add marker by an array of coord + * + * @param string $coordtab an array of lat,lng,content + * @param string $category marker category + * @param string $icon an icon url + * + * @return void + */ + + public function addArrayMarkerByCoords( $coordtab, $category='', $icon='' ) { + error_log("Add array marker by coords"); + + foreach ( $coordtab as $coord ) { + $this->addMarkerByCoords( $coord[0], $coord[1], $coord[2], $category, $icon ); + } + } + + + /** + * Adds a {@link ViewableData} object that implements {@link Mappable} + * to the map. + * + * @param ViewableData $obj + */ + public function addMarkerAsObject( ViewableData $obj ) { + error_log("Add marker as object"); + + if ( $obj instanceof Mappable ) { + //if(($obj->getMappableLatitude() > 0) || ($obj->getMappableLongitude() > 0)) { + $cat = $obj->hasMethod( 'getMapCategory' ) ? $obj->getMapCategory() : "default"; + $this->addMarkerByCoords( $obj->getMappableLatitude(), $obj->getMappableLongitude(), $obj->getMapContent(), $cat, $obj->getMapPin() ); + //} + } + } + + + /** + * Draws a line between two {@link ViewableData} objects + * + * @param ViewableData $one The first point + * @param ViewableData $two The second point + * @param string $color The hexidecimal color of the line + */ + public function connectPoints( ViewableData $one, ViewableData $two, $color = "#FF3300" ) { + $this->addLine( + array( $one->getMappableLatitude(), $one->getMappableLongitude() ), + array( $two->getMappableLatitude(), $two->getMappableLongitude() ), + $color + ); + } + + + public function forTemplate() { + $this->generate(); + return $this->getGoogleMap(); + } + + + + /** + * Add marker by an array of address + * + * @param string $coordtab an array of address + * @param string $category marker category + * @param string $icon an icon url + * + * @return void + */ + + public function addArrayMarkerByAddress( $coordtab, $category='', $icon='' ) { + foreach ( $coordtab as $coord ) { + $this->addMarkerByAddress( $coord[0], $coord[1], $category, $icon ); + } + } + + /** + * Set a direction between 2 addresss and set a text panel + * + * @param string $from an address + * @param string $to an address + * @param string $idpanel id of the div panel + * + * @return void + */ + + public function addDirection( $from, $to, $idpanel='' ) { + $this->contentMarker .= 'addDirection("'.$from.'","'.$to.'","'.$idpanel.'");'; + } + + /** + * Parse a KML file and add markers to a category + * + * @param string $url url of the kml file compatible with gmap and gearth + * @param string $category marker category + * @param string $icon an icon url + * + * @return void + */ + + public function addKML( $url, $category='', $icon='' ) { + $xml = new SimpleXMLElement( $url, null, true ); + foreach ( $xml->Document->Folder->Placemark as $item ) { + $coordinates = explode( ',', (string) $item->Point->coordinates ); + $name = (string) $item->name; + $this->addMarkerByCoords( $coordinates[1], $coordinates[0], $name, $category, $icon ); + } + } + + + public function addLine( $from = array(), $to = array(), $color = "#FF3300" ) { + $this->contentLines .= "var points = [new google.maps.LatLng({$from[0]},{$from[1]}), new google.maps.LatLng({$to[0]},{$to[1]})];\n"; + $this->contentLines .= "map.addOverlay(new GPolyline(points,'{$color}',4,0.6));\n"; + } + /** + * Initialize the javascript code + * + * @return void + */ + + + + + + /** + * Generate the gmap + * + * @return void + */ + + public function generate() { + error_log("GENERATE: MARKERS ARE"); + error_log(print_r($this->markers,1)); + $jsonMarkers = json_encode($this->markers); + error_log('JSON MARKERS:'.$jsonMarkers); + + // Center of the GMap + $geocodeCentre = ( $this->latLongCenter ) ? $this->latLongCenter : $this->geocoding( $this->center ); + + // coordinates for centre depending on which method used + if ( $geocodeCentre[0]=="200" ) { // success + $latlngCentre = $geocodeCentre[2].",".$geocodeCentre[3]; + } else { // Paris + $latlngCentre = "48.8792,2.34778"; + } + + $lenLng = $this->maxLng - $this->minLng; + $lenLat = $this->maxLat - $this->minLat; + $this->minLng -= $lenLng * $this->coordCoef; + $this->maxLng += $lenLng * $this->coordCoef; + $this->minLat -= $lenLat * $this->coordCoef; + $this->maxLat += $lenLat * $this->coordCoef; + + $vars = new ArrayData(array( + 'EnableWindowZoom' => $this->enableWindowZoom, + 'MapMarkers' => $jsonMarkers, + 'DelayLoadMapFunction' => $this->delayLoadMapFunction, + 'DefaultHideMarker' => $this->defaultHideMarker, + 'LatLngCentre' => $latlngCentre, + 'EnableAutomaticCenterZoom' => $this->enableAutomaticCenterZoom, + 'Zoom' => $this->zoom, + 'MinLng' => $this->minLng, + 'MinLat' => $this->minLat, + 'MaxLng' => $this->maxLng, + 'MaxLat' => $this->maxLat, + 'MapType' => $this->mapTypes, + 'GoogleMapID' => $this->googleMapId + ) + ); + $this->content = $this->processTemplate('Map', $vars); + } + + + function processTemplate( $templateName, $templateVariables = null ) { + if (!$templateVariables) { + $templateVariables = new ArrayList(); + } + // Requirements::javascriptTemplate( MAPPABLE_MODULE_PATH.'/javascript/'.$this->mappingService.'/'.$templateName.'.ss', $templateVariables ); + $result = $templateVariables->renderWith($templateName.$this->mappingService ); + error_log("TEMPLATE:"); + error_log($result); + return $result; + } + + + + // Only download the mapping JS once + public function NeedToDownloadMappingJS() { + if ( self::$jsIncluded ) return false; + + self::$jsIncluded = true; + + } + + public function UseClusterer() { + return $this->useClusterer; + } + + public function ClusterLibraryPath() { + return $this->clustererLibraryPath; + } + + public function InfoWidth() { + return $this->infoWindowWidth; + } + + public function InfoWindowZoom() { + return $this->infoWindowZoom; + } + + public function ShowInlineMapDivStyle() { + return $this->show_inline_map_div_style; + } + + public function Width() { + return $this->width; + } + + + public function Height() { + return $this->height; + } + + public function AdditionalCssClasses() { + return $this->additional_css_classes; + } + + public function JsonMapStyles() { + return $this->jsonMapStyles; + } } \ No newline at end of file From 4d2fc449e9f42329bf81c31d93d39fbd5a9d87b8 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 18 Dec 2012 11:40:28 +0700 Subject: [PATCH 018/354] WIP: Changed google map references to map, in order to be generic --- code/MappableData.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/MappableData.php b/code/MappableData.php index f44b850..5ccc727 100644 --- a/code/MappableData.php +++ b/code/MappableData.php @@ -8,10 +8,10 @@ */ class MappableData extends Extension { - public function GoogleMap($width = null, $height = null, $zoom = 9) { - $gmap = GoogleMapUtil::get_map(new DataObjectSet($this->owner)); - $w = $width ? $width : GoogleMapUtil::$map_width; - $h = $height ? $height : GoogleMapUtil::$map_height; + public function RenderMap($width = null, $height = null, $zoom = 9) { + $gmap = MapUtil::get_map(new DataObjectSet($this->owner)); + $w = $width ? $width : MapUtil::$map_width; + $h = $height ? $height : MapUtil::$map_height; $gmap->setSize($w,$h); $gmap->setZoom($zoom); $gmap->setEnableAutomaticCenterZoom(false); @@ -25,8 +25,8 @@ public function GoogleMap($width = null, $height = null, $zoom = 9) { return $gmap; } public function StaticMap($width = null, $height = null) { - $w = $width ? $width : GoogleMapUtil::$map_width; - $h = $height ? $height : GoogleMapUtil::$map_height; + $w = $width ? $width : MapUtil::$map_width; + $h = $height ? $height : MapUtil::$map_height; $lat = $this->owner->getLatitude(); $lng = $this->owner->getLongitude(); From d538ef11e1220fd033bccd757f7b5d08f9a5e7d9 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 18 Dec 2012 11:40:59 +0700 Subject: [PATCH 019/354] WIP: Avoid reference to google maps, be generic --- code/MapUtil.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/code/MapUtil.php b/code/MapUtil.php index 0fc0e83..72569b0 100644 --- a/code/MapUtil.php +++ b/code/MapUtil.php @@ -168,16 +168,16 @@ public static function instance() $key = $key[$host]; } - $gmap = new GoogleMapAPI($key); + $gmap = new MapAPI($key); $gmap->setDivId(self::$div_id."_".self::$instances); $gmap->setEnableAutomaticCenterZoom(self::$automatic_center); $gmap->setDisplayDirectionFields(self::$direction_fields); $gmap->setSize(self::$map_width, self::$map_height); $gmap->setDefaultHideMarker(self::$hide_marker); - $gmap->setMapType(self::$map_type); - $gmap->setInfoWindowWidth(self::$info_window_width); - $gmap->setCenter(self::$center); - $gmap->setIconSize(self::$iconWidth, self::$iconHeight); + $gmap->setMapType(self::$map_type); + $gmap->setInfoWindowWidth(self::$info_window_width); + $gmap->setCenter(self::$center); + $gmap->setIconSize(self::$iconWidth, self::$iconHeight); return $gmap; } @@ -209,5 +209,8 @@ public static function get_map(DataObjectSet $set) { } return $gmap; } + + + } \ No newline at end of file From 5e699c161b7e0534628b8d2f4194ecc38beb81b9 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 18 Dec 2012 11:41:21 +0700 Subject: [PATCH 020/354] WIP: Change reference from google map to be generic --- code/MappableDataObjectSet.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/MappableDataObjectSet.php b/code/MappableDataObjectSet.php index 6baa209..3ac23bc 100755 --- a/code/MappableDataObjectSet.php +++ b/code/MappableDataObjectSet.php @@ -8,10 +8,10 @@ */ class MappableDataObjectSet extends Extension { - public function GoogleMap($width = null, $height = null) { - $gmap = GoogleMapUtil::get_map($this->owner); - $w = $width ? $width : GoogleMapUtil::$map_width; - $h = $height ? $height : GoogleMapUtil::$map_height; + public function RenderMap($width = null, $height = null) { + $gmap = MapUtil::get_map($this->owner); + $w = $width ? $width : MapUtil::$map_width; + $h = $height ? $height : MapUtil::$map_height; $gmap->setSize($w,$h); return $gmap; } From 52f3c0d25db5b33b013cd77ef123cb3dae58095f Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 18 Dec 2012 11:41:43 +0700 Subject: [PATCH 021/354] WIP: Adding functionality as per old mappable, JS errors still though --- javascript/google/MapGoogle.ss | 129 ++++++++++++++++++++++++++++++--- 1 file changed, 118 insertions(+), 11 deletions(-) diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogle.ss index e3a3fff..3f09902 100644 --- a/javascript/google/MapGoogle.ss +++ b/javascript/google/MapGoogle.ss @@ -1,4 +1,4 @@ -MAPPING TEMPLATE 4: + <% if UseClusterer %> @@ -9,8 +9,8 @@ MAPPING TEMPLATE 4: Main JS below + +
+<% if ShowInlineMapDivStyle %> + style="width:{$Width}px;{$Height}px;"'; +<% end_if %> + +<% if AdditionalCssClasses %> + class="$AdditionalCssClasses"; +<% end_if %> +
+ + + End of main JS \ No newline at end of file From ad1276a682c65cb2fb54e109907a6971a77ad07a Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 18 Dec 2012 18:02:29 +0700 Subject: [PATCH 022/354] WIP: Pointers now working but no map tiles. Setting a breakpoint so I can reset back to a semi working version if necessary --- _config.php | 2 +- javascript/google/MapGoogle.ss | 61 ++++++++++++++++++++++------------ 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/_config.php b/_config.php index efa2075..d942a13 100755 --- a/_config.php +++ b/_config.php @@ -2,7 +2,7 @@ Object::add_extension("DataObject","MappableData"); -Object::add_extension("DataObjectSet","MappableDataObjectSet"); +Object::add_extension("DataList","MappableDataObjectSet"); //define global path to Components' root folder if(!defined('MAPPABLE_MODULE_PATH')) { diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogle.ss index 3f09902..430c421 100644 --- a/javascript/google/MapGoogle.ss +++ b/javascript/google/MapGoogle.ss @@ -1,13 +1,17 @@ +
+DOWNLOADJS: $DownloadJS
+
- - +<% if DownloadJS %> +*** DOWNLOADING JS **** + <% if UseClusterer %> <% end_if %> - - -Main JS below + <% else %> + no need for JS +<% end_if %> -
+
+ <% if ShowInlineMapDivStyle %> style="width:{$Width}px;{$Height}px;"'; <% end_if %> @@ -119,10 +121,10 @@ Main JS below -End of main JS \ No newline at end of file +<% if DelayLoadMapFunction %> +console.log('delay map loading'); +<% else %> +console.log("adding google maps load callback"); +google.maps.event.addDomListener(window, 'load', loadedGoogleMapsAPI); +<% end_if %> + \ No newline at end of file From 2012a81bc18562f7f191df5384e76a96d773a5f7 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 18 Dec 2012 18:02:53 +0700 Subject: [PATCH 023/354] ENHANCEMENT: More variables passed to template --- code/MapAPI.php | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index 8de347a..80421c2 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -506,9 +506,6 @@ public function geocoding( $address ) { */ public function addMarkerByCoords( $lat, $lng, $html='', $category='', $icon='' ) { - - error_log("Add marker by coords"); - // Save the lat/lon to enable the automatic center/zoom $this->maxLng = (float) max( (float)$lng, $this->maxLng ); $this->minLng = (float) min( (float)$lng, $this->minLng ); @@ -540,8 +537,6 @@ public function addMarkerByCoords( $lat, $lng, $html='', $category='', $icon='' */ public function addMarkerByAddress( $address, $content='', $category='', $icon='' ) { - error_log("Add marker by address"); - $point = $this->geocoding( $address ); if ( $point!==null ) { $this->addMarkerByCoords( $point[2], $point[3], $content, $category, $icon ); @@ -561,8 +556,6 @@ public function addMarkerByAddress( $address, $content='', $category='', $icon=' */ public function addArrayMarkerByCoords( $coordtab, $category='', $icon='' ) { - error_log("Add array marker by coords"); - foreach ( $coordtab as $coord ) { $this->addMarkerByCoords( $coord[0], $coord[1], $coord[2], $category, $icon ); } @@ -576,8 +569,6 @@ public function addArrayMarkerByCoords( $coordtab, $category='', $icon='' ) { * @param ViewableData $obj */ public function addMarkerAsObject( ViewableData $obj ) { - error_log("Add marker as object"); - if ( $obj instanceof Mappable ) { //if(($obj->getMappableLatitude() > 0) || ($obj->getMappableLongitude() > 0)) { $cat = $obj->hasMethod( 'getMapCategory' ) ? $obj->getMapCategory() : "default"; @@ -681,10 +672,7 @@ public function addLine( $from = array(), $to = array(), $color = "#FF3300" ) { */ public function generate() { - error_log("GENERATE: MARKERS ARE"); - error_log(print_r($this->markers,1)); $jsonMarkers = json_encode($this->markers); - error_log('JSON MARKERS:'.$jsonMarkers); // Center of the GMap $geocodeCentre = ( $this->latLongCenter ) ? $this->latLongCenter : $this->geocoding( $this->center ); @@ -702,6 +690,7 @@ public function generate() { $this->maxLng += $lenLng * $this->coordCoef; $this->minLat -= $lenLat * $this->coordCoef; $this->maxLat += $lenLat * $this->coordCoef; + $this->NeedToDownloadMappingJS(); $vars = new ArrayData(array( 'EnableWindowZoom' => $this->enableWindowZoom, @@ -711,12 +700,16 @@ public function generate() { 'LatLngCentre' => $latlngCentre, 'EnableAutomaticCenterZoom' => $this->enableAutomaticCenterZoom, 'Zoom' => $this->zoom, + 'MaxZoom' => $this->maxZoom, + 'GridSize' => $this->gridSize, 'MinLng' => $this->minLng, 'MinLat' => $this->minLat, 'MaxLng' => $this->maxLng, 'MaxLat' => $this->maxLat, - 'MapType' => $this->mapTypes, - 'GoogleMapID' => $this->googleMapId + 'MapType' => $this->MapTypeId, + 'GoogleMapID' => $this->googleMapId, + 'Lang'=>$this->lang, + 'DownloadJS' => (self::$jsIncluded) ) ); $this->content = $this->processTemplate('Map', $vars); @@ -729,8 +722,6 @@ function processTemplate( $templateName, $templateVariables = null ) { } // Requirements::javascriptTemplate( MAPPABLE_MODULE_PATH.'/javascript/'.$this->mappingService.'/'.$templateName.'.ss', $templateVariables ); $result = $templateVariables->renderWith($templateName.$this->mappingService ); - error_log("TEMPLATE:"); - error_log($result); return $result; } From 43b30b8fd1df363780d65c0fc1c2a565f5fbd3a9 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 20 Dec 2012 16:43:01 +0700 Subject: [PATCH 024/354] ENHANCMENT: Use fluster for clustering instead of MapClusterer --- javascript/Fluster2.packed.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 javascript/Fluster2.packed.js diff --git a/javascript/Fluster2.packed.js b/javascript/Fluster2.packed.js new file mode 100644 index 0000000..58fc252 --- /dev/null +++ b/javascript/Fluster2.packed.js @@ -0,0 +1,24 @@ +/* + * Fluster2 0.1.1 + * Copyright (C) 2009 Fusonic GmbH + * + * This file is part of Fluster2. + * + * Fluster2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Fluster2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +function Fluster2(_map,_debug){var map=_map;var projection=new Fluster2ProjectionOverlay(map);var me=this;var clusters=new Object();var markersLeft=new Object();this.debugEnabled=_debug;this.gridSize=60;this.markers=new Array();this.currentZoomLevel=-1;this.styles={0:{image:'http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m1.png',textColor:'#FFFFFF',width:53,height:52},10:{image:'http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m2.png',textColor:'#FFFFFF',width:56,height:55},20:{image:'http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m3.png',textColor:'#FFFFFF',width:66,height:65}};var zoomChangedTimeout=null;function createClusters(){var zoom=map.getZoom();if(clusters[zoom]){me.debug('Clusters for zoom level '+zoom+' already initialized.')}else{var clustersThisZoomLevel=new Array();var clusterCount=0;var markerCount=me.markers.length;for(var i=0;i=0;j--){var cluster=clustersThisZoomLevel[j];if(cluster.contains(markerPosition)){cluster.addMarker(marker);done=true;break}}if(!done){var cluster=new Fluster2Cluster(me,marker);clustersThisZoomLevel.push(cluster);clusterCount++}}clusters[zoom]=clustersThisZoomLevel;me.debug('Initialized '+clusters[zoom].length+' clusters for zoom level '+zoom+'.')}if(clusters[me.currentZoomLevel]){for(var i=0;i1){for(var i=0;ii){this.style=styles[i]}else{break}}google.maps.OverlayView.call(this);this.setMap(this.map);this.draw()};Fluster2ClusterMarker.prototype=new google.maps.OverlayView();Fluster2ClusterMarker.prototype.draw=function(){if(this.div==null){var me=this;this.div=document.createElement('div');this.div.style.position='absolute';this.div.style.width=this.style.width+'px';this.div.style.height=this.style.height+'px';this.div.style.lineHeight=this.style.height+'px';this.div.style.background='transparent url("'+this.style.image+'") 50% 50% no-repeat';this.div.style.color=this.style.textColor;this.div.style.textAlign='center';this.div.style.fontFamily='Arial, Helvetica';this.div.style.fontSize='11px';this.div.style.fontWeight='bold';this.div.innerHTML=this.markerCount;this.div.style.cursor='pointer';google.maps.event.addDomListener(this.div,'click',function(){me.map.fitBounds(me.cluster.getMarkerBounds())});this.getPanes().overlayLayer.appendChild(this.div)}var position=this.getProjection().fromLatLngToDivPixel(this.position);this.div.style.left=(position.x-parseInt(this.style.width/2))+'px';this.div.style.top=(position.y-parseInt(this.style.height/2))+'px'};Fluster2ClusterMarker.prototype.hide=function(){this.div.style.display='none'};Fluster2ClusterMarker.prototype.show=function(){this.div.style.display='block'}; +function Fluster2ProjectionOverlay(map){google.maps.OverlayView.call(this);this.setMap(map);this.getP=function(){return this.getProjection()}}Fluster2ProjectionOverlay.prototype=new google.maps.OverlayView();Fluster2ProjectionOverlay.prototype.draw=function(){}; \ No newline at end of file From 4583a7f47e401c469832ea782f5e354dd76f461f Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 20 Dec 2012 16:43:29 +0700 Subject: [PATCH 025/354] BUGFIX: Clustering now working --- code/MapAPI.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index 80421c2..3a00084 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -157,7 +157,7 @@ class MapAPI extends ViewableData protected $useClusterer = false; protected $gridSize = 100; protected $maxZoom = 9; - protected $clustererLibrarypath = 'markerclusterer_packed.js'; + protected $clustererLibrarypath = 'Fluster2.js'; /** Enable automatic center/zoom **/ protected $enableAutomaticCenterZoom = false; @@ -247,7 +247,7 @@ public function setDelayLoadMapFunction( $newDelay ) { * @return void */ - public function setClusterer( $useClusterer, $gridSize=100, $maxZoom=9, $clustererLibraryPath='mappable/javascript/clusterer.js' ) { + public function setClusterer( $useClusterer, $gridSize=100, $maxZoom=9, $clustererLibraryPath='mappable/javascript/Fluster2.packed.js' ) { $this->useClusterer = $useClusterer; $this->gridSize = $gridSize; $this->maxZoom = $maxZoom; @@ -709,9 +709,13 @@ public function generate() { 'MapType' => $this->MapTypeId, 'GoogleMapID' => $this->googleMapId, 'Lang'=>$this->lang, - 'DownloadJS' => (self::$jsIncluded) + 'UseClusterer'=>$this->useClusterer, + 'DownloadJS' => (self::$jsIncluded), + 'ClusterLibraryPath' => $this->clustererLibraryPath ) ); + + $this->content = $this->processTemplate('Map', $vars); } @@ -735,13 +739,9 @@ public function NeedToDownloadMappingJS() { } - public function UseClusterer() { - return $this->useClusterer; - } + - public function ClusterLibraryPath() { - return $this->clustererLibraryPath; - } + public function InfoWidth() { return $this->infoWindowWidth; From d0a95278208b0432cd8a9ce89bd87dd49e6ba05a Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 21 Dec 2012 22:31:48 +0700 Subject: [PATCH 026/354] ENHANCEMENT: Added lines functionality --- code/MapAPI.php | 60 +++++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index 3a00084..1192d11 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -41,6 +41,9 @@ class MapAPI extends ViewableData /** Icon height of the gmarker **/ protected $iconHeight = 34; + /* array of lines to be drawn on the map */ + protected $lines = array(); + /** * * @@ -183,7 +186,6 @@ class MapAPI extends ViewableData /** factor by which to fudge the boundaries so that when we zoom encompass, the markers aren't too close to the edge **/ protected $coordCoef = 0.01; - protected $lines = array (); protected $contentLines = ''; @@ -652,9 +654,19 @@ public function addKML( $url, $category='', $icon='' ) { public function addLine( $from = array(), $to = array(), $color = "#FF3300" ) { - $this->contentLines .= "var points = [new google.maps.LatLng({$from[0]},{$from[1]}), new google.maps.LatLng({$to[0]},{$to[1]})];\n"; - $this->contentLines .= "map.addOverlay(new GPolyline(points,'{$color}',4,0.6));\n"; + $line = array( + 'lat1' => $from[0], + 'lon1' => $from[1], + 'lat2' => $to[0], + 'lon2' => $to[1], + 'color' => $color + ); + + array_push($this->lines, $line); + + } + /** * Initialize the javascript code * @@ -673,6 +685,7 @@ public function addLine( $from = array(), $to = array(), $color = "#FF3300" ) { public function generate() { $jsonMarkers = json_encode($this->markers); + $linesJson = json_encode($this->lines); // Center of the GMap $geocodeCentre = ( $this->latLongCenter ) ? $this->latLongCenter : $this->geocoding( $this->center ); @@ -692,7 +705,15 @@ public function generate() { $this->maxLat += $lenLat * $this->coordCoef; $this->NeedToDownloadMappingJS(); + $vars = new ArrayData(array( + 'JsonMapStyles' => $this->jsonMapStyles, + 'AdditionalCssClasses' => $this->additional_css_classes, + 'Width' => $this->Width, + 'Height' => $this->Height, + 'InfoWindowWidth' => $this->InfoWindowWidth, + 'ShowInlineMapDivStyle' => $this->show_inline_map_div_style, + 'InfoWindowZoom' => $this->InfoWindowZoom, 'EnableWindowZoom' => $this->enableWindowZoom, 'MapMarkers' => $jsonMarkers, 'DelayLoadMapFunction' => $this->delayLoadMapFunction, @@ -711,7 +732,8 @@ public function generate() { 'Lang'=>$this->lang, 'UseClusterer'=>$this->useClusterer, 'DownloadJS' => (self::$jsIncluded), - 'ClusterLibraryPath' => $this->clustererLibraryPath + 'ClusterLibraryPath' => $this->clustererLibraryPath, + 'Lines' => $linesJson ) ); @@ -741,34 +763,4 @@ public function NeedToDownloadMappingJS() { - - - public function InfoWidth() { - return $this->infoWindowWidth; - } - - public function InfoWindowZoom() { - return $this->infoWindowZoom; - } - - public function ShowInlineMapDivStyle() { - return $this->show_inline_map_div_style; - } - - public function Width() { - return $this->width; - } - - - public function Height() { - return $this->height; - } - - public function AdditionalCssClasses() { - return $this->additional_css_classes; - } - - public function JsonMapStyles() { - return $this->jsonMapStyles; - } } \ No newline at end of file From a2e773b92570a63edf92cc768833e5c8f79670bf Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 21 Dec 2012 22:39:31 +0700 Subject: [PATCH 027/354] ENHANCEMENT: Addition of clustering and lines --- javascript/google/MapGoogle.ss | 85 ++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogle.ss index 430c421..0b6a88c 100644 --- a/javascript/google/MapGoogle.ss +++ b/javascript/google/MapGoogle.ss @@ -1,9 +1,4 @@ -
-DOWNLOADJS: $DownloadJS
-
- <% if DownloadJS %> -*** DOWNLOADING JS **** <% if UseClusterer %> @@ -17,7 +12,7 @@ DOWNLOADJS: $DownloadJS console.log('Map template JS loaded'); var gmarkers = []; var gicons = []; - var clusterer = null; + var fluster = null; var current_lat = 0; var current_lng = 0; var layer_wikipedia = null; @@ -28,15 +23,9 @@ DOWNLOADJS: $DownloadJS var infoWindow = new google.maps.InfoWindow({ content: 'test', maxWidth: 300 }); - - - function createMarker(lat,lng,html,category,icon) { var marker = new google.maps.Marker(); marker.setPosition(new google.maps.LatLng(lat,lng)); - - - marker.mycategory = category; if (icon != '') { @@ -45,12 +34,11 @@ DOWNLOADJS: $DownloadJS } <% if UseClusterer %> + fluster.addMarker(marker); <% else %> marker.setMap(map); <% end_if %> - - html = '
Something
'; google.maps.event.addListener(marker,"click",function() { <% if EnableWindowZoom %> map.setCenter(new google.maps.LatLng(lat,lng),$InfoWindowZoom); @@ -104,19 +92,32 @@ DOWNLOADJS: $DownloadJS } + function addLines(lines) { + for (i=0; i -
- -<% if ShowInlineMapDivStyle %> - style="width:{$Width}px;{$Height}px;"'; -<% end_if %> + -<% if AdditionalCssClasses %> - class="$AdditionalCssClasses"; -<% end_if %> +
style="width:{$Width}px;{$Height}px;"<% end_if %><% if AdditionalCssClasses %>class="$AdditionalCssClasses"<% end_if %> +>
@@ -124,10 +125,13 @@ DOWNLOADJS: $DownloadJS function loadmaps() { console.log('mapping service load'); map = new google.maps.Map(document.getElementById("$GoogleMapID")); - map.setMapTypeId(google.maps.MapTypeId.SATELLITE); + <% if UseClusterer %> + fluster = new Fluster2(map); + <% end_if %> geocoder = new google.maps.Geocoder(); <% if JsonMapStyles %> + console.log('JSON MAP STYLES'); var mappableStyles=$JsonMapStyles; map.setOptions({styles: mappableStyles}); <% end_if %> @@ -136,17 +140,29 @@ console.log('mapping service load'); <% if EnableAutomaticCenterZoom %> map.setCenter(new google.maps.LatLng($LatLngCentre)); + console.log("SET MAP CENTRE TO "+$LatLngCentre); var bds = new google.maps.LatLngBounds(new google.maps.LatLng($MinLat,$MinLng), new google.maps.LatLng($MaxLat,$MaxLng)); - map.setZoom(map.fitBounds(bds)); + console.log("BOUNDS"); + console.log(bds); + map.fitBounds(bds); + // map.setZoom(); + <% else %> map.setCenter(new google.maps.LatLng($LatLngCentre)); map.setZoom($Zoom); + console.log("SET MAP CENTRE TO "+$LatLngCentre); + console.log("T2 SET MAP ZOOM TO $Zoom"); + + + <% end_if %> + <% if $MapType %> + // map.setMapTypeId($MapType); <% end_if %> + map.setMapTypeId(google.maps.MapTypeId.ROADMAP); - map.setMapTypeId($MapType); google.maps.event.addListener(map,"click",function(overlay,latlng) { if (latlng) { current_lat=latlng.lat(); @@ -154,23 +170,14 @@ console.log('mapping service load'); } }); - - addAllMarkers(); - - /* - Add lines here - */ - - + addAllMarkers(); + addLines($Lines); + fluster.initialize(); + } - <% if UseClusterer %> - var markerCluster = new MarkerClusterer(map, gmarkers,{gridSize: $GridSize, maxZoom: $MaxZoom}); - <% end_if %> - - function loadedGoogleMapsAPI() { console.log('callback from google maps'); loadmaps(); From 3e903e448366d3bbcc2f38f68acef709eae3788c Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 21 Dec 2012 23:26:24 +0700 Subject: [PATCH 028/354] ENHANCEMENT: Add KML files --- code/MapAPI.php | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index 1192d11..e515d41 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -44,6 +44,9 @@ class MapAPI extends ViewableData /* array of lines to be drawn on the map */ protected $lines = array(); + /* kml file to be rendered */ + protected $kmlFiles = array(); + /** * * @@ -187,8 +190,6 @@ class MapAPI extends ViewableData protected $coordCoef = 0.01; - protected $contentLines = ''; - protected static $jsIncluded = false; @@ -637,19 +638,12 @@ public function addDirection( $from, $to, $idpanel='' ) { * Parse a KML file and add markers to a category * * @param string $url url of the kml file compatible with gmap and gearth - * @param string $category marker category - * @param string $icon an icon url * * @return void */ - public function addKML( $url, $category='', $icon='' ) { - $xml = new SimpleXMLElement( $url, null, true ); - foreach ( $xml->Document->Folder->Placemark as $item ) { - $coordinates = explode( ',', (string) $item->Point->coordinates ); - $name = (string) $item->name; - $this->addMarkerByCoords( $coordinates[1], $coordinates[0], $name, $category, $icon ); - } + public function addKML( $url ) { + array_push($this->kmlFiles, $url); } @@ -686,6 +680,7 @@ public function addLine( $from = array(), $to = array(), $color = "#FF3300" ) { public function generate() { $jsonMarkers = json_encode($this->markers); $linesJson = json_encode($this->lines); + $kmlJson = json_encode($this->kmlFiles); // Center of the GMap $geocodeCentre = ( $this->latLongCenter ) ? $this->latLongCenter : $this->geocoding( $this->center ); @@ -733,7 +728,8 @@ public function generate() { 'UseClusterer'=>$this->useClusterer, 'DownloadJS' => (self::$jsIncluded), 'ClusterLibraryPath' => $this->clustererLibraryPath, - 'Lines' => $linesJson + 'Lines' => $linesJson, + 'KmlFiles' => $kmlJson ) ); From 6d917e04e9c955cac2dd2fc49918b1d5a14302ca Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 21 Dec 2012 23:26:41 +0700 Subject: [PATCH 029/354] ENHANCEMENT: Add KML files as kml layers --- javascript/google/MapGoogle.ss | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogle.ss index 0b6a88c..b1ef10d 100644 --- a/javascript/google/MapGoogle.ss +++ b/javascript/google/MapGoogle.ss @@ -112,6 +112,16 @@ } + function addKmlFiles(kmlFiles) { + for (var i=0; i< kmlFiles.length; i++) { + var kmlFile = kmlFiles[i]; + var kmlLayer = new google.maps.KmlLayer(kmlFile, { + suppressInfoWindows:true, + map: map + }); + } + } + @@ -172,6 +182,7 @@ console.log('mapping service load'); addAllMarkers(); addLines($Lines); + addKmlFiles($KmlFiles); fluster.initialize(); } From 5376b3c48ab061fe5144ea779d03da77080ab6fd Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 3 Jan 2013 13:18:00 +0700 Subject: [PATCH 030/354] BUGFIX: Behavour consistent over multiple page loads, was previously failing if the browser was not refreshed --- javascript/mapField.js | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/javascript/mapField.js b/javascript/mapField.js index 6515a7b..b0d6475 100755 --- a/javascript/mapField.js +++ b/javascript/mapField.js @@ -1,8 +1,11 @@ // FIXME avoid global var marker; + +//('map field loaded'); + /* -The following variables are set up by a literal field int he map field, as they names of these fields can of course vary +The following variables are set up by a LiteralField in the LatLongField field, as they names of these fields can of course vary - latFieldName: latittude field name - lonFieldName: longitutude field name - zoomFieldName: zoom field name @@ -13,6 +16,7 @@ The following variables are set up by a literal field int he map field, as they function gmloaded() { + //('google maps call back'); initLivequery(); //initMap(); } @@ -20,6 +24,8 @@ The following variables are set up by a literal field int he map field, as they // initialise the map function initMap() { + //('init map'); + var myOptions = { zoom: 16, disableDefaultUI: false, @@ -31,15 +37,28 @@ The following variables are set up by a literal field int he map field, as they }; (function($) { - var latField = $('input[name="$LatFieldName"]'); - var lonField = $('input[name="$LonFieldName"]'); - var zoomField = $('input[name="$ZoomFieldName"]'); + var gm = $('#GoogleMap'); + //(gm); + var latFieldName = gm.attr('data-latfieldname'); + //("LAT FIELD NAME:"+latFieldName); + + var latField = $('input[name='+gm.attr('data-latfieldname')+']'); //$('input[name="$LatFieldName"]'); + var lonField = $('input[name='+gm.attr('data-lonfieldname')+']'); // $('input[name="$LonFieldName"]'); + var zoomField = $('input[name='+gm.attr('data-zoomfieldname')+']'); // $('input[name="$ZoomFieldName"]'); + + //("latitude field"); + //(latField); + //(latField.val()); + //('lon'); + //(lonField.val()); myOptions.center = new google.maps.LatLng(latField.val(), lonField.val()); if (zoomField.length) { myOptions['zoom'] = parseInt(zoomField.val()); + //("ZOOM="+myOptions['zoom']); + //(zoomField); } @@ -68,8 +87,8 @@ The following variables are set up by a literal field int he map field, as they google.maps.event.addListener(map, "zoom_changed", function(e) { - if ($('input[name="$ZoomFieldName"]').length) { - $('input[name="$ZoomFieldName"]').val(map.getZoom()); + if (zoomField.length) { + zoomField.val(map.getZoom()); } }); @@ -84,11 +103,11 @@ The following variables are set up by a literal field int he map field, as they //google.maps.event.trigger(map, 'resize'); $( document ).bind( "pageshow", function( event, data ){ - alert('page show'); google.maps.event.trigger(map, 'resize'); }); + // When the location tab is clicked, resize the map $('a[href="#Root_Location"]').click(function() { google.maps.event.trigger(map, 'resize'); map.setCenter(marker.getPosition()); @@ -207,6 +226,8 @@ The following variables are set up by a literal field int he map field, as they function initLivequery() { (function($) { + //('init live query'); + //triggers $('input[name=action_GetCoords]').livequery('click', function(e) { // get the data needed to ask coords @@ -227,8 +248,8 @@ The following variables are set up by a literal field int he map field, as they $('.geocodedSearchResults li').livequery('click', function(e) { // get the data needed to ask coords var t = $(this); - console.log("ENTRY CLICKED"); - console.log(t); + //("ENTRY CLICKED"); + //(t); var lat = t.attr("lat"); var lon = t.attr("lon"); var address = t.html(); From 3a204f42f0108767f76b606eb790063c6cebe43c Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 3 Jan 2013 13:18:47 +0700 Subject: [PATCH 031/354] BUGFIX: Removed dependency on map field and ensured JS is loaded each time (templates appear to be loaded only once) --- code/LatLongField.php | 114 ++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 61 deletions(-) diff --git a/code/LatLongField.php b/code/LatLongField.php index 445692c..41a7383 100755 --- a/code/LatLongField.php +++ b/code/LatLongField.php @@ -1,73 +1,60 @@ addressFields = $addressFields; - $this->buttonText = $buttonText ? $buttonText : _t('LatLongField.LOOKUP','Search'); + $this->buttonText = $buttonText ? $buttonText : _t( 'LatLongField.LOOKUP', 'Search' ); $this->latField = $children[0]->getName(); $this->longField = $children[1]->getName(); - if (sizeof($children) == 3) { + if ( sizeof( $children ) == 3 ) { $this->zoomField = $children[2]->getName(); } $name = ""; - foreach($children as $field) { + foreach ( $children as $field ) { $name .= $field->getName(); } // hide the lat long and zoom fields from the interface - foreach ($this->FieldList() as $fieldToHide) { - $fieldToHide->addExtraClass('hide'); + foreach ( $this->FieldList() as $fieldToHide ) { + $fieldToHide->addExtraClass( 'hide' ); } $this->name = $name; } - - + + public function hasData() {return true;} - - public function FieldHolder($properties = array()) { - Requirements::javascript(THIRDPARTY_DIR.'/jquery/jquery.js'); - Requirements::javascript(THIRDPARTY_DIR.'jquery-livequery/jquery.livequery.js'); - - Requirements::javascript(THIRDPARTY_DIR.'/jquery-metadata/jquery.metadata.js'); - - /* - Requirements::customScript(" - var latlonvars = { - 'latFieldName': '".$this->latField."', - 'lonFieldName': '".$this->lonField."', - 'zoomFieldName': '".$this->zoomField."' - - - }; - alert('test inline'); - - "); - */ - $js = ' + + public function FieldHolder( $properties = array() ) { + Requirements::javascript( THIRDPARTY_DIR.'/jquery/jquery.js' ); + Requirements::javascript( THIRDPARTY_DIR.'/jquery-livequery/jquery.livequery.js' ); + Requirements::javascript( THIRDPARTY_DIR.'/jquery-metadata/jquery.metadata.js' ); + //Requirements::javascript(MAPPABLE_MODULE_PATH.'/javascript/mapField.js'); + + $js = ' <% if UseClusterer %> - + <% end_if %> - <% else %> - no need for JS + + + <% end_if %> + <% if UseClusterer %> From c6be2e85b822ad10dae97081d7017e9ba6b38e48 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 27 Mar 2013 23:20:20 +0700 Subject: [PATCH 065/354] WIP: Setting up variables properly so that they can be used in javascript calls to functions in mapUtil.js --- code/MapAPI.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index a5e1312..79b63ce 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -129,7 +129,7 @@ class MapAPI extends ViewableData ];
*/ - protected $jsonMapStyles = null; + protected $jsonMapStyles = '[]'; protected $delayLoadMapFunction = false; @@ -698,11 +698,13 @@ public function generate() { // coordinates for centre depending on which method used if ( $geocodeCentre[0]=="200" ) { // success - $latlngCentre = $geocodeCentre[2].",".$geocodeCentre[3]; + $latlngCentre = array('lat'=>$geocodeCentre[2],'lng' => $geocodeCentre[3]); } else { // Paris - $latlngCentre = "48.8792,2.34778"; + $latlngCentre = array('lat'=>48.8792, 'lng' => 2.34778); } + $this->LatLngCentreJSON = stripslashes(json_encode($latlngCentre)); + $lenLng = $this->maxLng - $this->minLng; $lenLat = $this->maxLat - $this->minLat; $this->minLng -= $lenLng * $this->coordCoef; @@ -725,6 +727,10 @@ public function generate() { $this->defaultHideMarker = 'false'; } + if (!$this->MapTypeId) { + $this->MapTypeId = 'false'; + } + $vars = new ArrayData(array( 'JsonMapStyles' => $this->jsonMapStyles, @@ -738,7 +744,7 @@ public function generate() { 'MapMarkers' => $jsonMarkers, 'DelayLoadMapFunction' => $this->delayLoadMapFunction, 'DefaultHideMarker' => $this->defaultHideMarker, - 'LatLngCentre' => $latlngCentre, + 'LatLngCentre' => $this->LatLngCentreJSON, 'EnableAutomaticCenterZoom' => $this->enableAutomaticCenterZoom, 'Zoom' => $this->zoom, 'MaxZoom' => $this->maxZoom, From 7ed93b1623601ab7aef0c81556150002a9f7e3cf Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 27 Mar 2013 23:22:55 +0700 Subject: [PATCH 066/354] WIP: Moving all functions to separate JS file mapUtil.js and only keeping relevant map info as inline javascript --- javascript/google/MapGoogle.ss | 78 +++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogle.ss index f794fb3..0cb72d1 100644 --- a/javascript/google/MapGoogle.ss +++ b/javascript/google/MapGoogle.ss @@ -1,18 +1,43 @@ <% if DownloadJS %> - +**** DOWNLOAD JS **** + +<% if DelayLoadMapFunction %> +console.log('delay map loading'); +<% else %> +T1 + + + +<% end_if %> + + + + + + + +<% end_if %> <% if UseClusterer %> + + <% end_if %> - - - -<% end_if %> +
style="width:{$Width}px;{$Height}px;"<% end_if %><% if AdditionalCssClasses %>class="$AdditionalCssClasses"<% end_if %> @@ -36,8 +61,13 @@
+ \ No newline at end of file From df040c5d766154085006996434476adb6f49366f Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 27 Mar 2013 23:23:37 +0700 Subject: [PATCH 067/354] WIP: gmarkers now an associative array keyed to the map id --- javascript/google/maputil.js | 110 +++++++++++++++++++++++++++++++---- 1 file changed, 100 insertions(+), 10 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index 62eed45..4977ec1 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -1,5 +1,8 @@ -function createMarker(lat, lng, html, category, icon, useClusterer, enableWindowZoom, defaultHideMarker) { +function createMarker(map,lat, lng, html, category, icon, useClusterer, enableWindowZoom, defaultHideMarker) { + mapId = map.getDiv().attributes['id'].textContent; var marker = new google.maps.Marker(); + + console.log("Creating marker for "+mapId+" at "+lat+','+lng); marker.setPosition(new google.maps.LatLng(lat, lng)); marker.mycategory = category; @@ -14,19 +17,20 @@ function createMarker(lat, lng, html, category, icon, useClusterer, enableWindow marker.setMap(map); } - - google.maps.event.addListener(marker, "click", function() { + + google.maps.event.addListener(marker, "click", function() { if (enableWindowZoom) { - map.setCenter(new google.maps.LatLng(lat, lng), $InfoWindowZoom); + map.setCenter(new google.maps.LatLng(lat, lng), $InfoWindowZoom); } infoWindow.setContent(html); infoWindow.open(map, this); }); - gmarkers.push(marker); + gmarkers[mapId].push(marker); if (defaultHideMarker) { + console.log("Hide marker"); marker.hide(); } @@ -65,14 +69,12 @@ function showAddress(address) { } -//function createMarker(lat, lng, html, category, icon, useClusterer, enableWindowZoom, defaultHideMarker) { -function addAllMarkers(markers, useClusterer, enableWindowZoom, defaultHideMarker) { +function addAllMarkers(map, markers, useClusterer, enableWindowZoom, defaultHideMarker) { for (var i = 0; i < markers.length; i++) { var marker = markers[i]; - createMarker(marker.latitude, marker.longitude, marker.html, marker.category, marker.icon, - useClusterer, enableWindowZoom, defaultHideMarker - ); + createMarker(map,marker.latitude, marker.longitude, marker.html, marker.category, marker.icon, + useClusterer, enableWindowZoom, defaultHideMarker); } } @@ -105,4 +107,92 @@ function addKmlFiles(kmlFiles) { map: map }); } +} + +function registerMap(googleMapID, centreCoordinates, minLat, minLng, maxLat, maxLng, mapType, markers, lines, kmlFiles, jsonMapStyles, enableAutomaticCenterZoom, useClusterer) { + var newMap = new Array(); + newMap['googleMapID'] = googleMapID; + newMap['centreCoordinates'] = centreCoordinates; + newMap['minLat'] = minLat; + newMap['minLng'] = minLng; + newMap['maxLng'] = maxLng; + newMap['markers'] = markers; + newMap['googleMapID'] = googleMapID; + newMap['mapType'] = mapType; + newMap['lines'] = lines; + newMap['kmlFiles'] = kmlFiles; + newMap['jsonMapStyles'] = jsonMapStyles; + newMap['enableAutomaticCenterZoom'] = enableAutomaticCenterZoom; + newMap['useClusterer'] = useClusterer; + mappableMaps[googleMapID] = newMap; + + // initialise gmarkers array for this map + gmarkers[googleMapID] = []; +} + + + +function loadedGoogleMapsAPI() { + console.log("google maps api callback"); + console.log(mappableMaps); + + for (var i = 1; i <= Object.keys(mappableMaps).length; i++) { + console.log("MAP LOOP " + i); + var map_info = mappableMaps['google_map_' + i]; + var map = new google.maps.Map(document.getElementById(map_info.googleMapID)); + console.log(map_info.googleMapID); + console.log(map_info.centreCoordinates); + + if (map_info.useClusterer) { + console.log("Using clusterer"); + fluster = new Fluster2(map); + } + // <% end_if %> + geocoder = new google.maps.Geocoder(); + + if (map_info.jsonMapStyles) { + console.log('JSON MAP STYLES'); + //map.setOptions({styles: map_info.jsonMapStyles}); + }; + + + if (map_info.enableAutomaticCenterZoom) { + map.setCenter(new google.maps.LatLng($LatLngCentre)); + console.log("T1 SET MAP CENTRE TO " + $LatLngCentre); + var bds = new google.maps.LatLngBounds(new google.maps.LatLng($MinLat, $MinLng), + new google.maps.LatLng($MaxLat, $MaxLng)); + console.log("BOUNDS"); + console.log(bds); + map.fitBounds(bds); + // map.setZoom(); + } else { + var centre = map_info.centreCoordinates; + map.setCenter(new google.maps.LatLng(centre.lat,centre.lng)); + map.setZoom(2); //map_info.zoom); +//map.setCenter(new google.maps.LatLng(51.0453246, -114.0581012)); + + console.log("T2 SET MAP CENTRE TO " + centre.lat+','+centre.lng); + // console.log("T2 SET MAP ZOOM TO $Zoom"); + } + + if (map_info.mapType) { + console.log("MAP TYPE: T1 "); + map.setMapTypeId(mapType); + } else { + console.log("MAP TYPE: T2 "); + + map.setMapTypeId(google.maps.MapTypeId.SATELLITE); + } + + console.log("MARKERS:"); + console.log(map_info.markers); + + //addAllMarkers($MapMarkers,$UseClusterer,$EnableAutomaticCenterZoom, $DefaultHideMarker); + addAllMarkers(map, map_info.markers, map_info.useClusterer, map_info.enableAutomaticCenterZoom, map_info.defaultHideMarker); + //addLines($Lines); + //addKmlFiles(map_info.kmlFiles); + if (map_info.useClusterer) {fluster.initialize()}; + + + }; } \ No newline at end of file From 38326d69d262c3af63e82286b90a78eb78b5bf8f Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 27 Mar 2013 23:32:47 +0700 Subject: [PATCH 068/354] FIX: Zoom now works --- javascript/google/maputil.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index 4977ec1..1718992 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -109,9 +109,10 @@ function addKmlFiles(kmlFiles) { } } -function registerMap(googleMapID, centreCoordinates, minLat, minLng, maxLat, maxLng, mapType, markers, lines, kmlFiles, jsonMapStyles, enableAutomaticCenterZoom, useClusterer) { +function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLat, maxLng, mapType, markers, lines, kmlFiles, jsonMapStyles, enableAutomaticCenterZoom, useClusterer) { var newMap = new Array(); newMap['googleMapID'] = googleMapID; + newMap['zoom'] = zoom; newMap['centreCoordinates'] = centreCoordinates; newMap['minLat'] = minLat; newMap['minLng'] = minLng; @@ -157,8 +158,8 @@ function loadedGoogleMapsAPI() { if (map_info.enableAutomaticCenterZoom) { - map.setCenter(new google.maps.LatLng($LatLngCentre)); - console.log("T1 SET MAP CENTRE TO " + $LatLngCentre); + map.setCenter(new google.maps.LatLng(map_info.centreCoordinates)); + console.log("T1 SET MAP CENTRE TO " + map_info.centreCoordinates); var bds = new google.maps.LatLngBounds(new google.maps.LatLng($MinLat, $MinLng), new google.maps.LatLng($MaxLat, $MaxLng)); console.log("BOUNDS"); @@ -168,7 +169,7 @@ function loadedGoogleMapsAPI() { } else { var centre = map_info.centreCoordinates; map.setCenter(new google.maps.LatLng(centre.lat,centre.lng)); - map.setZoom(2); //map_info.zoom); + map.setZoom(map_info.zoom); //map.setCenter(new google.maps.LatLng(51.0453246, -114.0581012)); console.log("T2 SET MAP CENTRE TO " + centre.lat+','+centre.lng); From b38fe43961a7676861d22c9d07d621db9b554107 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 27 Mar 2013 23:33:04 +0700 Subject: [PATCH 069/354] FIX: Pass zoom parameter into register map method --- javascript/google/MapGoogle.ss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogle.ss index 0cb72d1..6582811 100644 --- a/javascript/google/MapGoogle.ss +++ b/javascript/google/MapGoogle.ss @@ -53,7 +53,7 @@ var mappableMaps = []; // mapping of google_map_N to an array of markers var gmarkers = []; - registerMap('$GoogleMapID', $LatLngCentre, $MinLat,$MinLng,$MaxLat,$MaxLng, $MapType, $MapMarkers, $Lines,$KmlFiles, $JsonMapStyles, $EnableAutomaticCenterZoom, $UseClusterer); + registerMap('$GoogleMapID', $LatLngCentre, $Zoom, $MinLat,$MinLng,$MaxLat,$MaxLng, $MapType, $MapMarkers, $Lines,$KmlFiles, $JsonMapStyles, $EnableAutomaticCenterZoom, $UseClusterer);
style="width:{$Width}px;{$Height}px;"<% end_if %><% if AdditionalCssClasses %>class="$AdditionalCssClasses"<% end_if %> From e17aaeb6a0c731d7a7417a5d6ab5f362b5609fab Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 27 Mar 2013 23:37:14 +0700 Subject: [PATCH 070/354] FIX: Info windows now an array keyed by google map div id --- javascript/google/MapGoogle.ss | 1 + 1 file changed, 1 insertion(+) diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogle.ss index 6582811..a4b3628 100644 --- a/javascript/google/MapGoogle.ss +++ b/javascript/google/MapGoogle.ss @@ -51,6 +51,7 @@ var mappableMaps = []; */ // mapping of google_map_N to an array of markers +var infoWindows = []; var gmarkers = []; registerMap('$GoogleMapID', $LatLngCentre, $Zoom, $MinLat,$MinLng,$MaxLat,$MaxLng, $MapType, $MapMarkers, $Lines,$KmlFiles, $JsonMapStyles, $EnableAutomaticCenterZoom, $UseClusterer); From e8997b17b2f2f1b3c1228547c35973277d406353 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 27 Mar 2013 23:37:51 +0700 Subject: [PATCH 071/354] FIX: Info window now working in the singular map case again --- javascript/google/maputil.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index 1718992..5274ac8 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -21,8 +21,9 @@ function createMarker(map,lat, lng, html, category, icon, useClusterer, enableWi google.maps.event.addListener(marker, "click", function() { if (enableWindowZoom) { - map.setCenter(new google.maps.LatLng(lat, lng), $InfoWindowZoom); + map.setCenter(new google.maps.LatLng(lat, lng), 12); // $InfoWindowZoom); } + var infoWindow = infoWindows[mapId]; infoWindow.setContent(html); infoWindow.open(map, this); }); @@ -129,6 +130,10 @@ function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLa // initialise gmarkers array for this map gmarkers[googleMapID] = []; + + var infoWindow = new google.maps.InfoWindow({ content: 'test', maxWidth: 400 }); + infoWindows[googleMapID] = infoWindow; + } From 4c044e672300d86df3f8b4d1324bd54d0857e2f4 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 28 Mar 2013 13:24:58 +0700 Subject: [PATCH 072/354] FIX: Ensure common variables are only created once --- javascript/google/MapGoogle.ss | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogle.ss index a4b3628..8366f1e 100644 --- a/javascript/google/MapGoogle.ss +++ b/javascript/google/MapGoogle.ss @@ -51,9 +51,12 @@ var mappableMaps = []; */ // mapping of google_map_N to an array of markers -var infoWindows = []; -var gmarkers = []; +<% if DownloadJS %> + var infoWindows = []; + var gmarkers = []; + var mapLayers = []; +<% end_if %> registerMap('$GoogleMapID', $LatLngCentre, $Zoom, $MinLat,$MinLng,$MaxLat,$MaxLng, $MapType, $MapMarkers, $Lines,$KmlFiles, $JsonMapStyles, $EnableAutomaticCenterZoom, $UseClusterer); From 02350639ec6a2d7ac22205d473bc1172fa66dcf6 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 28 Mar 2013 13:25:25 +0700 Subject: [PATCH 073/354] FIX: Render KML layers from basic map method --- code/MapExtension.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/code/MapExtension.php b/code/MapExtension.php index 0adb7b6..276dcc3 100644 --- a/code/MapExtension.php +++ b/code/MapExtension.php @@ -67,9 +67,10 @@ public function BasicMap() { $map->setZoom( $this->owner->ZoomLevel ); $map->setAdditionalCSSClasses( 'fullWidthMap' ); $map->setShowInlineMapDivStyle( true ); - //$map->addMarkerAsObject($this->owner); - - //$map->addKML('http://assets.tripodtravel.co.nz/cycling/meuang-nont-to-bang-sue-loop.kml'); + foreach($this->owner->MapLayers() as $layer) { + error_log("LINK".$layer->KmlFile()->getAbsoluteURL()); + $map->addKML($layer->KmlFile()->getAbsoluteURL()); + } return $map; } From e4445e0f66d36d778c66f4b4b7081334caf7358c Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 28 Mar 2013 13:25:51 +0700 Subject: [PATCH 074/354] FIX: Render KML files for one or more maps --- javascript/google/maputil.js | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index 5274ac8..49fcad9 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -28,6 +28,8 @@ function createMarker(map,lat, lng, html, category, icon, useClusterer, enableWi infoWindow.open(map, this); }); + console.log("PUSHING MARKER TO "+mapId); + console.log(gmarkers); gmarkers[mapId].push(marker); if (defaultHideMarker) { @@ -100,13 +102,17 @@ function addLines(lines) { } -function addKmlFiles(kmlFiles) { +function addKmlFiles(map, kmlFiles) { + console.log("Adding KML files"); for (var i = 0; i < kmlFiles.length; i++) { var kmlFile = kmlFiles[i]; + console.log("KML FILE:"+kmlFiles); var kmlLayer = new google.maps.KmlLayer(kmlFile, { suppressInfoWindows: true, map: map }); + + console.log(kmlLayer); } } @@ -131,16 +137,23 @@ function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLa // initialise gmarkers array for this map gmarkers[googleMapID] = []; + gmarkers['wibble'] = googleMapID; + + console.log("GMARKERS ARRAY CREATED FOR "+googleMapID); + console.log(gmarkers); + var infoWindow = new google.maps.InfoWindow({ content: 'test', maxWidth: 400 }); infoWindows[googleMapID] = infoWindow; + mapLayers[googleMapID] = kmlFiles; + } function loadedGoogleMapsAPI() { - console.log("google maps api callback"); - console.log(mappableMaps); + console.log("google maps api callback - GMARKERS:"); + console.log(gmarkers); for (var i = 1; i <= Object.keys(mappableMaps).length; i++) { console.log("MAP LOOP " + i); @@ -185,9 +198,7 @@ function loadedGoogleMapsAPI() { console.log("MAP TYPE: T1 "); map.setMapTypeId(mapType); } else { - console.log("MAP TYPE: T2 "); - - map.setMapTypeId(google.maps.MapTypeId.SATELLITE); + map.setMapTypeId(google.maps.MapTypeId.ROADMAP); } console.log("MARKERS:"); @@ -196,7 +207,7 @@ function loadedGoogleMapsAPI() { //addAllMarkers($MapMarkers,$UseClusterer,$EnableAutomaticCenterZoom, $DefaultHideMarker); addAllMarkers(map, map_info.markers, map_info.useClusterer, map_info.enableAutomaticCenterZoom, map_info.defaultHideMarker); //addLines($Lines); - //addKmlFiles(map_info.kmlFiles); + addKmlFiles(map,map_info.kmlFiles); if (map_info.useClusterer) {fluster.initialize()}; From ad126f2e7d9bc7a5c7170d0d030164e8bd69389d Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 29 Mar 2013 23:59:46 +0700 Subject: [PATCH 075/354] FIX: Update error message to provide correct information regarding the new map extension --- code/MapAPI.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index 79b63ce..0118dac 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -516,6 +516,7 @@ public function geocoding( $address ) { */ public function addMarkerByCoords( $lat, $lng, $html='', $category='', $icon='' ) { + // Save the lat/lon to enable the automatic center/zoom $this->maxLng = (float) max( (float)$lng, $this->maxLng ); $this->minLng = (float) min( (float)$lng, $this->minLng ); @@ -585,7 +586,7 @@ public function addMarkerAsObject( ViewableData $obj ) { $this->addMarkerByCoords( $obj->getMappableLatitude(), $obj->getMappableLongitude(), $obj->getMapContent(), $cat, $obj->getMapPin() ); //} } else { - error_log("Unable to add object ".$obj." of ID ".$obj->ID." to map as it does not implement mappable"); + error_log("Unable to add object ".$obj." of ID ".$obj->ID." to map as it does not implement mappable or use MapExtension"); } } @@ -656,6 +657,10 @@ public function addKML( $url ) { } + /* + Add a line to the map + + */ public function addLine( $from = array(), $to = array(), $color = "#FF3300" ) { $line = array( 'lat1' => $from[0], From 5e978ba42a652ae5f6c7979bb774e1a7d45b9f92 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 30 Mar 2013 00:00:07 +0700 Subject: [PATCH 076/354] FIX: Remove debug, add lines --- javascript/google/MapGoogle.ss | 71 +--------------------------------- 1 file changed, 2 insertions(+), 69 deletions(-) diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogle.ss index 8366f1e..d585741 100644 --- a/javascript/google/MapGoogle.ss +++ b/javascript/google/MapGoogle.ss @@ -1,10 +1,8 @@ <% if DownloadJS %> -**** DOWNLOAD JS **** - <% if DelayLoadMapFunction %> console.log('delay map loading'); <% else %> -T1 + @@ -64,69 +63,3 @@ var mappableMaps = []; >
- - - \ No newline at end of file From 90ca13fdfe916f3c86c36599c53a5772c7d869d1 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 30 Mar 2013 00:00:55 +0700 Subject: [PATCH 077/354] FIX: Datasets, lines, clustering now working for singular map again, using the generic structure where multiple maps are possible --- javascript/google/maputil.js | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index 49fcad9..6a79f50 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -82,7 +82,7 @@ function addAllMarkers(map, markers, useClusterer, enableWindowZoom, defaultHide } -function addLines(lines) { +function addLines(map, lines) { for (i = 0; i < lines.length; i++) { var line = lines[i]; console.log("LINE:"); @@ -98,8 +98,6 @@ function addLines(lines) { }); pl.setMap(map); } - - } function addKmlFiles(map, kmlFiles) { @@ -124,6 +122,7 @@ function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLa newMap['minLat'] = minLat; newMap['minLng'] = minLng; newMap['maxLng'] = maxLng; + newMap['maxLat'] = maxLat; newMap['markers'] = markers; newMap['googleMapID'] = googleMapID; newMap['mapType'] = mapType; @@ -146,6 +145,7 @@ function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLa infoWindows[googleMapID] = infoWindow; mapLayers[googleMapID] = kmlFiles; + mapLines[googleMapID] = lines; } @@ -176,14 +176,21 @@ function loadedGoogleMapsAPI() { if (map_info.enableAutomaticCenterZoom) { - map.setCenter(new google.maps.LatLng(map_info.centreCoordinates)); - console.log("T1 SET MAP CENTRE TO " + map_info.centreCoordinates); - var bds = new google.maps.LatLngBounds(new google.maps.LatLng($MinLat, $MinLng), - new google.maps.LatLng($MaxLat, $MaxLng)); + centre = map_info.centreCoordinates; + map.setCenter(new google.maps.LatLng(centre.lat,centre.lng)); + console.log("T1 SET MAP CENTRE TO " + centre.lat+','+centre.lng); + + console.log(map_info.minLat+','+map_info.minLng); + console.log(map_info.maxLat+','+map_info.maxLng); + + + var bds = new google.maps.LatLngBounds(new google.maps.LatLng(map_info.minLat, map_info.minLng), + new google.maps.LatLng(map_info.maxLat, map_info.maxLng)); console.log("BOUNDS"); console.log(bds); map.fitBounds(bds); - // map.setZoom(); + + map.setZoom(map_info.zoom); } else { var centre = map_info.centreCoordinates; map.setCenter(new google.maps.LatLng(centre.lat,centre.lng)); @@ -195,8 +202,8 @@ function loadedGoogleMapsAPI() { } if (map_info.mapType) { - console.log("MAP TYPE: T1 "); - map.setMapTypeId(mapType); + console.log("MAP TYPE: T1 "+map_info.mapType); + map.setMapTypeId(map_info.mapType); } else { map.setMapTypeId(google.maps.MapTypeId.ROADMAP); } @@ -204,11 +211,12 @@ function loadedGoogleMapsAPI() { console.log("MARKERS:"); console.log(map_info.markers); - //addAllMarkers($MapMarkers,$UseClusterer,$EnableAutomaticCenterZoom, $DefaultHideMarker); addAllMarkers(map, map_info.markers, map_info.useClusterer, map_info.enableAutomaticCenterZoom, map_info.defaultHideMarker); - //addLines($Lines); + addLines(map, map_info.lines); addKmlFiles(map,map_info.kmlFiles); - if (map_info.useClusterer) {fluster.initialize()}; + if (map_info.useClusterer) { + fluster.initialize() + }; }; From 7f8ff77d7914e05a7e1c61101c7fe1ecdcc1a19a Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 30 Mar 2013 00:37:40 +0700 Subject: [PATCH 078/354] API: Changed RenderMap to getRenderableMap as it seems more appropriate --- code/MappableData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/MappableData.php b/code/MappableData.php index f87f029..f1a1046 100644 --- a/code/MappableData.php +++ b/code/MappableData.php @@ -8,7 +8,7 @@ */ class MappableData extends Extension { - public function RenderMap($width = null, $height = null, $zoom = 9) { + public function getRenderableMap($width = null, $height = null, $zoom = 9) { error_log("MAP ALREADY RENDERED? ".MapUtil::get_map_already_rendered()); $gmap = MapUtil::get_map(new ArrayList(array($this->owner))); $w = $width ? $width : MapUtil::$map_width; From 3d0e702927f0fb36953eb49a82ec06fb93c385cd Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 30 Mar 2013 00:37:51 +0700 Subject: [PATCH 079/354] API: Changed RenderMap to getRenderableMap as it seems more appropriate --- code/MappableDataObjectSet.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/MappableDataObjectSet.php b/code/MappableDataObjectSet.php index 3ac23bc..3c794db 100755 --- a/code/MappableDataObjectSet.php +++ b/code/MappableDataObjectSet.php @@ -8,7 +8,7 @@ */ class MappableDataObjectSet extends Extension { - public function RenderMap($width = null, $height = null) { + public function getRenderableMap($width = null, $height = null) { $gmap = MapUtil::get_map($this->owner); $w = $width ? $width : MapUtil::$map_width; $h = $height ? $height : MapUtil::$map_height; From 51d426d7f781514224c62748d078937b9b1cc8fa Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 30 Mar 2013 00:38:12 +0700 Subject: [PATCH 080/354] API: RenderMap call now getRenderableMap --- code/MapExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/MapExtension.php b/code/MapExtension.php index 276dcc3..b3fd89c 100644 --- a/code/MapExtension.php +++ b/code/MapExtension.php @@ -62,7 +62,7 @@ public function HasGeo() { Render a map at the provided lat,lon, zoom from the editing functions, */ public function BasicMap() { - $map = $this->owner->RenderMap(); + $map = $this->owner->getRenderableMap(); // $map->setDelayLoadMapFunction( true ); $map->setZoom( $this->owner->ZoomLevel ); $map->setAdditionalCSSClasses( 'fullWidthMap' ); From 5dcaeed7b63d103b681c72a08bebd6fb5faf00ee Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 30 Mar 2013 00:41:13 +0700 Subject: [PATCH 081/354] FIX: Remove temporary stack trace library --- javascript/google/MapGoogle.ss | 1 - 1 file changed, 1 deletion(-) diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogle.ss index d585741..b730a71 100644 --- a/javascript/google/MapGoogle.ss +++ b/javascript/google/MapGoogle.ss @@ -14,7 +14,6 @@ google.maps.event.addDomListener(window, 'load', loadedGoogleMapsAPI); <% end_if %> - -
style="width:{$Width}px;{$Height}px;"<% end_if %><% if AdditionalCssClasses %>class="$AdditionalCssClasses"<% end_if %> +
style="width:{$Width}px; height: {$Height}px;"<% end_if %><% if AdditionalCssClasses %>class="$AdditionalCssClasses"<% end_if %> >
From fea63a2570de4ad74d162499c8bd05271f2d0757 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 11 Nov 2013 22:44:56 +0700 Subject: [PATCH 097/354] WIP: Map marker sets, work in progress --- code/MapMarkerSetsExtension.php | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 code/MapMarkerSetsExtension.php diff --git a/code/MapMarkerSetsExtension.php b/code/MapMarkerSetsExtension.php new file mode 100644 index 0000000..742abac --- /dev/null +++ b/code/MapMarkerSetsExtension.php @@ -0,0 +1,29 @@ + 'MapMarkerSet' + ); + + + static $belongs_many_many_extraFields = array( + 'MapMarkerSets' => array( + 'SortOrder' => "Int" + ) + ); + + + public function updateCMSFields( FieldList $fields ) { + + + $gridConfig2 = GridFieldConfig_RelationEditor::create(); + $gridConfig2->getComponentByType( 'GridFieldAddExistingAutocompleter' )->setSearchFields( array( 'Title' ) ); + $gridConfig2->getComponentByType( 'GridFieldPaginator' )->setItemsPerPage( 100 ); + + $gridField2 = new GridField( "MapMarkerSets", "MapMarkers", $this->owner->MapMarkerSets(), $gridConfig2 ); + $fields->addFieldToTab( "Root.MapMarkerSets", $gridField2 ); + + } + +} \ No newline at end of file From efd198a555830901b1aee1eaa98f842ecce7abd2 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 11 Nov 2013 23:20:15 +0700 Subject: [PATCH 098/354] ENHANCEMENT: Added composer --- composer.json | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index a6f2fa1..f0b09a4 100644 --- a/composer.json +++ b/composer.json @@ -1,16 +1,21 @@ { "name": "weboftalent/mappable", - "description": "An upgraded version of Uncle Cheese's Mappable module compatible with Silverstripe 3", + "description": "Fork of Uncle Cheese's original Mappable module. Inline JavaScript is minimal, and templated such that other mapping systems can be added later. A map editing interface is also included, addable by a one line configuration call.", "type": "silverstripe-module", - "keywords": ["silverstripe", "googlemaps","weboftalent"], + "keywords": ["silverstripe", "google maps", "maps", "mappable"], "authors": [ - { - "name": "Gordon Anderson", - "email": "gordon@weboftalent.asia" - } + { + "name": "Gordon Anderson", + "email": "gordon.b.anderson@gmail.com", + "homepage": "https://github.com/gordonbanderson/Mappable", + "role": "Developer" + } ], + "support": { + "issues": "https://github.com/gordonbanderson/Mappable/issues" + }, "require": { - "silverstripe/framework": "3.*" + "silverstripe/framework": "~3.1" } -} \ No newline at end of file +} From caa8a0fa435f0764ee3c1963750e0b6da3d6a4ff Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 12 Nov 2013 11:50:53 +0700 Subject: [PATCH 099/354] Update README.md --- README.md | 236 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 235 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2097587..61060be 100644 --- a/README.md +++ b/README.md @@ -1 +1,235 @@ -I will update this later at my earliest convenience, but meanwhile see http://demo.weboftalent.asia/mappable/ for worked examples of the module. I will transfer this into 'README' form +#Adding a Map to a DataObject + +Using the standard method of adding extensions in SilverStripe 3.1, add an extension called 'MapExtension' to relevant DataObjects. + +``` +--- +name: weboftalent-example-map-extensions +--- +PageWithMap: + extensions: + ['MapExtension'] + +``` + +This adds Latitude, Longitude and Zoom fields to the DataObject in question, here 'PageWithMap'. In addition, the admin interface for PageWithMap now has a location tab. Location can be changed in 3 ways +* Use the geocoder and search for a place name +* Drag the map pin +* Right click +The zoom level set by the content editor is also saved. +To render a map in the template, simply called $BasicMap + +``` +

$Title

+$Content + +$BasicMap +``` + +For an example of this, see http://demo.weboftalent.asia/mappable/quick-map-adding-a-map-to-a-dataobject/ + + +#Multiple Maps on the Same Page +Multiple maps can be added to a page. One option is to associate maps with a DataObject whose relationship to the parent page is 'has many', as in this example of contact page addresses. +##Example +Often a company will have more than one office location that they wish to display, this is a an example of that use case. It would probably need expanding in order to show the likes of email address and telephone number, left as an exercise for the reader. + +Firstly, create a parent container page called ContactPage, this has many locations of type ContactPageAddress. +``` + 'ContactPageAddress' + ); + + function getCMSFields() { + $fields = parent::getCMSFields(); + + $gridConfig = GridFieldConfig_RelationEditor::create(); + $gridConfig->getComponentByType( 'GridFieldAddExistingAutocompleter' )->setSearchFields( array( 'PostalAddress' ) ); + $gridConfig->getComponentByType( 'GridFieldPaginator' )->setItemsPerPage( 100 ); + $gridField = new GridField( "Locations", "List of Addresses:", $this->Locations(), $gridConfig ); + $fields->addFieldToTab( "Root.Addresses", $gridField ); + + return $fields; + } +} + +class ContactPage_Controller extends Page_Controller { + + +} + +?> +``` + +The latter contains the actual map for each location, configured as above using extensions.yml + +``` + 'Text' + ); + + static $has_one = array( 'ContactPage' => 'ContactPage' ); + + + public static $summary_fields = array( + 'PostalAddress' => 'PostalAddress' + ); + + + function getCMSFields() { + $fields = new FieldList(); + $fields->push( new TabSet( "Root", $mainTab = new Tab( "Main" ) ) ); + $mainTab->setTitle( _t( 'SiteTree.TABMAIN', "Main" ) ); + $fields->addFieldToTab( "Root.Main", new TextField( 'PostalAddress' ) ); + + $this->extend( 'updateCMSFields', $fields ); + + return $fields; + } +} +?> +``` + +The template simply loops through the contact page addresses, rendering a map. + +``` +

$Title

+$BriefDescription + +

Addresses

+ +<% loop Locations %> +

$PostalAddress

+$BasicMap +<% end_loop %> + +$Content +``` + +See http://demo.weboftalent.asia/mappable/multiple-maps-on-the-same-page/ for a working demo. + + +#Map Layers +KML layers can be added through the CMS using only a line of configuration. + +``` + +``` + +Add this to extensions.yml + +``` +PageWithMapAndLayers: + extensions: + ['MapExtension', 'MapLayersExtension'] +``` + +Do a /dev/build to update your database with the map layers relationship. + +When you add a new page of type PageWithMapAndLayers, there is now an extra tab called 'Map Layers'. Each layer consists of a human readable name and a file attachment, which in this case have to be KML files. +Templating is the same as before, the $BasicMap method takes account of layers when rendering a map. + +##Gotchas +Note that by default, one cannot upload KML files to the assets area, as .gpx and .kml files are blocked by default. Add them to /assets/.htacces. I've create a pull request to fix this, https://github.com/silverstripe/silverstripe-installer/pull/56 + +Also note you will not be able to see map layers in your dev environment, as the KML file URL needs to be publicly visible. + +#Adding Lines to Maps +A line can be added to a map with the following API call: + +``` + $map->addLine( $point1, $point2, $colorHexCode ); +``` + +Each point is an array whose 0th element is the latitude and 1st element is the longitude. The third parameter is optional and represents the color of the line in standard CSS hex code colors (RGB). + +An example method to draw a multicolored triangle on a map is as follows: + +``` +/* + Render a triangle around the provided lat,lon, zoom from the editing functions, + */ + public function MapWithLines() { + $map = $this->owner->getRenderableMap(); + $map->setZoom( $this->ZoomLevel ); + $map->setAdditionalCSSClasses( 'fullWidthMap' ); + $map->setShowInlineMapDivStyle( true ); + + $scale = 0.3; + + // draw a triangle + $point1 = array( + $this->Lat - 0.5*$scale, $this->Lon + ); + $point2 = array( + $this->Lat + 0.5*$scale, $this->Lon-0.7*$scale + ); + + $point3 = array( + $this->Lat + 0.5*$scale, $this->Lon+0.7*$scale + ); + + $map->addLine( $point1, $point2 ); + $map->addLine( $point2, $point3, '#000077' ); + $map->addLine( $point3, $point1, '#007700' ); + + return $map; + } +``` + + + + Instead of calling $BasicMap call $MapWithLines instead from the template. + + See http://demo.weboftalent.asia/mappable/map-with-lines/ for a working demo. + + +#Mapping a DataList + +The principle difference from a simple map is that the renderable map is obtained from the DataList itself. The objects in the DataList must implement the mappable interface, or use the extension called MapExtension. This requirement means that all the objects will have the necessary information to be rendered as a marker on a map. + +The following example is the code that renders the map on this page. Note that the clusterer on this page is only invoked if the checkbox 'Cluster Example Dataset' is set to true, in this case it is not. + +``` +public function MapWithDataList() { + $flickrPhotos = DataList::create( 'FlickrPhoto' )->where( 'Lat != 0 AND Lon !=0' ); + if ( $flickrPhotos->count() == 0 ) { + return ''; // don't render a map + } + + $map = $flickrPhotos->getRenderableMap(); + $map->setZoom( $this->ZoomLevel ); + $map->setAdditionalCSSClasses( 'fullWidthMap' ); + $map->setShowInlineMapDivStyle( true ); + if ( $this->ClusterExampleDataset ) { + $map->setClusterer( true ); + } + + return $map; + } + ``` + +The map is positioned so that it shows all of the points automatically. Also note that the host page of the map does not require to implement mappable or even have a location attached to it, as the map is rendered entirely from the DataList, in this case $flickrPhotos. + +For clustered and unclustered examples, see http://demo.weboftalent.asia/mappable/map-from-a-datalist-unclustered/ and http://demo.weboftalent.asia/mappable/map-with-datalist-clustered-markers/ respectively. + + +#TODO +* Render different markers or icons as map pointers +* Add map marker sets, where a set of markers e.g. of underground stations can be added to multiple maps on your website. From f01b9ab66beb5be975ddf358fbdc96db38911a86 Mon Sep 17 00:00:00 2001 From: Werner Krauss Date: Tue, 3 Jun 2014 17:36:45 +0200 Subject: [PATCH 100/354] get_map now accepts more generic SS_List instead of DataList This enables us to decorate ArrayList with Mappabl --- _config/_config.yml | 4 ++++ code/MapUtil.php | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/_config/_config.yml b/_config/_config.yml index 322bde2..16ab90d 100644 --- a/_config/_config.yml +++ b/_config/_config.yml @@ -11,6 +11,10 @@ DataList: extensions: - MappableDataObjectSet +ArrayList: + extensions: + - MappableDataObjectSet + # allow geographical format files to be uploaded File: diff --git a/code/MapUtil.php b/code/MapUtil.php index 0397de0..8166810 100644 --- a/code/MapUtil.php +++ b/code/MapUtil.php @@ -209,12 +209,12 @@ public static function sanitize($content) { /** * Creates a new {@link GoogleMapsAPI} object loaded with the default settings - * and places all of the items in a {@link DataObjectSet} on the map + * and places all of the items in a {@link SS_List}, e.g. {@link DataList} or {@link ArrayList} on the map * - * @param DataObjectSet $set - * @return GoogleMapsAPI + * @param SS_List $set + * @return MapAPI */ - public static function get_map(DataList $list) { + public static function get_map(SS_List $list) { $gmap = self::instance(); if($list) { $arr = $list->toArray(); From adb221e88406653c58c9930e9688775c17a2fa86 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 18 Jun 2014 15:15:27 +0700 Subject: [PATCH 101/354] FIX: Width now not in px, can be either px or % to allow for full screen responsive width --- javascript/google/MapGoogle.ss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogle.ss index 486d851..a759d4d 100644 --- a/javascript/google/MapGoogle.ss +++ b/javascript/google/MapGoogle.ss @@ -57,7 +57,7 @@ var mappableMaps = []; registerMap('$GoogleMapID', $LatLngCentre, $Zoom, $MinLat,$MinLng,$MaxLat,$MaxLng, $MapType, $MapMarkers, $Lines,$KmlFiles, $JsonMapStyles, $EnableAutomaticCenterZoom, $UseClusterer); -
style="width:{$Width}px; height: {$Height}px;"<% end_if %><% if AdditionalCssClasses %>class="$AdditionalCssClasses"<% end_if %> +
style="width:{$Width}; height: {$Height};"<% end_if %><% if AdditionalCssClasses %>class="$AdditionalCssClasses"<% end_if %> >
From 4c97476c3d33106a3094a7cd2754e06a061601c2 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 18 Jun 2014 15:15:45 +0700 Subject: [PATCH 102/354] FIX: Width now 100% for default --- code/MapUtil.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/MapUtil.php b/code/MapUtil.php index 0397de0..53698de 100644 --- a/code/MapUtil.php +++ b/code/MapUtil.php @@ -19,13 +19,13 @@ class MapUtil /** * @var int The default width of a Google Map */ - public static $map_width = 400; + public static $map_width = '100%'; /** * @var int The default height of a Google Map */ - public static $map_height = 400; + public static $map_height = '400px'; /** @var int Icon width of the gmarker **/ public static $iconWidth = 24; From d8ae85c0f89e35443d67a7075712764dab62ab45 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 18 Jun 2014 15:16:10 +0700 Subject: [PATCH 103/354] FIX: Ensure mappable extnsions are taken into account --- code/MapAPI.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index 4f5183a..05d4ab1 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -260,8 +260,6 @@ public function setClusterer( $useClusterer, $gridSize=100, $maxZoom=9, $cluster $this->gridSize = $gridSize; $this->maxZoom = $maxZoom; $this->clustererLibraryPath = $clustererLibraryPath; - - error_log('T1 Set cluster pathj to '.$clustererLibraryPath); } /** @@ -580,13 +578,21 @@ public function addArrayMarkerByCoords( $coordtab, $category='', $icon='' ) { * @param ViewableData $obj */ public function addMarkerAsObject( ViewableData $obj ) { - if ( ($obj instanceof Mappable) || (Object::has_extension($obj->ClassName, 'MapExtension')) ) { + $extensionsImplementMappable = false; + $extensions = Object::get_extensions(get_class($obj)); + + foreach ($extensions as $extension) { + $class = new ReflectionClass($extension); + if ($class->implementsInterface('Mappable')) { + $extensionsImplementMappable = true; + } + + } + if ( $extensionsImplementMappable || ($obj instanceof Mappable) || (Object::has_extension($obj->ClassName, 'MapExtension')) ) { //if(($obj->getMappableLatitude() > 0) || ($obj->getMappableLongitude() > 0)) { $cat = $obj->hasMethod( 'getMappableMapCategory' ) ? $obj->getMappableMapCategory() : "default"; $this->addMarkerByCoords( $obj->getMappableLatitude(), $obj->getMappableLongitude(), $obj->getMappableMapContent(), $cat, $obj->getMappableMapPin() ); //} - } else { - error_log("Unable to add object ".$obj." of ID ".$obj->ID." to map as it does not implement mappable or use MapExtension"); } } From d16a551b672661211f7afbc86030329b1816da01 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 26 Sep 2014 14:55:19 +0700 Subject: [PATCH 104/354] FIX: Ensure Thai text is not garbled. Note this will not work on PHP5.4 --- code/MapAPI.php | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index 05d4ab1..ad93b30 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -52,7 +52,7 @@ class MapAPI extends ViewableData * * @var int Infowindow width of the gmarker * */ - protected $infoWindowWidth = 250; + protected $infoWindowWidth = 500; /** Default zoom of the gmap **/ protected $zoom = 9; @@ -689,6 +689,12 @@ public function addLine( $from = array(), $to = array(), $color = "#FF3300" ) { +/* +For php 5.3 +*/ +function jsonRemoveUnicodeSequences($struct) { + return preg_replace("/\\\\u([a-f0-9]{4})/e", "iconv('UCS-4LE','UTF-8',pack('V', hexdec('U$1')))", json_encode($struct)); +} /** @@ -699,10 +705,11 @@ public function addLine( $from = array(), $to = array(), $color = "#FF3300" ) { public function generate() { // from http://stackoverflow.com/questions/3586401/cant-decode-json-string-in-php - $jsonMarkers = stripslashes(json_encode($this->markers)); + $jsonMarkers = stripslashes($this->jsonRemoveUnicodeSequences($this->markers)); + - $linesJson = stripslashes(json_encode($this->lines)); - $kmlJson = stripslashes(json_encode($this->kmlFiles)); + $linesJson = stripslashes($this->jsonRemoveUnicodeSequences($this->lines)); + $kmlJson = stripslashes($this->jsonRemoveUnicodeSequences($this->kmlFiles)); // Center of the GMap $geocodeCentre = ( $this->latLongCenter ) ? $this->latLongCenter : $this->geocoding( $this->center ); @@ -775,9 +782,6 @@ public function generate() { ) ); - - - $this->content = $this->processTemplate('Map', $vars); } From 7a281f2879ffe10b8bab5307973ace003d1c77d7 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 26 Sep 2014 14:55:45 +0700 Subject: [PATCH 105/354] FIX: Removed debug, made info window wider (still need hard cording removed) --- javascript/google/MapGoogle.ss | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogle.ss index a759d4d..b8398a3 100644 --- a/javascript/google/MapGoogle.ss +++ b/javascript/google/MapGoogle.ss @@ -5,11 +5,7 @@ console.log('delay map loading'); - + <% end_if %> @@ -30,22 +26,6 @@ var mappableMaps = []; -
style="width:{$Width}; height: {$Height};"<% end_if %><% if AdditionalCssClasses %>class="$AdditionalCssClasses"<% end_if %> -> +
style="width:{$Width}; height: {$Height};"<% end_if %><% if AdditionalCssClasses %> class="$AdditionalCssClasses"<% end_if %>
From b523589a69f04c665ec4c6b2be83ac2465986d76 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sun, 28 Sep 2014 18:10:58 +0700 Subject: [PATCH 106/354] FIX: Closing > sign was missing from the google map div, breaking layout --- javascript/google/MapGoogle.ss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogle.ss index b8398a3..7760233 100644 --- a/javascript/google/MapGoogle.ss +++ b/javascript/google/MapGoogle.ss @@ -37,6 +37,6 @@ var mappableMaps = []; registerMap('$GoogleMapID', $LatLngCentre, $Zoom, $MinLat,$MinLng,$MaxLat,$MaxLng, $MapType, $MapMarkers, $Lines,$KmlFiles, $JsonMapStyles, $EnableAutomaticCenterZoom, $UseClusterer); -
style="width:{$Width}; height: {$Height};"<% end_if %><% if AdditionalCssClasses %> class="$AdditionalCssClasses"<% end_if %> +
style="width:{$Width}; height: {$Height};"<% end_if %><% if AdditionalCssClasses %> class="$AdditionalCssClasses"<% end_if %>>
From a6365c7cc958d6ea93b392f74c1867b3245dc9b4 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sun, 28 Sep 2014 18:11:46 +0700 Subject: [PATCH 107/354] FIX: Deal with unicode/json conversion different depending on whether or not the version is newer than 5.3 --- code/MapAPI.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index ad93b30..3dbe66f 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -705,11 +705,22 @@ function jsonRemoveUnicodeSequences($struct) { public function generate() { // from http://stackoverflow.com/questions/3586401/cant-decode-json-string-in-php - $jsonMarkers = stripslashes($this->jsonRemoveUnicodeSequences($this->markers)); - + $jsonMarkers = null; + $linesJson = null; + $kmlJson = null; + + // prior to PHP version 5.4, one needs to use regex + if (PHP_VERSION_ID < 50400) { + $jsonMarkers = stripslashes($this->jsonRemoveUnicodeSequences($this->markers)); + $linesJson = stripslashes($this->jsonRemoveUnicodeSequences($this->lines)); + $kmlJson = stripslashes($this->jsonRemoveUnicodeSequences($this->kmlFiles)); + } else { + $jsonMarkers = stripslashes(json_encode($this->markers,JSON_UNESCAPED_UNICODE)); + $linesJson = stripslashes(json_encode($this->lines,JSON_UNESCAPED_UNICODE)); + $kmlJson = stripslashes(json_encode($this->kmlFiles,JSON_UNESCAPED_UNICODE)); + } - $linesJson = stripslashes($this->jsonRemoveUnicodeSequences($this->lines)); - $kmlJson = stripslashes($this->jsonRemoveUnicodeSequences($this->kmlFiles)); + // Center of the GMap $geocodeCentre = ( $this->latLongCenter ) ? $this->latLongCenter : $this->geocoding( $this->center ); From 2a763193accbb8fc5890313daad581e36967acd4 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 4 Nov 2014 17:12:02 +0700 Subject: [PATCH 108/354] FIX: Use protocol agnostic URL so that both http and https function with error warnings or just fail completely --- code/MapAPI.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index 05d4ab1..dda77b7 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -481,7 +481,7 @@ public function getContent( $url ) { public function geocoding( $address ) { $encodeAddress = urlencode( $address ); - $url = "http://maps.google.com/maps/geo?q=".$encodeAddress."&output=csv&key=".$this->googleMapKey; + $url = "//maps.google.com/maps/geo?q=".$encodeAddress."&output=csv&key=".$this->googleMapKey; if ( function_exists( 'curl_init' ) ) { $data = $this->getContent( $url ); From 2af84f55013c64fddfaf3ef1d67b5eca9bc361ce Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 4 Nov 2014 17:12:22 +0700 Subject: [PATCH 109/354] FIX: Use protocol agnostic URL so that both http and https function with error warnings or just fail completely --- code/MappableData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/MappableData.php b/code/MappableData.php index 6e0a19a..d4d3a7c 100644 --- a/code/MappableData.php +++ b/code/MappableData.php @@ -37,7 +37,7 @@ public function StaticMap($width = null, $height = null) { $lat = $this->owner->getMappableLatitude(); $lng = $this->owner->getMappableLongitude(); - $src = htmlentities("http://maps.google.com/maps/api/staticmap?center=$lat,$lng&markers=$lat,$lng&zoom=13&size=${w}x$h&sensor=false"); + $src = htmlentities("//maps.google.com/maps/api/staticmap?center=$lat,$lng&markers=$lat,$lng&zoom=13&size=${w}x$h&sensor=false"); return ''.$this->owner->Title.''; From 95f7ea6071040b2a066517b1ec68296d75e15af9 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 4 Nov 2014 17:12:40 +0700 Subject: [PATCH 110/354] FIX: Use protocol agnostic URL so that both http and https function with error warnings or just fail completely --- javascript/Fluster2.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/Fluster2.js b/javascript/Fluster2.js index 28c457a..fc444b2 100644 --- a/javascript/Fluster2.js +++ b/javascript/Fluster2.js @@ -41,19 +41,19 @@ function Fluster2(_map, _debug) this.currentZoomLevel = -1; this.styles = { 0: { - image: 'http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m1.png', + image: '//gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m1.png', textColor: '#FFFFFF', width: 53, height: 52 }, 10: { - image: 'http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m2.png', + image: '//gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m2.png', textColor: '#FFFFFF', width: 56, height: 55 }, 20: { - image: 'http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m3.png', + image: '//gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m3.png', textColor: '#FFFFFF', width: 66, height: 65 From e83756e60bd0473ed765fcbc47a37fa64e1b4d1d Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 4 Nov 2014 17:12:52 +0700 Subject: [PATCH 111/354] FIX: Use protocol agnostic URL so that both http and https function with error warnings or just fail completely --- javascript/Fluster2.packed.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/Fluster2.packed.js b/javascript/Fluster2.packed.js index 58fc252..7370b4f 100644 --- a/javascript/Fluster2.packed.js +++ b/javascript/Fluster2.packed.js @@ -18,7 +18,7 @@ * License along with this library. If not, see . */ -function Fluster2(_map,_debug){var map=_map;var projection=new Fluster2ProjectionOverlay(map);var me=this;var clusters=new Object();var markersLeft=new Object();this.debugEnabled=_debug;this.gridSize=60;this.markers=new Array();this.currentZoomLevel=-1;this.styles={0:{image:'http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m1.png',textColor:'#FFFFFF',width:53,height:52},10:{image:'http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m2.png',textColor:'#FFFFFF',width:56,height:55},20:{image:'http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m3.png',textColor:'#FFFFFF',width:66,height:65}};var zoomChangedTimeout=null;function createClusters(){var zoom=map.getZoom();if(clusters[zoom]){me.debug('Clusters for zoom level '+zoom+' already initialized.')}else{var clustersThisZoomLevel=new Array();var clusterCount=0;var markerCount=me.markers.length;for(var i=0;i=0;j--){var cluster=clustersThisZoomLevel[j];if(cluster.contains(markerPosition)){cluster.addMarker(marker);done=true;break}}if(!done){var cluster=new Fluster2Cluster(me,marker);clustersThisZoomLevel.push(cluster);clusterCount++}}clusters[zoom]=clustersThisZoomLevel;me.debug('Initialized '+clusters[zoom].length+' clusters for zoom level '+zoom+'.')}if(clusters[me.currentZoomLevel]){for(var i=0;i=0;j--){var cluster=clustersThisZoomLevel[j];if(cluster.contains(markerPosition)){cluster.addMarker(marker);done=true;break}}if(!done){var cluster=new Fluster2Cluster(me,marker);clustersThisZoomLevel.push(cluster);clusterCount++}}clusters[zoom]=clustersThisZoomLevel;me.debug('Initialized '+clusters[zoom].length+' clusters for zoom level '+zoom+'.')}if(clusters[me.currentZoomLevel]){for(var i=0;i1){for(var i=0;ii){this.style=styles[i]}else{break}}google.maps.OverlayView.call(this);this.setMap(this.map);this.draw()};Fluster2ClusterMarker.prototype=new google.maps.OverlayView();Fluster2ClusterMarker.prototype.draw=function(){if(this.div==null){var me=this;this.div=document.createElement('div');this.div.style.position='absolute';this.div.style.width=this.style.width+'px';this.div.style.height=this.style.height+'px';this.div.style.lineHeight=this.style.height+'px';this.div.style.background='transparent url("'+this.style.image+'") 50% 50% no-repeat';this.div.style.color=this.style.textColor;this.div.style.textAlign='center';this.div.style.fontFamily='Arial, Helvetica';this.div.style.fontSize='11px';this.div.style.fontWeight='bold';this.div.innerHTML=this.markerCount;this.div.style.cursor='pointer';google.maps.event.addDomListener(this.div,'click',function(){me.map.fitBounds(me.cluster.getMarkerBounds())});this.getPanes().overlayLayer.appendChild(this.div)}var position=this.getProjection().fromLatLngToDivPixel(this.position);this.div.style.left=(position.x-parseInt(this.style.width/2))+'px';this.div.style.top=(position.y-parseInt(this.style.height/2))+'px'};Fluster2ClusterMarker.prototype.hide=function(){this.div.style.display='none'};Fluster2ClusterMarker.prototype.show=function(){this.div.style.display='block'}; function Fluster2ProjectionOverlay(map){google.maps.OverlayView.call(this);this.setMap(map);this.getP=function(){return this.getProjection()}}Fluster2ProjectionOverlay.prototype=new google.maps.OverlayView();Fluster2ProjectionOverlay.prototype.draw=function(){}; \ No newline at end of file From c7b4884aaab42ebd573ba9d85ce8ee04d8de9bc4 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 4 Nov 2014 17:13:05 +0700 Subject: [PATCH 112/354] FIX: Use protocol agnostic URL so that both http and https function with error warnings or just fail completely --- javascript/google/MapGoogle.ss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogle.ss index a759d4d..7d78490 100644 --- a/javascript/google/MapGoogle.ss +++ b/javascript/google/MapGoogle.ss @@ -2,7 +2,7 @@ <% if DelayLoadMapFunction %> console.log('delay map loading'); <% else %> - + + + +<% end_if %> + + +<% end_if %> +<% if UseClusterer %> + +<% end_if %> \ No newline at end of file diff --git a/templates/Includes/GoogleStreetView.ss b/templates/Includes/GoogleStreetView.ss new file mode 100644 index 0000000..65bdab1 --- /dev/null +++ b/templates/Includes/GoogleStreetView.ss @@ -0,0 +1,23 @@ +<% include GoogleJavaScript %> +
+<% if $Caption %>$Caption<% end_if %> + \ No newline at end of file From 6906444e6d612ff3b01535a5e3ba62a1cab11381 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 4 Mar 2015 21:38:49 +0700 Subject: [PATCH 132/354] ENHANCEMENT: Refactor to extract the downloading of JavaScript as a centralised entity (needed for Google Street View and Google Maps) --- javascript/google/MapGoogle.ss | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogle.ss index b44c11a..f392825 100644 --- a/javascript/google/MapGoogle.ss +++ b/javascript/google/MapGoogle.ss @@ -1,33 +1,7 @@ -<% if DownloadJS %> -<% if DelayLoadMapFunction %> -console.log('delay map loading'); -<% else %> - - - - -<% end_if %> - - - - - - -<% end_if %> -<% if UseClusterer %> - - - - <% end_if %> - - +<% include GoogleJavaScript %> -
style="width:{$Width}; height: {$Height};"<% end_if %><% if AdditionalCssClasses %> class="$AdditionalCssClasses"<% end_if %>> -
- +
\ No newline at end of file From 2913659df77553f574e62346016c0bd304aa101d Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 4 Mar 2015 21:39:15 +0700 Subject: [PATCH 133/354] MINOR: Removed error log and some unecessary whitespace --- code/MapUtil.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/code/MapUtil.php b/code/MapUtil.php index f8d8d79..a73f978 100644 --- a/code/MapUtil.php +++ b/code/MapUtil.php @@ -164,8 +164,6 @@ public static function instance() { self::$instances++; -// error_log("GOOGLE MAP INSTANCE:".self::$instances); - $url = Director::absoluteBaseURL(); // remove http and https @@ -223,9 +221,5 @@ public static function get_map(SS_List $list) { } } return $gmap; - } - - - - + } } \ No newline at end of file From 8eb9221bd023e738b65bcccbe378c5c9cfe20deb Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 4 Mar 2015 21:39:33 +0700 Subject: [PATCH 134/354] MINOR: Removal of console debug --- javascript/mapField.js | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/javascript/mapField.js b/javascript/mapField.js index bccbc15..78e6979 100755 --- a/javascript/mapField.js +++ b/javascript/mapField.js @@ -5,15 +5,12 @@ var bounds ; function gmloaded() { - //console.log('google maps call back'); initLivequery(); } // initialise the map function initMap() { - //console.log('init map'); - var myOptions = { zoom: 16, disableDefaultUI: false, @@ -105,8 +102,6 @@ var bounds ; setMarker(event.latLng, false); statusMessage('Location changed to '+lat+','+lng); }); - - google.maps.event.addListener(map, "zoom_changed", function(e) { if (zoomField.length) { @@ -118,12 +113,10 @@ var bounds ; map.setZoom( map.getZoom() ); - // When any tab is clicked, resize the map $('.ui-tabs-anchor').click(function() { google.maps.event.trigger(map, 'resize'); var gm = $('#GoogleMap'); - //console.log(gm); var useMapBounds = gm.attr('data-usemapbounds'); if (useMapBounds) { map.fitBounds(bounds); @@ -140,8 +133,6 @@ var bounds ; // utility functions function addGuideMarker(lat,lon) { - //console.log("LAT:"+lat); - //console.log("LON:"+lon); var latlng = new google.maps.LatLng(lat, lon); var pinColor = "CCCCCC"; var pinImage = new google.maps.MarkerImage("//chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColor, @@ -290,8 +281,6 @@ var bounds ; $('.geocodedSearchResults li').livequery('click', function(e) { // get the data needed to ask coords var t = $(this); - //console.log("ENTRY CLICKED"); - //console.log(t); var lat = t.attr("lat"); var lon = t.attr("lon"); var address = t.html(); @@ -331,7 +320,6 @@ var bounds ; (function($) { - function loadGoogleMapsAPI() { var script = document.createElement("script"); script.type = "text/javascript"; @@ -340,13 +328,8 @@ var bounds ; } - // deal with document ready - note this only gets called once due to the way silverstripe works, until the CMS is refreshed $(document).ready(function() { - loadGoogleMapsAPI(); - - - }); })(jQuery); \ No newline at end of file From ee7017380908137a68577251b0fed2d481537332 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 4 Mar 2015 21:40:10 +0700 Subject: [PATCH 135/354] ENHANCEMETN: Shortcode handler for google street view --- .../GoogleStreetViewShortCodeHandler.php | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 code/shortcodes/GoogleStreetViewShortCodeHandler.php diff --git a/code/shortcodes/GoogleStreetViewShortCodeHandler.php b/code/shortcodes/GoogleStreetViewShortCodeHandler.php new file mode 100644 index 0000000..93c5d75 --- /dev/null +++ b/code/shortcodes/GoogleStreetViewShortCodeHandler.php @@ -0,0 +1,68 @@ + 1, + 'Pitch' => 0 + ); + + // ensure JavaScript for the map service is only downloaded once + $arguments['DownloadJS'] = !MapUtil::get_map_already_rendered(); + MapUtil::set_map_already_rendered(true); + + // convert parameters to CamelCase as per standard template conventions + $arguments['Latitude'] = $arguments['latitude']; + $arguments['Longitude'] = $arguments['longitude']; + $arguments['Heading'] = $arguments['heading']; + + // optional parameter caption + if (isset($arguments['caption'])) { + $arguments['Caption'] = $arguments['caption']; + } + + // optional parameter pitch + if (isset($arguments['pitch'])) { + $arguments['Pitch'] = $arguments['pitch']; + } + + // optional parameter zoom + if (isset($arguments['zoom'])) { + $arguments['Zoom'] = $arguments['zoom']; + } + + // the id of the dom element to be used to render the street view + $arguments['DomID'] = 'google_streetview_'.self::$gsv_ctr; + + // incrememt the counter to ensure a unique id for each map canvas + self::$gsv_ctr++; + + // merge defaults and arguments + $customised = array_merge($defaults, $arguments); + + //get streetview template template + $template = new SSViewer( 'GoogleStreetView' ); + + //return the template customised with the parmameters + return $template->process( new ArrayData( $customised ) ); + } +} \ No newline at end of file From caae97f4a4ab15808bcbf328e8809e4b29b999a2 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 6 Mar 2015 11:10:56 +0700 Subject: [PATCH 136/354] MINOR: Added container div wrapper around the streetview rendering in order to style a caption as desired --- templates/Includes/GoogleStreetView.ss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/templates/Includes/GoogleStreetView.ss b/templates/Includes/GoogleStreetView.ss index 65bdab1..124ca1e 100644 --- a/templates/Includes/GoogleStreetView.ss +++ b/templates/Includes/GoogleStreetView.ss @@ -1,6 +1,8 @@ <% include GoogleJavaScript %> +
-<% if $Caption %>$Caption<% end_if %> +<% if $Caption %>

$Caption

<% end_if %> +
\ No newline at end of file From d54382545f76ddfe60fc5ac089f2e85c6ab1ecdb Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 11 Mar 2015 15:51:46 +0700 Subject: [PATCH 152/354] ENHANCEMENT: Enabled google maps short code handler --- _config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config.php b/_config.php index b942e4f..0e80497 100755 --- a/_config.php +++ b/_config.php @@ -7,4 +7,4 @@ } ShortcodeParser::get('default')->register('GoogleStreetView',array('GoogleStreetViewShortCodeHandler','parse_googlestreetview')); - +ShortcodeParser::get('default')->register('GoogleMap',array('GoogleMapShortCodeHandler','parse_googlemap')); \ No newline at end of file From c7af5b2faad3070c07d290ee85eb4049bc4118c8 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 11 Mar 2015 15:53:34 +0700 Subject: [PATCH 153/354] MINOR: Renamed css class in small letters for consistency --- templates/Includes/GoogleStreetView.ss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/Includes/GoogleStreetView.ss b/templates/Includes/GoogleStreetView.ss index 124ca1e..84c9e23 100644 --- a/templates/Includes/GoogleStreetView.ss +++ b/templates/Includes/GoogleStreetView.ss @@ -1,5 +1,5 @@ <% include GoogleJavaScript %> -
+
<% if $Caption %>

$Caption

<% end_if %>
From 020c819b21589e555e6bfc81cdc555d51704157c Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 11 Mar 2015 21:19:42 +0700 Subject: [PATCH 154/354] FIX: Remove word 'Test' from template --- templates/Includes/GoogleMapShortCode.ss | 2 -- 1 file changed, 2 deletions(-) diff --git a/templates/Includes/GoogleMapShortCode.ss b/templates/Includes/GoogleMapShortCode.ss index 32593e2..bb06eb2 100644 --- a/templates/Includes/GoogleMapShortCode.ss +++ b/templates/Includes/GoogleMapShortCode.ss @@ -1,10 +1,8 @@ <% include GoogleJavaScript %>
-Test
<% if $Caption %>

$Caption

<% end_if %>
-
style="width:{$Width}; height: {$Height};"<% end_if %><% if AdditionalCssClasses %> class="$AdditionalCssClasses"<% end_if %>>
\ No newline at end of file From 8ed07203bbf7d1d0dfaaaa79d0e6ef339d3245cb Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 16 Mar 2015 20:39:59 +0700 Subject: [PATCH 174/354] ENHANCEMENT: Render full screen button if flag set --- javascript/google/maputil.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index a9f5d63..15b9661 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -108,7 +108,8 @@ function addKmlFiles(map, kmlFiles) { } } -function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLat, maxLng, mapType, markers, lines, kmlFiles, jsonMapStyles, enableAutomaticCenterZoom, useClusterer) { +function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLat, maxLng, mapType, markers, lines, kmlFiles, + jsonMapStyles, enableAutomaticCenterZoom, useClusterer, allowFullScreen) { var newMap = []; newMap.googleMapID = googleMapID; newMap.zoom = zoom; @@ -125,6 +126,7 @@ function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLa newMap.jsonMapStyles = jsonMapStyles; newMap.enableAutomaticCenterZoom = enableAutomaticCenterZoom; newMap.useClusterer = useClusterer; + newMap.allowFullScreen = allowFullScreen; mappableMaps[googleMapID] = newMap; // increment map counter @@ -145,6 +147,7 @@ function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLa function loadedGoogleMapsAPI() { for (var i = 1; i <= mappableMapCtr; i++) { var map_info = mappableMaps['google_map_' + i]; + console.log(map_info); var map = new google.maps.Map(document.getElementById(map_info.googleMapID)); if (map_info.useClusterer) { @@ -158,7 +161,11 @@ function loadedGoogleMapsAPI() { //map.setOptions({styles: map_info.jsonMapStyles}); //}; - + if (map_info.allowFullScreen) { + map.controls[google.maps.ControlPosition.TOP_RIGHT].push( + FullScreenControl(map, "Full Screen", "Original Size") + ); + } if (map_info.enableAutomaticCenterZoom) { centre = map_info.centreCoordinates; map.setCenter(new google.maps.LatLng(centre.lat,centre.lng)); From 0a5eccd4ff6eab68c08c4442938bec859fdcc323 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 16 Mar 2015 20:41:04 +0700 Subject: [PATCH 175/354] ENHANCEMENT: JavaScript code to allow full screen rendering of Google maps. Source of script, http://www.doogal.co.uk/FullScreen.php --- javascript/google/FullScreenControl.js | 128 +++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 javascript/google/FullScreenControl.js diff --git a/javascript/google/FullScreenControl.js b/javascript/google/FullScreenControl.js new file mode 100644 index 0000000..6368b4b --- /dev/null +++ b/javascript/google/FullScreenControl.js @@ -0,0 +1,128 @@ +/// +function FullScreenControl(map, enterFull, exitFull) { + if (enterFull === void 0) { enterFull = null; } + if (exitFull === void 0) { exitFull = null; } + if (enterFull == null) { + enterFull = "Full screen"; + } + if (exitFull == null) { + exitFull = "Exit full screen"; + } + var controlDiv = document.createElement("div"); + controlDiv.className = "fullScreen"; + controlDiv.index = 1; + controlDiv.style.padding = "5px"; + // Set CSS for the control border. + var controlUI = document.createElement("div"); + controlUI.style.backgroundColor = "white"; + controlUI.style.borderStyle = "solid"; + controlUI.style.borderWidth = "1px"; + controlUI.style.borderColor = "#717b87"; + controlUI.style.cursor = "pointer"; + controlUI.style.textAlign = "center"; + controlUI.style.boxShadow = "rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px"; + controlDiv.appendChild(controlUI); + // Set CSS for the control interior. + var controlText = document.createElement("div"); + controlText.style.fontFamily = "Roboto,Arial,sans-serif"; + controlText.style.fontSize = "11px"; + controlText.style.fontWeight = "400"; + controlText.style.paddingTop = "1px"; + controlText.style.paddingBottom = "1px"; + controlText.style.paddingLeft = "6px"; + controlText.style.paddingRight = "6px"; + controlText.innerHTML = "" + enterFull + ""; + controlUI.appendChild(controlText); + // set print CSS so the control is hidden + var head = document.getElementsByTagName("head")[0]; + var newStyle = document.createElement("style"); + newStyle.setAttribute("type", "text/css"); + newStyle.setAttribute("media", "print"); + var cssText = ".fullScreen { display: none;}"; + var texNode = document.createTextNode(cssText); + try { + newStyle.appendChild(texNode); + } + catch (e) { + // IE8 hack + newStyle.styleSheet.cssText = cssText; + } + head.appendChild(newStyle); + var fullScreen = false; + var interval; + var mapDiv = map.getDiv(); + var divStyle = mapDiv.style; + if (mapDiv.runtimeStyle) { + divStyle = mapDiv.runtimeStyle; + } + var originalPos = divStyle.position; + var originalWidth = divStyle.width; + var originalHeight = divStyle.height; + // IE8 hack + if (originalWidth === "") { + originalWidth = mapDiv.style.width; + } + if (originalHeight === "") { + originalHeight = mapDiv.style.height; + } + var originalTop = divStyle.top; + var originalLeft = divStyle.left; + var originalZIndex = divStyle.zIndex; + var bodyStyle = document.body.style; + if (document.body.runtimeStyle) { + bodyStyle = document.body.runtimeStyle; + } + var originalOverflow = bodyStyle.overflow; + controlDiv.goFullScreen = function () { + var center = map.getCenter(); + mapDiv.style.position = "fixed"; + mapDiv.style.width = "100%"; + mapDiv.style.height = "100%"; + mapDiv.style.top = "0"; + mapDiv.style.left = "0"; + mapDiv.style.zIndex = "100"; + document.body.style.overflow = "hidden"; + controlText.innerHTML = "" + exitFull + ""; + fullScreen = true; + google.maps.event.trigger(map, "resize"); + map.setCenter(center); + // this works around street view causing the map to disappear, which is caused by Google Maps setting the + // CSS position back to relative. There is no event triggered when Street View is shown hence the use of setInterval + interval = setInterval(function () { + if (mapDiv.style.position !== "fixed") { + mapDiv.style.position = "fixed"; + google.maps.event.trigger(map, "resize"); + } + }, 100); + }; + controlDiv.exitFullScreen = function () { + var center = map.getCenter(); + if (originalPos === "") { + mapDiv.style.position = "relative"; + } + else { + mapDiv.style.position = originalPos; + } + mapDiv.style.width = originalWidth; + mapDiv.style.height = originalHeight; + mapDiv.style.top = originalTop; + mapDiv.style.left = originalLeft; + mapDiv.style.zIndex = originalZIndex; + document.body.style.overflow = originalOverflow; + controlText.innerHTML = "" + enterFull + ""; + fullScreen = false; + google.maps.event.trigger(map, "resize"); + map.setCenter(center); + clearInterval(interval); + }; + // Setup the click event listener + google.maps.event.addDomListener(controlUI, "click", function () { + if (!fullScreen) { + controlDiv.goFullScreen(); + } + else { + controlDiv.exitFullScreen(); + } + }); + return controlDiv; +} From ca3e4ff0a5dbcec2ae6b093b9bdaf2cd773b6252 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 16 Mar 2015 20:41:23 +0700 Subject: [PATCH 176/354] ENHANCEMENT: Global config for full screen --- _config/maps.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 _config/maps.yml diff --git a/_config/maps.yml b/_config/maps.yml new file mode 100644 index 0000000..e04d5e9 --- /dev/null +++ b/_config/maps.yml @@ -0,0 +1,6 @@ +--- +Name: mappable +After: 'framework/*','cms/*' +--- +Mappable: + allow_full_screen: true \ No newline at end of file From 6a2d92b2b380bcdca1fd0943ec7994d80a58fa13 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 16 Mar 2015 20:43:02 +0700 Subject: [PATCH 177/354] ENHANCEMENT: Include fullscreen controlJS --- templates/Includes/GoogleJavaScript.ss | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/Includes/GoogleJavaScript.ss b/templates/Includes/GoogleJavaScript.ss index 08b670b..7a39183 100644 --- a/templates/Includes/GoogleJavaScript.ss +++ b/templates/Includes/GoogleJavaScript.ss @@ -3,6 +3,7 @@ <% else %> + <% end_if %> \ No newline at end of file + From 179ab39d7a3eac0db6d6d2844bd58237cf7a5e4e Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 16 Mar 2015 23:38:56 +0700 Subject: [PATCH 182/354] FIX: Whitespace and coding conventions WIP: Fixing whitespace WIP: About to perl search and replace for bracket issues FIX: Bracket spacing FIX: Added line length warning FIX: Whitespace around brackets FIX: Whitespace around brackets FIX: Ensure static variable conforms to coding conventions --- .editorconfig | 4 + code/LatLongField.php | 81 +- code/MapAPI.php | 1543 +++++++++-------- code/MapExtension.php | 306 ++-- code/MapField.php | 107 +- code/MapLayer.php | 46 +- code/MapLayerExtension.php | 40 +- code/MapMarkerSetsExtension.php | 50 +- code/MapUtil.php | 44 +- code/Mappable.php | 18 +- code/MappableData.php | 14 +- code/MappableDataObjectSet.php | 6 +- code/OSMPointOfInterestExtension.php | 8 +- code/POIMapPage.php | 10 +- code/PointOfInterest.php | 6 +- code/PointsOfInterestAdmin.php | 8 +- code/PointsOfInterestLayer.php | 14 +- code/PointsOfInterestLayerExtension.php | 38 +- code/shortcodes/GoogleMapShortCodeHandler.php | 14 +- .../GoogleStreetViewShortCodeHandler.php | 128 +- 20 files changed, 1260 insertions(+), 1225 deletions(-) diff --git a/.editorconfig b/.editorconfig index 34ecfa8..b22c832 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,6 +12,10 @@ indent_style = tab insert_final_newline = true trim_trailing_whitespace = true +# Docs say 80 ideally, 100 ok, no more than 120 +# http://doc.silverstripe.org/en/getting_started/coding_conventions/ +max_line_length = 100 + [*.md] trim_trailing_whitespace = false diff --git a/code/LatLongField.php b/code/LatLongField.php index 8f2a942..c551fdc 100755 --- a/code/LatLongField.php +++ b/code/LatLongField.php @@ -16,42 +16,46 @@ class LatLongField extends FieldGroup { protected $buttonText; - public function __construct( $children = array(), $addressFields = array(), $buttonText = null ) { - if ( ( sizeof( $children ) < 2 ) || ( !$children[0] instanceof FormField ) || ( !$children[1] instanceof FormField ) ) { - user_error( 'LatLongField argument 1 must be an array containing at least two FormField objects for Lat/Long values, respectively.', E_USER_ERROR ); + public function __construct($children = array(), $addressFields = array(), $buttonText = null) { + if ((sizeof($children) < 2) || + (!$children[0] instanceof FormField) || + (!$children[1] instanceof FormField) + ) { + user_error('LatLongField argument 1 must be an array containing at least two FormField '. + 'objects for Lat/Long values, respectively.', E_USER_ERROR); } - parent::__construct( $children ); + parent::__construct($children); $this->addressFields = $addressFields; - $this->buttonText = $buttonText ? $buttonText : _t( 'LatLongField.LOOKUP', 'Search' ); + $this->buttonText = $buttonText ? $buttonText : _t('LatLongField.LOOKUP', 'Search'); $this->latField = $children[0]->getName(); $this->longField = $children[1]->getName(); - if ( sizeof( $children ) == 3 ) { + if (sizeof($children) == 3) { $this->zoomField = $children[2]->getName(); } $name = ""; - foreach ( $children as $field ) { + foreach ($children as $field) { $name .= $field->getName(); } // hide the lat long and zoom fields from the interface - foreach ( $this->FieldList() as $fieldToHide ) { - $fieldToHide->addExtraClass( 'hide' ); + foreach ($this->FieldList() as $fieldToHide) { + $fieldToHide->addExtraClass('hide'); } - - $this->name = $name; } - public function hasData() {return true;} + public function hasData() { + return true; + } - public function FieldHolder( $properties = array() ) { - Requirements::javascript( THIRDPARTY_DIR.'/jquery/jquery.js' ); - Requirements::javascript( THIRDPARTY_DIR.'/jquery-livequery/jquery.livequery.js' ); - Requirements::javascript( THIRDPARTY_DIR.'/jquery-metadata/jquery.metadata.js' ); + public function FieldHolder($properties = array()) { + Requirements::javascript(THIRDPARTY_DIR.'/jquery/jquery.js'); + Requirements::javascript(THIRDPARTY_DIR.'/jquery-livequery/jquery.livequery.js'); + Requirements::javascript(THIRDPARTY_DIR.'/jquery-metadata/jquery.metadata.js'); //Requirements::javascript(MAPPABLE_MODULE_PATH.'/javascript/mapField.js'); $js = ' @@ -62,13 +66,7 @@ public function FieldHolder( $properties = array() ) { '; - - - //Requirements::javascriptTemplate( MAPPABLE_MODULE_PATH.'/javascript/mapField.js', $fieldNames ); - Requirements::javascript( MAPPABLE_MODULE_PATH.'/javascript/mapField.js' ); - - //$this->FieldList()->push( new MapField( 'GoogleMap', 'GoogleMap' ) ); - + Requirements::javascript(MAPPABLE_MODULE_PATH.'/javascript/mapField.js'); $attributes = array( 'class' => 'editableMap', 'id' => 'GoogleMap', @@ -76,7 +74,7 @@ public function FieldHolder( $properties = array() ) { 'data-LonFieldName' => $this->longField, 'data-ZoomFieldName' => $this->zoomField, 'data-UseMapBounds' => false - ); + ); Requirements::css('mappable/css/mapField.css'); $guidePointsJSON = ''; @@ -84,15 +82,16 @@ public function FieldHolder( $properties = array() ) { $guidePointsJSON = json_encode($this->guidePoints); $attributes['data-GuidePoints'] = $guidePointsJSON; - // we only wish to change the bounds to those of all the points iff the item currently has no location + // we only wish to change the bounds to those of all the points iff + // the item currently has no location $attributes['data-useMapBounds'] = true; } $content = '
' . $this->createTag( "div", $attributes - ) . '
'; + ) . '
'; - $this->FieldList()->push( new LiteralField( 'locationEditor', $content ) ); + $this->FieldList()->push(new LiteralField('locationEditor', $content)); @@ -101,31 +100,37 @@ public function FieldHolder( $properties = array() ) {
-
- '; +
+ '; - $this->FieldList()->push( new LiteralField( 'mapSearch', $content2 ) ); + $this->FieldList()->push(new LiteralField('mapSearch', $content2)); return parent::FieldHolder(); } - public function geocode( SS_HTTPRequest $r ) { - if ( $address = $r->requestVar( 'address' ) ) { - if ( $json = @file_get_contents( "http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=".urlencode( $address ) ) ) { - $response = Convert::json2array( $json ); + /* + Perform place name search as a means of navigation when editing locations + */ + public function geocode(SS_HTTPRequest $r) { + if ($address = $r->requestVar('address')) { + if ($json = @file_get_contents( + "http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=". + urlencode($address))) { + $response = Convert::json2array($json); $location = $response['results'][0]->geometry->location; - return new SS_HTTPResponse( $location->lat.",".$location->lng ); + return new SS_HTTPResponse($location->lat.",".$location->lng); } } } + /* - Set guidance points for the map being edited. For example in a photographic set show the map position of some other images - so that subsequent photo edits do not start with a map centred on the horizon + Set guidance points for the map being edited. For example in a photographic set show the map + position of some other images so that subsequent photo edits do not start with a map centred + on the horizon */ public function setGuidePoints($guidePoints) { $this->guidePoints = $guidePoints; } } -?> \ No newline at end of file diff --git a/code/MapAPI.php b/code/MapAPI.php index 1b7ab61..49ed9cd 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -17,690 +17,707 @@ class MapAPI extends ViewableData { - /** GoogleMap key **/ - protected $googleMapKey = ''; + /** GoogleMap key **/ + protected $googleMapKey = ''; - /** GoogleMap ID for the HTML DIV **/ - protected $googleMapId = 'googlemapapi'; + /** GoogleMap ID for the HTML DIV **/ + protected $googleMapId = 'googlemapapi'; - /** GoogleMap Direction ID for the HTML DIV **/ - protected $googleMapDirectionId = 'route'; + /** GoogleMap Direction ID for the HTML DIV **/ + protected $googleMapDirectionId = 'route'; - /* Additional CSS classes to use when rendering the map */ - protected $set_additional_css_classes = ''; + /* Additional CSS classes to use when rendering the map */ + protected $set_additional_css_classes = ''; - /** Width of the gmap **/ - protected $width = 800; + /** Width of the gmap **/ + protected $width = 800; - /** Height of the gmap **/ - protected $height = 600; + /** Height of the gmap **/ + protected $height = 600; - /** Icon width of the gmarker **/ - protected $iconWidth = 20; + /** Icon width of the gmarker **/ + protected $iconWidth = 20; - /** Icon height of the gmarker **/ - protected $iconHeight = 34; + /** Icon height of the gmarker **/ + protected $iconHeight = 34; - /* array of lines to be drawn on the map */ - protected $lines = array(); + /* array of lines to be drawn on the map */ + protected $lines = array(); - /* kml file to be rendered */ - protected $kmlFiles = array(); + /* kml file to be rendered */ + protected $kmlFiles = array(); - /** - * - * - * @var int Infowindow width of the gmarker - * */ - protected $infoWindowWidth = 500; + /** + * + * + * @var int Infowindow width of the gmarker + * */ + protected $infoWindowWidth = 500; - /** Default zoom of the gmap **/ - protected $zoom = 9; + /** Default zoom of the gmap **/ + protected $zoom = 9; - /** Enable the zoom of the Infowindow **/ - protected $enableWindowZoom = false; + /** Enable the zoom of the Infowindow **/ + protected $enableWindowZoom = false; - /** Default zoom of the Infowindow **/ - protected $infoWindowZoom = 13; + /** Default zoom of the Infowindow **/ + protected $infoWindowZoom = 13; - /** Lang of the gmap **/ - protected $lang = 'en'; + /** Lang of the gmap **/ + protected $lang = 'en'; - /**Center of the gmap **/ - protected $center = 'Paris, France'; + /**Center of the gmap **/ + protected $center = 'Paris, France'; - /* - Additional CSS classes to render as a class attribute for the div of the map. Use this if you want more - fine grained control over your map using CSS. If blank it will be ignored - */ - protected $additional_css_classes = ''; + /* + Additional CSS classes to render as a class attribute for the div of the + map. Use this if you want more fine grained control over your map using + CSS. If blank it will be ignored + */ + protected $additional_css_classes = ''; - /* Decided whether or not to show the inline map css style on div creation */ - protected $show_inline_map_div_style = true; + /* Decided whether or not to show the inline map css style on div creation */ + protected $show_inline_map_div_style = true; - protected $latLongCenter = null; + protected $latLongCenter = null; - protected $mappingService = 'Google'; + protected $mappingService = 'Google'; - /* - Map styles for google maps, ignored if null -
+	/*
+		Map styles for google maps, ignored if null
+		
 var styles = [
-       {
-               featureType: 'water',
-               elementType: 'all',
-               stylers: [
-                       { hue: '#B6C5CF' },
-                       { saturation: -54 },
-                       { lightness: 1 },
-                       { visibility: 'on' }
-               ]
-       },{
-               featureType: 'landscape',
-               elementType: 'all',
-               stylers: [
-                       { hue: '#D9D4C8' },
-                       { saturation: -32 },
-                       { lightness: -8 },
-                       { visibility: 'on' }
-               ]
-       },{
-               featureType: 'road',
-               elementType: 'all',
-               stylers: [
-                       { hue: '#A69D97' },
-                       { saturation: -92 },
-                       { lightness: -3 },
-                       { visibility: 'on' }
-               ]
-       },{
-               featureType: 'poi',
-               elementType: 'all',
-               stylers: [
-                       { hue: '#E7E6DB' },
-                       { saturation: -53 },
-                       { lightness: 47 },
-                       { visibility: 'on' }
-               ]
-       }
+			 {
+							 featureType: 'water',
+							 elementType: 'all',
+							 stylers: [
+											 { hue: '#B6C5CF' },
+											 { saturation: -54 },
+											 { lightness: 1 },
+											 { visibility: 'on' }
+							 ]
+			 },{
+							 featureType: 'landscape',
+							 elementType: 'all',
+							 stylers: [
+											 { hue: '#D9D4C8' },
+											 { saturation: -32 },
+											 { lightness: -8 },
+											 { visibility: 'on' }
+							 ]
+			 },{
+							 featureType: 'road',
+							 elementType: 'all',
+							 stylers: [
+											 { hue: '#A69D97' },
+											 { saturation: -92 },
+											 { lightness: -3 },
+											 { visibility: 'on' }
+							 ]
+			 },{
+							 featureType: 'poi',
+							 elementType: 'all',
+							 stylers: [
+											 { hue: '#E7E6DB' },
+											 { saturation: -53 },
+											 { lightness: 47 },
+											 { visibility: 'on' }
+							 ]
+			 }
 ];
-    
- */ - protected $jsonMapStyles = '[]'; +
+ */ + protected $jsonMapStyles = '[]'; - protected $delayLoadMapFunction = false; + protected $delayLoadMapFunction = false; - /** - * Type of the gmap, can be: - * 'google.maps.MapTypeId.ROADMAP' (roadmap), - * 'G_SATELLITE_MAP' (sattelite) - * 'G_HYBRID_MAP' (hybrid) - * 'G_PHYSICAL_MAP' (terrain) - */ + /** + * Type of the gmap, can be: + * 'google.maps.MapTypeId.ROADMAP' (roadmap), + * 'G_SATELLITE_MAP' (sattelite) + * 'G_HYBRID_MAP' (hybrid) + * 'G_PHYSICAL_MAP' (terrain) + */ - protected $mapType = 'google.maps.MapTypeId.ROADMAP'; + protected $mapType = 'google.maps.MapTypeId.ROADMAP'; - /** Content of the HTML generated **/ - protected $content = ''; + /** Content of the HTML generated **/ + protected $content = ''; - protected $mapService = 'google'; + protected $mapService = 'google'; - /** Add the direction button to the infowindow **/ - protected $displayDirectionFields = false; + /** Add the direction button to the infowindow **/ + protected $displayDirectionFields = false; - /** Hide the marker by default **/ - protected $defaultHideMarker = false; + /** Hide the marker by default **/ + protected $defaultHideMarker = false; - /** Extra content (marker, etc...) **/ - //protected $contentMarker = ''; + /** Extra content (marker, etc...) **/ + //protected $contentMarker = ''; - // a list of markers, markers being associative arrays - protected $markers = array(); + // a list of markers, markers being associative arrays + protected $markers = array(); - /** Use clusterer to display a lot of markers on the gmap **/ - protected $useClusterer = false; - protected $gridSize = 100; - protected $maxZoom = 9; - protected $clustererLibraryPath = '/mappable/javascript/Fluster2.packed.js'; + /** Use clusterer to display a lot of markers on the gmap **/ + protected $useClusterer = false; + protected $gridSize = 100; + protected $maxZoom = 9; + protected $clustererLibraryPath = '/mappable/javascript/Fluster2.packed.js'; - /** Enable automatic center/zoom **/ - protected $enableAutomaticCenterZoom = false; + /** Enable automatic center/zoom **/ + protected $enableAutomaticCenterZoom = false; - /** maximum longitude of all markers **/ - protected $maxLng = -1000000; + /** maximum longitude of all markers **/ + protected $maxLng = -1000000; - /** minimum longitude of all markers **/ - protected $minLng = 1000000; + /** minimum longitude of all markers **/ + protected $minLng = 1000000; - /** max latitude of all markers **/ - protected $maxLat = -1000000; + /** max latitude of all markers **/ + protected $maxLat = -1000000; - /** min latitude of all markers **/ - protected $minLat = 1000000; + /** min latitude of all markers **/ + protected $minLat = 1000000; - /** map center latitude (horizontal), calculated automatically as markers are added to the map **/ - protected $centerLat = null; + /** map center latitude (horizontal), calculated automatically as markers + are added to the map **/ + protected $centerLat = null; - /** map center longitude (vertical), calculated automatically as markers are added to the map **/ - protected $centerLng = null; + /** map center longitude (vertical), calculated automatically as markers + are added to the map **/ + protected $centerLng = null; - /** factor by which to fudge the boundaries so that when we zoom encompass, the markers aren't too close to the edge **/ - protected $coordCoef = 0.01; + /** factor by which to fudge the boundaries so that when we zoom encompass, + the markers aren't too close to the edge **/ + protected $coordCoef = 0.01; - /* set this to true to render button to maximize / minimize a map */ - protected $allowFullScreen = null; + /* set this to true to render button to maximize / minimize a map */ + protected $allowFullScreen = null; - protected static $includeDownloadJavascript = false; - - - /** - * Class constructor - * - * @param string $googleMapKey the googleMapKey - * - * @return void - */ - - public function __construct( $googleMapKey='' ) { - $this->googleMapKey = $googleMapKey; - } - - /** - * Set the key of the gmap - * - * @param string $googleMapKey the googleMapKey - * - * @return void - */ - - public function setKey( $googleMapKey ) { - $this->googleMapKey = $googleMapKey; - } - - public function setIncludeDownloadJavascript($inclusion) { - self::$includeDownloadJavascript = $inclusion; - } - - - public function setShowInlineMapDivStyle( $new_show_inline_map_div_style ) { - $this->show_inline_map_div_style = $new_show_inline_map_div_style; - } - - public function setAdditionalCSSClasses( $new_additional_css_classes ) { - $this->additional_css_classes = $new_additional_css_classes; - } - - - public function setMapStyles( $newStyles ) { - $this->jsonMapStyles = $newStyles; - } - - - - public function setDelayLoadMapFunction( $newDelay ) { - $this->delayLoadMapFunction = $newDelay; - } - - /** - * Set the useClusterer parameter (optimization to display a lot of marker) - * - * @param boolean $useClusterer use cluster or not - * @param string $clusterIcon the cluster icon - * @param int $maxVisibleMarkers max visible markers - * @param int $gridSize grid size - * @param int $minMarkersPerClusterer minMarkersPerClusterer - * @param int $maxLinesPerInfoBox maxLinesPerInfoBox - * - * @return void - */ - - public function setClusterer( $useClusterer, $gridSize=100, $maxZoom=9, $clustererLibraryPath='mappable/javascript/Fluster2.packed.js' ) { - $this->useClusterer = $useClusterer; - $this->gridSize = $gridSize; - $this->maxZoom = $maxZoom; - $this->clustererLibraryPath = $clustererLibraryPath; - } - - /** - * Set the ID of the default gmap DIV - * - * @param string $googleMapId the google div ID - * - * @return void - */ - - public function setDivId( $googleMapId ) { - $this->googleMapId = $googleMapId; - } - - /** - * Set the ID of the default gmap direction DIV - * - * @param string $googleMapDirectionId GoogleMap Direction ID for the HTML DIV - * - * @return void - */ - - public function setDirectionDivId( $googleMapDirectionId ) { - $this->googleMapDirectionId = $googleMapDirectionId; - } - - /** - * Set the size of the gmap - * - * @param int $width GoogleMap width - * @param int $height GoogleMap height - * - * @return void - */ - - public function setSize( $width, $height ) { - $this->width = $width; - $this->height = $height; - } - - /** - * Set the with of the gmap infowindow (on marker clik) - * - * @param int $infoWindowWidth GoogleMap info window width - * - * @return void - */ - - public function setInfoWindowWidth( $infoWindowWidth ) { - $this->infoWindowWidth = $infoWindowWidth; - } - - /** - * Set the size of the icon markers - * - * @param int $iconWidth GoogleMap marker icon width - * @param int $iconHeight GoogleMap marker icon height - * - * @return void - */ - - public function setIconSize( $iconWidth, $iconHeight ) { - $this->iconWidth = $iconWidth; - $this->iconHeight = $iconHeight; - } - - /** - * Set the lang of the gmap - * - * @param string $lang GoogleMap lang : fr,en,.. - * - * @return void - */ - - public function setLang( $lang ) { - $this->lang = $lang; - } - - /** - * Set the zoom of the gmap - * - * @param int $zoom GoogleMap zoom. - * - * @return void - */ - - public function setZoom( $zoom ) { - $this->zoom = $zoom; - } - - /** - * Set the zoom of the infowindow - * - * @param int $zoom GoogleMap zoom. - * - * @return void - */ - - public function setInfoWindowZoom( $infoWindowZoom ) { - $this->infoWindowZoom = $infoWindowZoom; - } - - /** - * Enable the zoom on the marker when you click on it - * - * @param int $zoom GoogleMap zoom. - * - * @return void - */ - - public function setEnableWindowZoom( $enableWindowZoom ) { - $this->enableWindowZoom = $enableWindowZoom; - } - - /** - * Enable theautomatic center/zoom at the gmap load - * - * @param int $zoom GoogleMap zoom. - * - * @return void - */ - - public function setEnableAutomaticCenterZoom( $enableAutomaticCenterZoom ) { - $this->enableAutomaticCenterZoom = $enableAutomaticCenterZoom; - } - - /** - * Set the center of the gmap (an address) - * - * @param string $center GoogleMap center (an address) - * - * @return void - */ - - public function setCenter( $center ) { - $this->center = $center; - } - - /** - * Set the type of the gmap - * - * @param string $mapType ( can be 'google.maps.MapTypeId.ROADMAP', 'G_SATELLITE_MAP', 'G_HYBRID_MAP', 'G_PHYSICAL_MAP') - * - * @return void - */ - - public function setMapType( $mapType ) { - $this->mapType = $mapType; - } - - /* - Set whether or not to allow the full screen tools - */ - public function setAllowFullScreen( $allowed ) { - $this->allowFullScreen = $allowed; - } - - /** - * Set the center of the gmap - **/ - public function setLatLongCenter( $center ) { - $this->latLongCenter = $center; - } - - /** - * Decide whether or not to show direction controls - * - * @param boolean $displayDirectionFields display directions or not in the info window - * - * @return void - */ - - public function setDisplayDirectionFields( $displayDirectionFields ) { - $this->displayDirectionFields = $displayDirectionFields; - } - - /** - * Set the defaultHideMarker - * - * @param boolean $defaultHideMarker hide all the markers on the map by default - * - * @return void - */ - - public function setDefaultHideMarker( $defaultHideMarker ) { - $this->defaultHideMarker = $defaultHideMarker; - } - - /** - * Get the google map content - * - * @return string the google map html code - */ - - public function getGoogleMap() { - return $this->content; - } - - /** - * Get URL content using cURL. - * - * @param string $url the url - * - * @return string the html code - * - * @todo add proxy settings - */ - - public function getContent( $url ) { - $curl = curl_init(); - curl_setopt( $curl, CURLOPT_TIMEOUT, 10 ); - curl_setopt( $curl, CURLOPT_CONNECTTIMEOUT, 5 ); - curl_setopt( $curl, CURLOPT_RETURNTRANSFER, TRUE ); - curl_setopt( $curl, CURLOPT_URL, $url ); - $data = curl_exec( $curl ); - curl_close( $curl ); - return $data; - } - - /** - * Geocoding an address (address -> lat,lng) - * - * @param string $address an address - * - * @return array array with precision, lat & lng - */ - - public function geocoding( $address ) { - $encodeAddress = urlencode( $address ); - $url = "//maps.google.com/maps/geo?q=".$encodeAddress."&output=csv&key=".$this->googleMapKey; - - if ( function_exists( 'curl_init' ) ) { - $data = $this->getContent( $url ); - } else { - $data = file_get_contents( $url ); - } - - $csvSplit = preg_split( "/,/", $data ); - $status = $csvSplit[0]; - - if ( strcmp( $status, "200" ) == 0 ) { - $return = $csvSplit; // successful geocode, $precision = $csvSplit[1],$lat = $csvSplit[2],$lng = $csvSplit[3]; - } else { - $return = null; // failure to geocode - } - - return $return; - } - - /** - * Add marker by his coord - * - * @param string $lat lat - * @param string $lng lngs - * @param string $html html code display in the info window - * @param string $category marker category - * @param string $icon an icon url - * - * @return void - */ - - public function addMarkerByCoords( $lat, $lng, $html='', $category='', $icon='' ) { - - // Save the lat/lon to enable the automatic center/zoom - $this->maxLng = (float) max( (float)$lng, $this->maxLng ); - $this->minLng = (float) min( (float)$lng, $this->minLng ); - $this->maxLat = (float) max( (float)$lat, $this->maxLat ); - $this->minLat = (float) min( (float)$lat, $this->minLat ); - $this->centerLng = (float) ( $this->minLng + $this->maxLng ) / 2; - $this->centerLat = (float) ( $this->minLat + $this->maxLat ) / 2; - $iconURL = null; - if ($icon) { - $iconURL = $icon->getURL(); - } - - //$this->contentMarker .= "\t\t\t".'createMarker('.$lat.','.$lng.',"'.$html.'","'.$category.'","'.$icon.'");'."\n"; - $m = array( - 'latitude' => $lat, - 'longitude' => $lng, - 'html' => $html, - 'category' => $category, - 'icon' => $iconURL - ); - array_push($this->markers, $m); - } - - /** - * Add marker by his address - * - * @param string $address an ddress - * @param string $content html code display in the info window - * @param string $category marker category - * @param string $icon an icon url - * - * @return void - */ - - public function addMarkerByAddress( $address, $content='', $category='', $icon='' ) { - $point = $this->geocoding( $address ); - if ( $point!==null ) { - $this->addMarkerByCoords( $point[2], $point[3], $content, $category, $icon ); - } else { - // throw new Exception('Adress not found : '.$address); - } - } - - /** - * Add marker by an array of coord - * - * @param string $coordtab an array of lat,lng,content - * @param string $category marker category - * @param string $icon an icon url - * - * @return void - */ - - public function addArrayMarkerByCoords( $coordtab, $category='', $icon='' ) { - foreach ( $coordtab as $coord ) { - $this->addMarkerByCoords( $coord[0], $coord[1], $coord[2], $category, $icon ); - } - } - - - /** - * Adds a {@link ViewableData} object that implements {@link Mappable} - * to the map. - * - * @param ViewableData $obj - */ - public function addMarkerAsObject( ViewableData $obj ) { - $extensionsImplementMappable = false; - $extensions = Object::get_extensions(get_class($obj)); - - foreach ($extensions as $extension) { - $class = new ReflectionClass($extension); - if ($class->implementsInterface('Mappable')) { - $extensionsImplementMappable = true; - } - - } - if ( $extensionsImplementMappable || ($obj instanceof Mappable) || (Object::has_extension($obj->ClassName, 'MapExtension')) ) { - //if(($obj->getMappableLatitude() > 0) || ($obj->getMappableLongitude() > 0)) { - $cat = $obj->hasMethod( 'getMappableMapCategory' ) ? $obj->getMappableMapCategory() : "default"; - $this->addMarkerByCoords( $obj->getMappableLatitude(), $obj->getMappableLongitude(), $obj->getMappableMapContent(), $cat, $obj->getMappableMapPin() ); - //} - } - } - - - /** - * Draws a line between two {@link ViewableData} objects - * - * @param ViewableData $one The first point - * @param ViewableData $two The second point - * @param string $color The hexidecimal color of the line - */ - public function connectPoints( ViewableData $one, ViewableData $two, $color = "#FF3300" ) { - $this->addLine( - array( $one->getMappableLatitude(), $one->getMappableLongitude() ), - array( $two->getMappableLatitude(), $two->getMappableLongitude() ), - $color - ); - } - - - public function forTemplate() { - $this->generate(); - return $this->getGoogleMap(); - } - - - - /** - * Add marker by an array of address - * - * @param string $coordtab an array of address - * @param string $category marker category - * @param string $icon an icon url - * - * @return void - */ - - public function addArrayMarkerByAddress( $coordtab, $category='', $icon='' ) { - foreach ( $coordtab as $coord ) { - $this->addMarkerByAddress( $coord[0], $coord[1], $category, $icon ); - } - } - - /** - * Set a direction between 2 addresss and set a text panel - * - * @param string $from an address - * @param string $to an address - * @param string $idpanel id of the div panel - * - * @return void - */ - - public function addDirection( $from, $to, $idpanel='' ) { - $this->contentMarker .= 'addDirection("'.$from.'","'.$to.'","'.$idpanel.'");'; - } - - /** - * Parse a KML file and add markers to a category - * - * @param string $url url of the kml file compatible with gmap and gearth - * - * @return void - */ - - public function addKML( $url ) { - array_push($this->kmlFiles, $url); - } - - - /* - Add a line to the map - - */ - public function addLine( $from = array(), $to = array(), $color = "#FF3300" ) { - $line = array( - 'lat1' => $from[0], - 'lon1' => $from[1], - 'lat2' => $to[0], - 'lon2' => $to[1], - 'color' => $color - ); - - array_push($this->lines, $line); - - - } - - /** - * Initialize the javascript code - * - * @return void - */ + protected static $include_download_javascript = false; + + + /** + * Class constructor + * + * @param string $googleMapKey the googleMapKey + * + * @return void + */ + + public function __construct($googleMapKey='') { + $this->googleMapKey = $googleMapKey; + } + + /** + * Set the key of the gmap + * + * @param string $googleMapKey the googleMapKey + * + * @return void + */ + + public function setKey($googleMapKey) { + $this->googleMapKey = $googleMapKey; + } + + public function setIncludeDownloadJavascript($inclusion) { + self::$include_download_javascript = $inclusion; + } + + + public function setShowInlineMapDivStyle($new_show_inline_map_div_style) { + $this->show_inline_map_div_style = $new_show_inline_map_div_style; + } + + public function setAdditionalCSSClasses($new_additional_css_classes) { + $this->additional_css_classes = $new_additional_css_classes; + } + + + public function setMapStyles($newStyles) { + $this->jsonMapStyles = $newStyles; + } + + + + public function setDelayLoadMapFunction($newDelay) { + $this->delayLoadMapFunction = $newDelay; + } + + /** + * Set the useClusterer parameter (optimization to display a lot of marker) + * + * @param boolean $useClusterer use cluster or not + * @param string $clusterIcon the cluster icon + * @param int $maxVisibleMarkers max visible markers + * @param int $gridSize grid size + * @param int $minMarkersPerClusterer minMarkersPerClusterer + * @param int $maxLinesPerInfoBox maxLinesPerInfoBox + * + * @return void + */ + + public function setClusterer($useClusterer, $gridSize=100, $maxZoom=9, + $clustererLibraryPath='mappable/javascript/Fluster2.packed.js') { + $this->useClusterer = $useClusterer; + $this->gridSize = $gridSize; + $this->maxZoom = $maxZoom; + $this->clustererLibraryPath = $clustererLibraryPath; + } + + /** + * Set the ID of the default gmap DIV + * + * @param string $googleMapId the google div ID + * + * @return void + */ + + public function setDivId($googleMapId) { + $this->googleMapId = $googleMapId; + } + + /** + * Set the ID of the default gmap direction DIV + * + * @param string $googleMapDirectionId GoogleMap Direction ID for the HTML DIV + * + * @return void + */ + + public function setDirectionDivId($googleMapDirectionId) { + $this->googleMapDirectionId = $googleMapDirectionId; + } + + /** + * Set the size of the gmap + * + * @param int $width GoogleMap width + * @param int $height GoogleMap height + * + * @return void + */ + + public function setSize($width, $height) { + $this->width = $width; + $this->height = $height; + } + + /** + * Set the with of the gmap infowindow (on marker clik) + * + * @param int $infoWindowWidth GoogleMap info window width + * + * @return void + */ + + public function setInfoWindowWidth($infoWindowWidth) { + $this->infoWindowWidth = $infoWindowWidth; + } + + /** + * Set the size of the icon markers + * + * @param int $iconWidth GoogleMap marker icon width + * @param int $iconHeight GoogleMap marker icon height + * + * @return void + */ + + public function setIconSize($iconWidth, $iconHeight) { + $this->iconWidth = $iconWidth; + $this->iconHeight = $iconHeight; + } + + /** + * Set the lang of the gmap + * + * @param string $lang GoogleMap lang : fr,en,.. + * + * @return void + */ + + public function setLang($lang) { + $this->lang = $lang; + } + + /** + * Set the zoom of the gmap + * + * @param int $zoom GoogleMap zoom. + * + * @return void + */ + + public function setZoom($zoom) { + $this->zoom = $zoom; + } + + /** + * Set the zoom of the infowindow + * + * @param int $zoom GoogleMap zoom. + * + * @return void + */ + + public function setInfoWindowZoom($infoWindowZoom) { + $this->infoWindowZoom = $infoWindowZoom; + } + + /** + * Enable the zoom on the marker when you click on it + * + * @param int $zoom GoogleMap zoom. + * + * @return void + */ + + public function setEnableWindowZoom($enableWindowZoom) { + $this->enableWindowZoom = $enableWindowZoom; + } + + /** + * Enable theautomatic center/zoom at the gmap load + * + * @param int $zoom GoogleMap zoom. + * + * @return void + */ + + public function setEnableAutomaticCenterZoom($enableAutomaticCenterZoom) { + $this->enableAutomaticCenterZoom = $enableAutomaticCenterZoom; + } + + /** + * Set the center of the gmap (an address) + * + * @param string $center GoogleMap center (an address) + * + * @return void + */ + + public function setCenter($center) { + $this->center = $center; + } + + /** + * Set the type of the gmap + * + * @param string $mapType (can be 'google.maps.MapTypeId.ROADMAP', + * 'G_SATELLITE_MAP', 'G_HYBRID_MAP', 'G_PHYSICAL_MAP') + * + * @return void + */ + + public function setMapType($mapType) { + $this->mapType = $mapType; + } + + /* + Set whether or not to allow the full screen tools + */ + public function setAllowFullScreen($allowed) { + $this->allowFullScreen = $allowed; + } + + /** + * Set the center of the gmap + **/ + public function setLatLongCenter($center) { + $this->latLongCenter = $center; + } + + /** + * Decide whether or not to show direction controls + * + * @param boolean $displayDirectionFields display directions or not in the info window + * + * @return void + */ + + public function setDisplayDirectionFields($displayDirectionFields) { + $this->displayDirectionFields = $displayDirectionFields; + } + + /** + * Set the defaultHideMarker + * + * @param boolean $defaultHideMarker hide all the markers on the map by default + * + * @return void + */ + + public function setDefaultHideMarker($defaultHideMarker) { + $this->defaultHideMarker = $defaultHideMarker; + } + + /** + * Get the google map content + * + * @return string the google map html code + */ + + public function getGoogleMap() { + return $this->content; + } + + /** + * Get URL content using cURL. + * + * @param string $url the url + * + * @return string the html code + * + * @todo add proxy settings + */ + + public function getContent($url) { + $curl = curl_init(); + curl_setopt($curl, CURLOPT_TIMEOUT, 10); + curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); + curl_setopt($curl, CURLOPT_URL, $url); + $data = curl_exec($curl); + curl_close($curl); + return $data; + } + + /** + * Geocoding an address (address -> lat,lng) + * + * @param string $address an address + * + * @return array array with precision, lat & lng + */ + + public function geocoding($address) { + $encodeAddress = urlencode($address); + $url = "//maps.google.com/maps/geo?q=".$encodeAddress."&output=csv&key=".$this->googleMapKey; + + if (function_exists('curl_init')) { + $data = $this->getContent($url); + } else { + $data = file_get_contents($url); + } + + $csvSplit = preg_split("/,/", $data); + $status = $csvSplit[0]; + + if (strcmp($status, "200") == 0) { + /* + For a successful geocode: + - $precision = $csvSplit[1 + - $lat = $csvSplit[2] + - $lng = $csvSplit[3]; + */ + $return = $csvSplit; + } else { + $return = null; // failure to geocode + } + return $return; + } + + /** + * Add marker by his coord + * + * @param string $lat lat + * @param string $lng lngs + * @param string $html html code display in the info window + * @param string $category marker category + * @param string $icon an icon url + * + * @return void + */ + + public function addMarkerByCoords($lat, $lng, $html='', $category='', $icon='') { + + // Save the lat/lon to enable the automatic center/zoom + $this->maxLng = (float) max((float)$lng, $this->maxLng); + $this->minLng = (float) min((float)$lng, $this->minLng); + $this->maxLat = (float) max((float)$lat, $this->maxLat); + $this->minLat = (float) min((float)$lat, $this->minLat); + $this->centerLng = (float) ($this->minLng + $this->maxLng) / 2; + $this->centerLat = (float) ($this->minLat + $this->maxLat) / 2; + $iconURL = null; + if ($icon) { + $iconURL = $icon->getURL(); + } + + $m = array( + 'latitude' => $lat, + 'longitude' => $lng, + 'html' => $html, + 'category' => $category, + 'icon' => $iconURL + ); + array_push($this->markers, $m); + } + + /** + * Add marker by his address + * + * @param string $address an ddress + * @param string $content html code display in the info window + * @param string $category marker category + * @param string $icon an icon url + * + * @return void + */ + + public function addMarkerByAddress($address, $content='', $category='', $icon='') { + $point = $this->geocoding($address); + if ($point!==null) { + $this->addMarkerByCoords($point[2], $point[3], $content, $category, $icon); + } else { + // throw new Exception('Adress not found : '.$address); + } + } + + /** + * Add marker by an array of coord + * + * @param string $coordtab an array of lat,lng,content + * @param string $category marker category + * @param string $icon an icon url + * + * @return void + */ + + public function addArrayMarkerByCoords($coordtab, $category='', $icon='') { + foreach ($coordtab as $coord) { + $this->addMarkerByCoords($coord[0], $coord[1], $coord[2], $category, $icon); + } + } + + + /** + * Adds a {@link ViewableData} object that implements {@link Mappable} + * to the map. + * + * @param ViewableData $obj + */ + public function addMarkerAsObject(ViewableData $obj) { + $extensionsImplementMappable = false; + $extensions = Object::get_extensions(get_class($obj)); + + foreach ($extensions as $extension) { + $class = new ReflectionClass($extension); + if ($class->implementsInterface('Mappable')) { + $extensionsImplementMappable = true; + } + + } + if ($extensionsImplementMappable || + ($obj instanceof Mappable) || + (Object::has_extension($obj->ClassName, 'MapExtension')) + ) { + //if(($obj->getMappableLatitude() > 0) || ($obj->getMappableLongitude() > 0)) { + $cat = $obj->hasMethod('getMappableMapCategory') ? $obj->getMappableMapCategory() : "default"; + $this->addMarkerByCoords( + $obj->getMappableLatitude(), + $obj->getMappableLongitude(), + $obj->getMappableMapContent(), + $cat, + $obj->getMappableMapPin() + ); + } + } + + + /** + * Draws a line between two {@link ViewableData} objects + * + * @param ViewableData $one The first point + * @param ViewableData $two The second point + * @param string $color The hexidecimal color of the line + */ + public function connectPoints(ViewableData $one, ViewableData $two, $color = "#FF3300") { + $this->addLine( + array($one->getMappableLatitude(), $one->getMappableLongitude()), + array($two->getMappableLatitude(), $two->getMappableLongitude()), + $color + ); + } + + + public function forTemplate() { + $this->generate(); + return $this->getGoogleMap(); + } + + + /** + * Add marker by an array of address + * + * @param string $coordtab an array of address + * @param string $category marker category + * @param string $icon an icon url + * + * @return void + */ + + public function addArrayMarkerByAddress($coordtab, $category='', $icon='') { + foreach ($coordtab as $coord) { + $this->addMarkerByAddress($coord[0], $coord[1], $category, $icon); + } + } + + /** + * Set a direction between 2 addresss and set a text panel + * + * @param string $from an address + * @param string $to an address + * @param string $idpanel id of the div panel + * + * @return void + */ + + public function addDirection($from, $to, $idpanel='') { + $this->contentMarker .= 'addDirection("'.$from.'","'.$to.'","'.$idpanel.'");'; + } + + /** + * Parse a KML file and add markers to a category + * + * @param string $url url of the kml file compatible with gmap and gearth + * + * @return void + */ + + public function addKML($url) { + array_push($this->kmlFiles, $url); + } + + + /* + Add a line to the map + + */ + public function addLine($from = array(), $to = array(), $color = "#FF3300") { + $line = array( + 'lat1' => $from[0], + 'lon1' => $from[1], + 'lat2' => $to[0], + 'lon2' => $to[1], + 'color' => $color + ); + + array_push($this->lines, $line); + + + } + + /** + * Initialize the javascript code + * + * @return void + */ @@ -708,130 +725,128 @@ public function addLine( $from = array(), $to = array(), $color = "#FF3300" ) { For php 5.3 */ function jsonRemoveUnicodeSequences($struct) { - return preg_replace("/\\\\u([a-f0-9]{4})/e", "iconv('UCS-4LE','UTF-8',pack('V', hexdec('U$1')))", json_encode($struct)); + return preg_replace("/\\\\u([a-f0-9]{4})/e", + "iconv('UCS-4LE','UTF-8',pack('V', hexdec('U$1')))", + json_encode($struct)); +} + + + /** + * Generate the gmap + * + * @return void + */ + + public function generate() { + // from http://stackoverflow.com/questions/3586401/cant-decode-json-string-in-php + $jsonMarkers = null; + $linesJson = null; + $kmlJson = null; + + // prior to PHP version 5.4, one needs to use regex + if (PHP_VERSION_ID < 50400) { + $jsonMarkers = stripslashes($this->jsonRemoveUnicodeSequences($this->markers)); + $linesJson = stripslashes($this->jsonRemoveUnicodeSequences($this->lines)); + $kmlJson = stripslashes($this->jsonRemoveUnicodeSequences($this->kmlFiles)); + } else { + $jsonMarkers = stripslashes(json_encode($this->markers,JSON_UNESCAPED_UNICODE)); + $linesJson = stripslashes(json_encode($this->lines,JSON_UNESCAPED_UNICODE)); + $kmlJson = stripslashes(json_encode($this->kmlFiles,JSON_UNESCAPED_UNICODE)); + } + + + + // Center of the GMap + $geocodeCentre = ($this->latLongCenter) ? + $this->latLongCenter : $this->geocoding($this->center); + + // coordinates for centre depending on which method used + if ($geocodeCentre[0]=="200") { // success + $latlngCentre = array('lat'=>$geocodeCentre[2],'lng' => $geocodeCentre[3]); + } else { // Paris + $latlngCentre = array('lat'=>48.8792, 'lng' => 2.34778); + } + + $this->LatLngCentreJSON = stripslashes(json_encode($latlngCentre)); + + $lenLng = $this->maxLng - $this->minLng; + $lenLat = $this->maxLat - $this->minLat; + $this->minLng -= $lenLng * $this->coordCoef; + $this->maxLng += $lenLng * $this->coordCoef; + $this->minLat -= $lenLat * $this->coordCoef; + $this->maxLat += $lenLat * $this->coordCoef; + + // add the css class mappable as a handle onto the map styling + $this->additional_css_classes .= ' mappable'; + + if (!$this->enableAutomaticCenterZoom) { + $this->enableAutomaticCenterZoom = 'false'; + } + + if (!$this->useClusterer) { + $this->useClusterer = 'false'; + } + + if (!$this->defaultHideMarker) { + $this->defaultHideMarker = 'false'; + } + + if (!$this->MapTypeId) { + $this->MapTypeId = 'false'; + } + + // initialise full screen as the config value if not already set + if ($this->allowFullScreen === null) { + $this->allowFullScreen = Config::inst()->get('Mappable', 'allow_full_screen'); + } + + if (!$this->allowFullScreen) { + $this->allowFullScreen = 'false'; + } + + $vars = new ArrayData(array( + 'JsonMapStyles' => $this->jsonMapStyles, + 'AdditionalCssClasses' => $this->additional_css_classes, + 'Width' => $this->width, + 'Height' => $this->height, + 'InfoWindowWidth' => $this->infoWindowWidth, + 'ShowInlineMapDivStyle' => $this->show_inline_map_div_style, + 'InfoWindowZoom' => $this->infoWindowZoom, + 'EnableWindowZoom' => $this->enableWindowZoom, + 'MapMarkers' => $jsonMarkers, + 'DelayLoadMapFunction' => $this->delayLoadMapFunction, + 'DefaultHideMarker' => $this->defaultHideMarker, + 'LatLngCentre' => $this->LatLngCentreJSON, + 'EnableAutomaticCenterZoom' => $this->enableAutomaticCenterZoom, + 'Zoom' => $this->zoom, + 'MaxZoom' => $this->maxZoom, + 'GridSize' => $this->gridSize, + 'MinLng' => $this->minLng, + 'MinLat' => $this->minLat, + 'MaxLng' => $this->maxLng, + 'MaxLat' => $this->maxLat, + 'MapType' => $this->mapType, + 'GoogleMapID' => $this->googleMapId, + 'Lang'=>$this->lang, + 'UseClusterer'=>$this->useClusterer, + 'DownloadJS' => !(self::$include_download_javascript), + 'ClustererLibraryPath' => $this->clustererLibraryPath, + 'Lines' => $linesJson, + 'KmlFiles' => $kmlJson, + 'AllowFullScreen' => $this->allowFullScreen + ) + ); + + $this->content = $this->processTemplate('Map', $vars); + } + + + function processTemplate($templateName, $templateVariables = null ) { + if (!$templateVariables) { + $templateVariables = new ArrayList(); + } + + $result = $templateVariables->renderWith($templateName.$this->mappingService); + return $result; + } } - - - /** - * Generate the gmap - * - * @return void - */ - - public function generate() { - // from http://stackoverflow.com/questions/3586401/cant-decode-json-string-in-php - $jsonMarkers = null; - $linesJson = null; - $kmlJson = null; - - // prior to PHP version 5.4, one needs to use regex - if (PHP_VERSION_ID < 50400) { - $jsonMarkers = stripslashes($this->jsonRemoveUnicodeSequences($this->markers)); - $linesJson = stripslashes($this->jsonRemoveUnicodeSequences($this->lines)); - $kmlJson = stripslashes($this->jsonRemoveUnicodeSequences($this->kmlFiles)); - } else { - $jsonMarkers = stripslashes(json_encode($this->markers,JSON_UNESCAPED_UNICODE)); - $linesJson = stripslashes(json_encode($this->lines,JSON_UNESCAPED_UNICODE)); - $kmlJson = stripslashes(json_encode($this->kmlFiles,JSON_UNESCAPED_UNICODE)); - } - - - - // Center of the GMap - $geocodeCentre = ( $this->latLongCenter ) ? $this->latLongCenter : $this->geocoding( $this->center ); - - // coordinates for centre depending on which method used - if ( $geocodeCentre[0]=="200" ) { // success - $latlngCentre = array('lat'=>$geocodeCentre[2],'lng' => $geocodeCentre[3]); - } else { // Paris - $latlngCentre = array('lat'=>48.8792, 'lng' => 2.34778); - } - - $this->LatLngCentreJSON = stripslashes(json_encode($latlngCentre)); - - $lenLng = $this->maxLng - $this->minLng; - $lenLat = $this->maxLat - $this->minLat; - $this->minLng -= $lenLng * $this->coordCoef; - $this->maxLng += $lenLng * $this->coordCoef; - $this->minLat -= $lenLat * $this->coordCoef; - $this->maxLat += $lenLat * $this->coordCoef; - - // add the css class mappable as a handle onto the map styling - $this->additional_css_classes .= ' mappable'; - - if (!$this->enableAutomaticCenterZoom) { - $this->enableAutomaticCenterZoom = 'false'; - } - - if (!$this->useClusterer) { - $this->useClusterer = 'false'; - } - - if (!$this->defaultHideMarker) { - $this->defaultHideMarker = 'false'; - } - - if (!$this->MapTypeId) { - $this->MapTypeId = 'false'; - } - - // initialise full screen as the config value if not already set - if ($this->allowFullScreen === null) { - $this->allowFullScreen = Config::inst()->get('Mappable', 'allow_full_screen'); - } - - if (!$this->allowFullScreen) { - $this->allowFullScreen = 'false'; - } - - $vars = new ArrayData(array( - 'JsonMapStyles' => $this->jsonMapStyles, - 'AdditionalCssClasses' => $this->additional_css_classes, - 'Width' => $this->width, - 'Height' => $this->height, - 'InfoWindowWidth' => $this->infoWindowWidth, - 'ShowInlineMapDivStyle' => $this->show_inline_map_div_style, - 'InfoWindowZoom' => $this->infoWindowZoom, - 'EnableWindowZoom' => $this->enableWindowZoom, - 'MapMarkers' => $jsonMarkers, - 'DelayLoadMapFunction' => $this->delayLoadMapFunction, - 'DefaultHideMarker' => $this->defaultHideMarker, - 'LatLngCentre' => $this->LatLngCentreJSON, - 'EnableAutomaticCenterZoom' => $this->enableAutomaticCenterZoom, - 'Zoom' => $this->zoom, - 'MaxZoom' => $this->maxZoom, - 'GridSize' => $this->gridSize, - 'MinLng' => $this->minLng, - 'MinLat' => $this->minLat, - 'MaxLng' => $this->maxLng, - 'MaxLat' => $this->maxLat, - 'MapType' => $this->mapType, - 'GoogleMapID' => $this->googleMapId, - 'Lang'=>$this->lang, - 'UseClusterer'=>$this->useClusterer, - 'DownloadJS' => !(self::$includeDownloadJavascript), - 'ClustererLibraryPath' => $this->clustererLibraryPath, - 'Lines' => $linesJson, - 'KmlFiles' => $kmlJson, - 'AllowFullScreen' => $this->allowFullScreen - ) - ); - - $this->content = $this->processTemplate('Map', $vars); - } - - - function processTemplate( $templateName, $templateVariables = null ) { - if (!$templateVariables) { - $templateVariables = new ArrayList(); - } - // Requirements::javascriptTemplate( MAPPABLE_MODULE_PATH.'/javascript/'.$this->mappingService.'/'.$templateName.'.ss', $templateVariables ); - $result = $templateVariables->renderWith($templateName.$this->mappingService ); - return $result; - } - - - - - -} \ No newline at end of file diff --git a/code/MapExtension.php b/code/MapExtension.php index 1ddffa8..d911944 100644 --- a/code/MapExtension.php +++ b/code/MapExtension.php @@ -7,167 +7,171 @@ class MapExtension extends DataExtension implements Mappable { */ private static $map_info_window_suffix = '_MapInfoWindow'; - static $db = array( - 'Lat' => 'Decimal(18,15)', - 'Lon' => 'Decimal(18,15)', - 'ZoomLevel' => 'Int', - 'MapPinEdited' => 'Boolean' - ); - - static $has_one = array( - 'MapPinIcon' => 'Image' - ); - - - static $defaults = array ( - 'Lat' =>0, - 'Lon' => 0, - 'Zoom' => 4, - 'MapPinEdited' => false - ); - - - /* - Add a Location tab containing the map - */ - public function updateCMSFields( FieldList $fields ) { - // These fields need removed, as they may have already been created by the form scaffolding - $fields->removeByName('Lat'); - $fields->removeByName('Lon'); - $fields->removeByName('ZoomLevel'); - $fields->removeByName('MapPinIcon'); - $fields->removeByName('MapPinEdited'); - - $fields->addFieldToTab( "Root.Location", new LatLongField( array( - new TextField( 'Lat', 'Latitude' ), - new TextField( 'Lon', 'Longitude' ), - new TextField( 'ZoomLevel', 'Zoom' ) - ), - array( 'Address' ) - ) - ); - - $fields->addFieldToTab( 'Root.Location', $uf = new UploadField('MapPinIcon', _t('Mappable.MAP_PIN', 'Map Pin Icon. Leave this blank for default pin to show'))); - $uf->setFolderName('mapicons'); - } - - - public function getMappableLatitude() { - return $this->owner->Lat; - } - - public function getMappableLongitude() { - return $this->owner->Lon; - } + private static $db = array( + 'Lat' => 'Decimal(18,15)', + 'Lon' => 'Decimal(18,15)', + 'ZoomLevel' => 'Int', + 'MapPinEdited' => 'Boolean' + ); + + static $has_one = array( + 'MapPinIcon' => 'Image' + ); + + static $defaults = array ( + 'Lat' =>0, + 'Lon' => 0, + 'Zoom' => 4, + 'MapPinEdited' => false + ); + + + /* + Add a Location tab containing the map + */ + public function updateCMSFields(FieldList $fields) { + // These fields need removed, as they may have already been created by the form scaffolding + $fields->removeByName('Lat'); + $fields->removeByName('Lon'); + $fields->removeByName('ZoomLevel'); + $fields->removeByName('MapPinIcon'); + $fields->removeByName('MapPinEdited'); + + $fields->addFieldToTab("Root.Location", + new LatLongField(array( + new TextField('Lat', 'Latitude'), + new TextField('Lon', 'Longitude'), + new TextField('ZoomLevel', 'Zoom') + ), + array('Address') + ) + ); + + $fields->addFieldToTab('Root.Location', $uf = new UploadField('MapPinIcon', + _t('Mappable.MAP_PIN', 'Map Pin Icon. Leave this blank for default pin to show'))); + $uf->setFolderName('mapicons'); + } + + + public function getMappableLatitude() { + return $this->owner->Lat; + } + + public function getMappableLongitude() { + return $this->owner->Lon; + } /** * Renders the map info window for the DataObject. * - * Be sure to define a template for that, named by the decorated class suffixed with _MapInfoWindow, + * Be sure to define a template for that, named by the decorated class suffixed with _MapInfoWindow * e.g. MyPage_MapInfoWindow * - * You can change the suffix globally by editing the MapExtension.map_info_window_suffix config value. + * You can change the suffix globally by editing the MapExtension.map_info_window_suffix config val * * @return string */ public function getMappableMapContent() { $defaultTemplate = 'MapInfoWindow'; - $classTemplate = SSViewer::get_templates_by_class($this->owner->ClassName, Config::inst()->get('MapExtension', 'map_info_window_suffix')); - $template = count($classTemplate) ? $classTemplate : $defaultTemplate; - return MapUtil::sanitize($this->owner->renderWith($template)); - } - - /* - If the marker pin is not at position 0,0 mark the pin as edited. This provides the option of - filtering out (0,0) point which is often irrelevant for plots - */ - public function onBeforeWrite() { - if (($this->owner->Lat !== 0) || ($this->owner->Lon !== 0)) { - $this->owner->MapPinEdited = true; - } - } - - /* - If a user has uploaded a map pin icon display that, otherwise - */ - public function getMappableMapPin() { - $result = false; - if ($this->owner->MapPinIconID != 0) { - $mappin = $this->owner->MapPinIcon(); - $result = $mappin->getAbsoluteURL(); - } else { - // check for a cached map pin already having been provided - if ($this->owner->CachedMapPin) { - $result = $this->owner->CachedMapPin; - } - } - return $result; - } - - /* - Check for non zero coordinates, on the assumption that (0,0) will never be the desired coordinates - */ - public function HasGeo() { - $result = ($this->owner->Lat != 0) && ($this->owner->Lon != 0); - if ($this->owner->hasExtension('MapLayerExtension')) { - if ($this->owner->MapLayers()->count() > 0) { - $result = true; - } - } - - if ($this->owner->hasExtension('PointsOfInterestLayerExtension')) { - if ($this->owner->PointsOfInterestLayers()->count() > 0) { - $result = true; - } - } - return $result; - } - - - /* - Render a map at the provided lat,lon, zoom from the editing functions, - */ - public function BasicMap() { - $map = $this->owner->getRenderableMap(); - // $map->setDelayLoadMapFunction( true ); - $map->setZoom( $this->owner->ZoomLevel ); - $map->setAdditionalCSSClasses( 'fullWidthMap' ); - $map->setShowInlineMapDivStyle( true ); - - // add any KML map layers - if (Object::has_extension($this->owner->ClassName, 'MapLayerExtension')) { - foreach($this->owner->MapLayers() as $layer) { - $map->addKML($layer->KmlFile()->getAbsoluteURL()); - } - $map->setEnableAutomaticCenterZoom(true); - } - - // add points of interest taking into account the default icon of the layer as an override - if (Object::has_extension($this->owner->ClassName, 'PointsOfInterestLayerExtension')) { - foreach($this->owner->PointsOfInterestLayers() as $layer) { - $layericon = $layer->DefaultIcon(); - if ($layericon->ID === 0) { - $layericon = null; - } - foreach ($layer->PointsOfInterest() as $poi) { - if ($poi->MapPinEdited) { - if ($poi->MapPinIconID == 0) { - $poi->CachedMapPin = $layericon; - } - $map->addMarkerAsObject($poi); - } - } - } - $map->setClusterer( true ); - $map->setEnableAutomaticCenterZoom(true); - } - - $map->setZoom( 10 ); - $map->setAdditionalCSSClasses( 'fullWidthMap' ); - $map->setShowInlineMapDivStyle( true ); - $map->setClusterer(true); - - return $map; - } + $classTemplate = + SSViewer::get_templates_by_class( + $this->owner->ClassName, + Config::inst()->get('MapExtension', 'map_info_window_suffix') + ); + $template = count($classTemplate) ? $classTemplate : $defaultTemplate; + return MapUtil::sanitize($this->owner->renderWith($template)); + } + + /* + If the marker pin is not at position 0,0 mark the pin as edited. This provides the option of + filtering out (0,0) point which is often irrelevant for plots + */ + public function onBeforeWrite() { + if (($this->owner->Lat !== 0) || ($this->owner->Lon !== 0)) { + $this->owner->MapPinEdited = true; + } + } + + /* + If a user has uploaded a map pin icon display that, otherwise + */ + public function getMappableMapPin() { + $result = false; + if ($this->owner->MapPinIconID != 0) { + $mappin = $this->owner->MapPinIcon(); + $result = $mappin->getAbsoluteURL(); + } else { + // check for a cached map pin already having been provided + if ($this->owner->CachedMapPin) { + $result = $this->owner->CachedMapPin; + } + } + return $result; + } + + /* + Check for non zero coordinates, on the assumption that (0,0) will never be the desired coordinates + */ + public function HasGeo() { + $result = ($this->owner->Lat != 0) && ($this->owner->Lon != 0); + if ($this->owner->hasExtension('MapLayerExtension')) { + if ($this->owner->MapLayers()->count() > 0) { + $result = true; + } + } + + if ($this->owner->hasExtension('PointsOfInterestLayerExtension')) { + if ($this->owner->PointsOfInterestLayers()->count() > 0) { + $result = true; + } + } + return $result; + } + + + /* + Render a map at the provided lat,lon, zoom from the editing functions, + */ + public function BasicMap() { + $map = $this->owner->getRenderableMap(); + $map->setZoom($this->owner->ZoomLevel); + $map->setAdditionalCSSClasses('fullWidthMap'); + $map->setShowInlineMapDivStyle(true); + + // add any KML map layers + if (Object::has_extension($this->owner->ClassName, 'MapLayerExtension')) { + foreach($this->owner->MapLayers() as $layer) { + $map->addKML($layer->KmlFile()->getAbsoluteURL()); + } + $map->setEnableAutomaticCenterZoom(true); + } + + // add points of interest taking into account the default icon of the layer as an override + if (Object::has_extension($this->owner->ClassName, 'PointsOfInterestLayerExtension')) { + foreach($this->owner->PointsOfInterestLayers() as $layer) { + $layericon = $layer->DefaultIcon(); + if ($layericon->ID === 0) { + $layericon = null; + } + foreach ($layer->PointsOfInterest() as $poi) { + if ($poi->MapPinEdited) { + if ($poi->MapPinIconID == 0) { + $poi->CachedMapPin = $layericon; + } + $map->addMarkerAsObject($poi); + } + } + } + $map->setClusterer(true); + $map->setEnableAutomaticCenterZoom(true); + } + + $map->setZoom(10); + $map->setAdditionalCSSClasses('fullWidthMap'); + $map->setShowInlineMapDivStyle(true); + $map->setClusterer(true); + + return $map; + } } diff --git a/code/MapField.php b/code/MapField.php index 0b4bb69..797b569 100755 --- a/code/MapField.php +++ b/code/MapField.php @@ -3,57 +3,58 @@ * Map field to point for pois langitude and longitude positioning */ class MapField extends DatalessField { - - /** - * @var int $headingLevel The level of the

to

HTML tag. Default: 2 - */ - protected $headingLevel = 2; - private $divId; - - function __construct($name, $title = null, $headingLevel = 2, $allowHTML = false, $form = null) { - $this->divId = $name; - // legacy handling for old parameters: $title, $heading, ... - // instead of new handling: $name, $title, $heading, ... - $args = func_get_args(); - if(!isset($args[1]) || is_numeric($args[1])) { - $title = (isset($args[0])) ? $args[0] : null; - // Use "HeaderField(title)" as the default field name for a HeaderField; if it's just set to title then we risk - // causing accidental duplicate-field creation. - $name = 'MapField' . $title; // this means i18nized fields won't be easily accessible through fieldByName() - $headingLevel = (isset($args[1])) ? $args[1] : null; - $allowHTML = (isset($args[2])) ? $args[2] : null; - $form = (isset($args[3])) ? $args[3] : null; - } - - if($headingLevel) $this->headingLevel = $headingLevel; - $this->allowHTML = $allowHTML; - parent::__construct($name, $title, null, $allowHTML, $form); - } - - function Field($properties = array()) { - Requirements::javascript('framework/thirdparty/jquery/jquery.js'); - Requirements::javascript('framework/thirdparty/jquery-livequery/jquery.livequery.js'); - // Requirements::javascript('http://maps.google.com/maps/api/js?sensor=false'); - - - // Requirements::javascript('mappable/javascript/mapField.js'); - - - - - - $attributes = array( - 'class' => 'middleColumn', - 'id' => $this->divId, - 'style' => "width:100%;height:300px;margin:5px 0px 5px 5px;position:relative;" - ); - - Requirements::css('mappable/css/mapField.css'); - - return '
' . $this->createTag( - "div", - $attributes - ) . '
'; - } + + /** + * @var int $headingLevel The level of the

to

HTML tag. Default: 2 + */ + protected $headingLevel = 2; + private $divId; + + function __construct($name, $title = null, $headingLevel = 2, $allowHTML = false, $form = null) { + $this->divId = $name; + // legacy handling for old parameters: $title, $heading, ... + // instead of new handling: $name, $title, $heading, ... + $args = func_get_args(); + if(!isset($args[1]) || is_numeric($args[1])) { + $title = (isset($args[0])) ? $args[0] : null; + // Use "HeaderField(title)" as the default field name for a HeaderField; + // if it's just set to title then we risk causing accidental duplicate-field creation. + + // this means i18nized fields won't be easily accessible through fieldByName() + $name = 'MapField' . $title; + $headingLevel = (isset($args[1])) ? $args[1] : null; + $allowHTML = (isset($args[2])) ? $args[2] : null; + $form = (isset($args[3])) ? $args[3] : null; + } + + if($headingLevel) $this->headingLevel = $headingLevel; + $this->allowHTML = $allowHTML; + parent::__construct($name, $title, null, $allowHTML, $form); + } + + function Field($properties = array()) { + Requirements::javascript('framework/thirdparty/jquery/jquery.js'); + Requirements::javascript('framework/thirdparty/jquery-livequery/jquery.livequery.js'); + // Requirements::javascript('http://maps.google.com/maps/api/js?sensor=false'); + + + // Requirements::javascript('mappable/javascript/mapField.js'); + + + + + + $attributes = array( + 'class' => 'middleColumn', + 'id' => $this->divId, + 'style' => "width:100%;height:300px;margin:5px 0px 5px 5px;position:relative;" + ); + + Requirements::css('mappable/css/mapField.css'); + + return '
' . $this->createTag( + "div", + $attributes + ) . '
'; + } } -?> \ No newline at end of file diff --git a/code/MapLayer.php b/code/MapLayer.php index 9c7aef1..bcdcc9a 100644 --- a/code/MapLayer.php +++ b/code/MapLayer.php @@ -1,43 +1,23 @@ 'Varchar(255)' + ); - static $db = array( - 'Title' => 'Varchar(255)' - ); + static $has_one = array( + 'KmlFile' => 'File' + ); - static $has_one = array( - 'KmlFile' => 'File' - ); + function getCMSFields_forPopup() { + $fields = new FieldSet(); + $fields->push(new TextField('Title')); + $fields->push(new FileIFrameField('KmlFile')); - - - -/* - - static $belongs_many_many = array( - 'Articles' => 'Article', - 'FlickrSetPages' => 'FlickrSetPage' - ); -*/ - - - - - - function getCMSFields_forPopup() { - $fields = new FieldSet(); - - $fields->push( new TextField( 'Title' ) ); - $fields->push( new FileIFrameField( 'KmlFile') ); - - return $fields; - } + return $fields; + } } - - -?> \ No newline at end of file diff --git a/code/MapLayerExtension.php b/code/MapLayerExtension.php index 341e3ab..9a365d7 100644 --- a/code/MapLayerExtension.php +++ b/code/MapLayerExtension.php @@ -2,22 +2,28 @@ class MapLayerExtension extends DataExtension { - static $many_many = array( - 'MapLayers' => 'MapLayer' - ); + static $many_many = array( + 'MapLayers' => 'MapLayer' + ); - static $belongs_many_many_extraFields = array( - 'MapLayers' => array( - 'SortOrder' => "Int" - ) - ); + static $belongs_many_many_extraFields = array( + 'MapLayers' => array( + 'SortOrder' => "Int" + ) + ); - - public function updateCMSFields( FieldList $fields ) { - $gridConfig2 = GridFieldConfig_RelationEditor::create(); - $gridConfig2->getComponentByType( 'GridFieldAddExistingAutocompleter' )->setSearchFields( array( 'Title' ) ); - $gridConfig2->getComponentByType( 'GridFieldPaginator' )->setItemsPerPage( 100 ); - $gridField2 = new GridField( "Map Layers", "Map Layers:", $this->owner->MapLayers(), $gridConfig2 ); - $fields->addFieldToTab( "Root.MapLayers", $gridField2 ); - } -} \ No newline at end of file + public function updateCMSFields(FieldList $fields) { + $gridConfig2 = GridFieldConfig_RelationEditor::create(); + $gridConfig2->getComponentByType( + 'GridFieldAddExistingAutocompleter')-> + setSearchFields(array('Title') + ); + $gridConfig2->getComponentByType('GridFieldPaginator')->setItemsPerPage(100); + $gridField2 = new GridField("Map Layers", + "Map Layers:", + $this->owner->MapLayers(), + $gridConfig2 + ); + $fields->addFieldToTab("Root.MapLayers", $gridField2); + } +} diff --git a/code/MapMarkerSetsExtension.php b/code/MapMarkerSetsExtension.php index 3ad52ad..1486656 100644 --- a/code/MapMarkerSetsExtension.php +++ b/code/MapMarkerSetsExtension.php @@ -2,25 +2,31 @@ class MapMarkerSetsExtension extends DataExtension { - static $many_many = array( - 'MapMarkerSets' => 'MapMarkerSet' - ); - - - static $belongs_many_many_extraFields = array( - 'MapMarkerSets' => array( - 'SortOrder' => "Int" - ) - ); - - - public function updateCMSFields( FieldList $fields ) { - $gridConfig2 = GridFieldConfig_RelationEditor::create(); - $gridConfig2->getComponentByType( 'GridFieldAddExistingAutocompleter' )->setSearchFields( array( 'Title' ) ); - $gridConfig2->getComponentByType( 'GridFieldPaginator' )->setItemsPerPage( 100 ); - - $gridField2 = new GridField( "MapMarkerSets", "MapMarkers", $this->owner->MapMarkerSets(), $gridConfig2 ); - $fields->addFieldToTab( "Root.MapMarkerSets", $gridField2 ); - } - -} \ No newline at end of file + static $many_many = array( + 'MapMarkerSets' => 'MapMarkerSet' + ); + + + static $belongs_many_many_extraFields = array( + 'MapMarkerSets' => array( + 'SortOrder' => "Int" + ) + ); + + + public function updateCMSFields(FieldList $fields) { + $gridConfig2 = GridFieldConfig_RelationEditor::create(); + $gridConfig2->getComponentByType( + 'GridFieldAddExistingAutocompleter')->setSearchFields(array('Title') + ); + $gridConfig2->getComponentByType('GridFieldPaginator')->setItemsPerPage(100); + + $gridField2 = new GridField("MapMarkerSets", + "MapMarkers", + $this->owner->MapMarkerSets(), + $gridConfig2 + ); + $fields->addFieldToTab("Root.MapMarkerSets", $gridField2); + } + +} diff --git a/code/MapUtil.php b/code/MapUtil.php index 8a0b6a3..5a3eff2 100644 --- a/code/MapUtil.php +++ b/code/MapUtil.php @@ -7,7 +7,7 @@ class MapUtil * @var string The Google Maps API key */ protected static $api_key; - + /** * @var int Number of active {@see GoogleMapsAPI} instances (for the HTML ID) */ @@ -17,7 +17,7 @@ class MapUtil * @var int The default width of a Google Map */ public static $map_width = '100%'; - + /** * @var int The default height of a Google Map */ @@ -28,22 +28,22 @@ class MapUtil /** @var int Icon height of the gmarker **/ public static $iconHeight = 24; - + /** * @var int Prefix for the div ID of the map */ public static $div_id = "google_map"; - + /** * @var boolean Automatic center/zoom for the map */ public static $automatic_center = true; - + /** * @var boolean Show directions fields on the map */ public static $direction_fields = false; - + /** * @var boolean Show the marker fields on the map */ @@ -67,12 +67,12 @@ class MapUtil /* Whether or not to allow full screen */ private static $allow_full_screen = null; - - + + /** * Set the API key for Google Maps * - * @param string $key + * @param string $key */ public static function set_api_key($key) { self::$api_key = $key; @@ -86,8 +86,8 @@ public static function set_map_already_rendered($new_map_already_rendered) { public static function get_map_already_rendered() { return self::$map_already_rendered; } - - + + /** * Set the default size of the map * @@ -98,11 +98,12 @@ public static function set_map_size($width, $height) { self:: $map_width = $width; self::$map_height = $height; } - + /** * Set the type of the gmap * - * @param string $mapType ( can be 'google.maps.MapTypeId.ROADMAP', 'G_SATELLITE_MAP', 'G_HYBRID_MAP', 'G_PHYSICAL_MAP') + * @param string $mapType (can be 'google.maps.MapTypeId.ROADMAP', 'G_SATELLITE_MAP', + * 'G_HYBRID_MAP', 'G_PHYSICAL_MAP') * * @return void */ @@ -175,7 +176,7 @@ public static function instance() $url = str_replace('https://', '', $url); $parts = explode('/', $url); $host = $parts[0]; - + $key = self::$api_key; // if an array, get the key by an array keyed by host @@ -208,13 +209,14 @@ public static function instance() * @return string */ public static function sanitize($content) { - return addslashes(str_replace(array("\n","\r"),array("",""),$content)); + return addslashes(str_replace(array("\n","\r"),array("",""),$content)); } - - + + /** * Creates a new {@link GoogleMapsAPI} object loaded with the default settings - * and places all of the items in a {@link SS_List}, e.g. {@link DataList} or {@link ArrayList} on the map + * and places all of the items in a {@link SS_List} + * e.g. {@link DataList} or {@link ArrayList} on the map * * @param SS_List $set * @return MapAPI @@ -229,6 +231,6 @@ public static function get_map(SS_List $list) { } } } - return $gmap; - } -} \ No newline at end of file + return $gmap; + } +} diff --git a/code/Mappable.php b/code/Mappable.php index f98aa34..ae34d4f 100755 --- a/code/Mappable.php +++ b/code/Mappable.php @@ -19,7 +19,7 @@ interface Mappable { * @return string */ public function getMappableLatitude(); - + /** * An accessor method for the longitude field. @@ -31,10 +31,10 @@ public function getMappableLatitude(); * @return string */ public function getMappableLongitude(); - - + + /** - * An accessor method for the path to the marker pin for this point on the map. + * An accessor method for the path to the marker pin for this point on the map. * If null or false, use the default Google Maps icon. * @example * @@ -42,10 +42,10 @@ public function getMappableLongitude(); * * * @return string - */ + */ public function getMappableMapPin(); - - + + /** * An accessor method that returns the content for the map bubble popup. * It is best to use the {@see ViewableData::renderWith()} method to take advantaging @@ -60,7 +60,7 @@ public function getMappableMapPin(); * * * @return string - */ + */ public function getMappableMapContent(); -} \ No newline at end of file +} diff --git a/code/MappableData.php b/code/MappableData.php index 12726b3..07894c6 100644 --- a/code/MappableData.php +++ b/code/MappableData.php @@ -29,13 +29,15 @@ public function getRenderableMap($width = null, $height = null, $zoom = 9) { return $gmap; } - public function StaticMap($width = null, $height = null) { + public function StaticMap($width = null, $height = null) { $w = $width ? $width : MapUtil::$map_width; $h = $height ? $height : MapUtil::$map_height; - $lat = $this->owner->getMappableLatitude(); - $lng = $this->owner->getMappableLongitude(); - $src = htmlentities("//maps.google.com/maps/api/staticmap?center=$lat,$lng&markers=$lat,$lng&zoom=13&size=${w}x$h&sensor=false"); - return ''.$this->owner->Title.''; + $lat = $this->owner->getMappableLatitude(); + $lng = $this->owner->getMappableLongitude(); + $url = "//maps.google.com/maps/api/staticmap?center=$lat,$lng&markers=$lat,". + "$lng&zoom=13&size=${w}x$h&sensor=false"; + $src = htmlentities($url); + return ''.$this->owner->Title.''; } -} \ No newline at end of file +} diff --git a/code/MappableDataObjectSet.php b/code/MappableDataObjectSet.php index 3c794db..db38c93 100755 --- a/code/MappableDataObjectSet.php +++ b/code/MappableDataObjectSet.php @@ -15,6 +15,6 @@ public function getRenderableMap($width = null, $height = null) { $gmap->setSize($w,$h); return $gmap; } - - -} \ No newline at end of file + + +} diff --git a/code/OSMPointOfInterestExtension.php b/code/OSMPointOfInterestExtension.php index 55724d3..bd0b213 100644 --- a/code/OSMPointOfInterestExtension.php +++ b/code/OSMPointOfInterestExtension.php @@ -1,4 +1,4 @@ - true - ); + ); /* The openstreetmap id is only for scripting purposes */ - public function updateCMSFields( FieldList $fields ) { + public function updateCMSFields(FieldList $fields) { $osmid = $fields->removeByName('OpenStreetMapID'); } -} \ No newline at end of file +} diff --git a/code/POIMapPage.php b/code/POIMapPage.php index a900a18..95ab2a3 100644 --- a/code/POIMapPage.php +++ b/code/POIMapPage.php @@ -2,12 +2,12 @@ class POIMapPage extends Page { function getCMSFields() { - $fields = parent::getCMSFields(); - $fields->removeByName('Location'); - return $fields; - } + $fields = parent::getCMSFields(); + $fields->removeByName('Location'); + return $fields; + } } class POIMapPage_Controller extends Page_Controller { -} \ No newline at end of file +} diff --git a/code/PointOfInterest.php b/code/PointOfInterest.php index c0c26ff..9ccd365 100644 --- a/code/PointOfInterest.php +++ b/code/PointOfInterest.php @@ -1,4 +1,4 @@ -addFieldToTab( 'Root.Main', new TextField('Name', 'Name of the item on the map')); + $fields->addFieldToTab('Root.Main', new TextField('Name', 'Name of the item on the map')); return $fields; } -} \ No newline at end of file +} diff --git a/code/PointsOfInterestAdmin.php b/code/PointsOfInterestAdmin.php index 6a4da22..b12c50e 100644 --- a/code/PointsOfInterestAdmin.php +++ b/code/PointsOfInterestAdmin.php @@ -1,11 +1,11 @@ - 'Image' ); -} \ No newline at end of file +} diff --git a/code/PointsOfInterestLayer.php b/code/PointsOfInterestLayer.php index 00b92c9..504ccfa 100644 --- a/code/PointsOfInterestLayer.php +++ b/code/PointsOfInterestLayer.php @@ -1,7 +1,7 @@ - 'Varchar'); - + private static $many_many = array('PointsOfInterest' => 'PointOfInterest'); static $has_one = array( @@ -10,9 +10,13 @@ class PointsOfInterestLayer extends DataObject { function getCMSFields() { $fields = parent::getCMSFields(); - $fields->addFieldToTab( 'Root.Main', new TextField('Name', 'Name of this layer')); - $fields->addFieldToTab( 'Root.Main', $uf = new UploadField('DefaultIcon', _t('PointsOfInterest.ICON', 'Default Icon'))); + $fields->addFieldToTab('Root.Main', new TextField('Name', 'Name of this layer')); + $fields->addFieldToTab('Root.Main', + $uf = new UploadField('DefaultIcon', + _t('PointsOfInterest.ICON', + 'Default Icon')) + ); $uf->setFolderName('mapicons'); return $fields; } -} \ No newline at end of file +} diff --git a/code/PointsOfInterestLayerExtension.php b/code/PointsOfInterestLayerExtension.php index f1fc83d..c37d07b 100644 --- a/code/PointsOfInterestLayerExtension.php +++ b/code/PointsOfInterestLayerExtension.php @@ -2,23 +2,29 @@ class PointsOfInterestLayerExtension extends DataExtension { - static $many_many = array( - 'PointsOfInterestLayers' => 'PointsOfInterestLayer' - ); + static $many_many = array( + 'PointsOfInterestLayers' => 'PointsOfInterestLayer' + ); - static $belongs_many_many_extraFields = array( - 'PointsOfInterestLayers' => array( - 'SortOrder' => "Int" - ) - ); + static $belongs_many_many_extraFields = array( + 'PointsOfInterestLayers' => array( + 'SortOrder' => "Int" + ) + ); - public function updateCMSFields( FieldList $fields ) { - $gridConfig2 = GridFieldConfig_RelationEditor::create(); - $gridConfig2->getComponentByType( 'GridFieldAddExistingAutocompleter' )->setSearchFields( array( 'Name' ) ); - $gridConfig2->getComponentByType( 'GridFieldPaginator' )->setItemsPerPage( 100 ); - $gridField2 = new GridField( "POI Layers", "POI Layers:", $this->owner->PointsOfInterestLayers(), $gridConfig2 ); - $fields->addFieldToTab( "Root.MapLayers", $gridField2 ); - } + public function updateCMSFields(FieldList $fields) { + $gridConfig2 = GridFieldConfig_RelationEditor::create(); + $gridConfig2->getComponentByType( + 'GridFieldAddExistingAutocompleter')-> + setSearchFields(array('Name') + ); + $gridConfig2->getComponentByType('GridFieldPaginator')->setItemsPerPage(100); + $gridField2 = new GridField("POI Layers", "POI Layers:", + $this->owner->PointsOfInterestLayers(), + $gridConfig2 + ); + $fields->addFieldToTab("Root.MapLayers", $gridField2); + } -} \ No newline at end of file +} diff --git a/code/shortcodes/GoogleMapShortCodeHandler.php b/code/shortcodes/GoogleMapShortCodeHandler.php index 822febd..4bcb062 100644 --- a/code/shortcodes/GoogleMapShortCodeHandler.php +++ b/code/shortcodes/GoogleMapShortCodeHandler.php @@ -5,7 +5,7 @@ class GoogleMapShortCodeHandler { /* Counter used to ensure unique div ids to allow for multiple maps on on page */ private static $gsv_ctr = 1; - public static function parse_googlemap( $arguments, $caption = null, $parser = null ) { + public static function parse_googlemap($arguments, $caption = null, $parser = null) { // each of latitude and longitude are required at a bare minimum if(!isset($arguments['latitude'])){ return ''; @@ -20,7 +20,7 @@ public static function parse_googlemap( $arguments, $caption = null, $parser = n $defaults = array( 'Zoom' => 5, 'MapType' => 'ROAD' - ); + ); // ensure JavaScript for the map service is only downloaded once $arguments['DownloadJS'] = !MapUtil::get_map_already_rendered(); @@ -52,7 +52,7 @@ public static function parse_googlemap( $arguments, $caption = null, $parser = n case 'terrain': $arguments['MapType'] = 'TERRAIN'; break; - + default: $arguments['MapType'] = 'ROAD'; break; @@ -63,7 +63,7 @@ public static function parse_googlemap( $arguments, $caption = null, $parser = n if (isset($arguments['zoom'])) { $arguments['Zoom'] = $arguments['zoom']; } - + // the id of the dom element to be used to render the street view $arguments['DomID'] = 'google_sc_map_'.self::$gsv_ctr; @@ -77,9 +77,9 @@ public static function parse_googlemap( $arguments, $caption = null, $parser = n $customised = array_merge($defaults, $arguments); //get streetview template template - $template = new SSViewer( 'GoogleMapShortCode' ); + $template = new SSViewer('GoogleMapShortCode'); //return the template customised with the parmameters - return $template->process( new ArrayData( $customised ) ); + return $template->process(new ArrayData($customised)); } -} \ No newline at end of file +} diff --git a/code/shortcodes/GoogleStreetViewShortCodeHandler.php b/code/shortcodes/GoogleStreetViewShortCodeHandler.php index 93c5d75..5b2d045 100644 --- a/code/shortcodes/GoogleStreetViewShortCodeHandler.php +++ b/code/shortcodes/GoogleStreetViewShortCodeHandler.php @@ -2,67 +2,67 @@ class GoogleStreetViewShortCodeHandler { - /* Counter used to ensure unique div ids to allow for multiple StreetViews on on page */ - private static $gsv_ctr = 1; - - // taken from http://www.ssbits.com/tutorials/2010/2-4-using-short-codes-to-embed-a-youtube-video/ and adapted for SS3 - public static function parse_googlestreetview( $arguments, $caption = null, $parser = null ) { - // each of latitude, longitude and heading are required at a bare minimum - if(!isset($arguments['latitude'])){ - return ''; - } - - if(!isset($arguments['longitude'])){ - return ''; - } - - if(!isset($arguments['heading'])){ - return ''; - } - - // defaults - these can be overriden by using zoom and pitch in the shortcode - $defaults = array( - 'Zoom' => 1, - 'Pitch' => 0 - ); - - // ensure JavaScript for the map service is only downloaded once - $arguments['DownloadJS'] = !MapUtil::get_map_already_rendered(); - MapUtil::set_map_already_rendered(true); - - // convert parameters to CamelCase as per standard template conventions - $arguments['Latitude'] = $arguments['latitude']; - $arguments['Longitude'] = $arguments['longitude']; - $arguments['Heading'] = $arguments['heading']; - - // optional parameter caption - if (isset($arguments['caption'])) { - $arguments['Caption'] = $arguments['caption']; - } - - // optional parameter pitch - if (isset($arguments['pitch'])) { - $arguments['Pitch'] = $arguments['pitch']; - } - - // optional parameter zoom - if (isset($arguments['zoom'])) { - $arguments['Zoom'] = $arguments['zoom']; - } - - // the id of the dom element to be used to render the street view - $arguments['DomID'] = 'google_streetview_'.self::$gsv_ctr; - - // incrememt the counter to ensure a unique id for each map canvas - self::$gsv_ctr++; - - // merge defaults and arguments - $customised = array_merge($defaults, $arguments); - - //get streetview template template - $template = new SSViewer( 'GoogleStreetView' ); - - //return the template customised with the parmameters - return $template->process( new ArrayData( $customised ) ); - } -} \ No newline at end of file + /* Counter used to ensure unique div ids to allow for multiple StreetViews on on page */ + private static $gsv_ctr = 1; + + + public static function parse_googlestreetview($arguments, $caption = null, $parser = null) { + // each of latitude, longitude and heading are required at a bare minimum + if(!isset($arguments['latitude'])){ + return ''; + } + + if(!isset($arguments['longitude'])){ + return ''; + } + + if(!isset($arguments['heading'])){ + return ''; + } + + // defaults - these can be overriden by using zoom and pitch in the shortcode + $defaults = array( + 'Zoom' => 1, + 'Pitch' => 0 + ); + + // ensure JavaScript for the map service is only downloaded once + $arguments['DownloadJS'] = !MapUtil::get_map_already_rendered(); + MapUtil::set_map_already_rendered(true); + + // convert parameters to CamelCase as per standard template conventions + $arguments['Latitude'] = $arguments['latitude']; + $arguments['Longitude'] = $arguments['longitude']; + $arguments['Heading'] = $arguments['heading']; + + // optional parameter caption + if (isset($arguments['caption'])) { + $arguments['Caption'] = $arguments['caption']; + } + + // optional parameter pitch + if (isset($arguments['pitch'])) { + $arguments['Pitch'] = $arguments['pitch']; + } + + // optional parameter zoom + if (isset($arguments['zoom'])) { + $arguments['Zoom'] = $arguments['zoom']; + } + + // the id of the dom element to be used to render the street view + $arguments['DomID'] = 'google_streetview_'.self::$gsv_ctr; + + // incrememt the counter to ensure a unique id for each map canvas + self::$gsv_ctr++; + + // merge defaults and arguments + $customised = array_merge($defaults, $arguments); + + //get streetview template template + $template = new SSViewer('GoogleStreetView'); + + //return the template customised with the parmameters + return $template->process(new ArrayData($customised)); + } +} From 54e5c530c8df1ec3933d18618a7d9af299c34c6c Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 17 Mar 2015 12:08:09 +0700 Subject: [PATCH 183/354] FIX: Ensure lat,long and zoom are hidden --- css/mapField.css | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/css/mapField.css b/css/mapField.css index dcbced2..8045f6c 100644 --- a/css/mapField.css +++ b/css/mapField.css @@ -7,8 +7,19 @@ } .editableMap { - display: block; height: 300px; - width: 700px; + /* width: 90%;*/ float: none; -} \ No newline at end of file +} + +.editableMapWrapper { + display: inline-block; + width: 900px; + float: none; + margin-left: auto; + margin-right: auto; +} + +.fieldholder-small { + display: none; +} From c58f78b0d8a36227362942a877a41e3e30643911 Mon Sep 17 00:00:00 2001 From: Werner Krauss Date: Mon, 16 Mar 2015 15:13:29 +0100 Subject: [PATCH 184/354] fixed error in MapUtil::sanitize() which converted a tab in a template to a single t Conflicts: code/MapUtil.php --- code/MapUtil.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/MapUtil.php b/code/MapUtil.php index 5a3eff2..d7f2422 100644 --- a/code/MapUtil.php +++ b/code/MapUtil.php @@ -209,7 +209,7 @@ public static function instance() * @return string */ public static function sanitize($content) { - return addslashes(str_replace(array("\n","\r"),array("",""),$content)); + return addslashes(str_replace(array("\n","\r", "\t"), '' ,$content)); } From 03c4bee81ae99b4ce108ebbba97f6d320f2a5a6c Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 17 Mar 2015 13:13:54 +0700 Subject: [PATCH 185/354] WIP: Tidy up of whitespace and line length issues MINOR: Added php indicator for code FIX: Whitespace vertical alignment MINOR: Wording fix MINOR: Added hints for coding rendering FIX: Whitespace compliance FIX: Whitespace compliance FIX: Whitespace and coding convention compliance FIX: Whitespace compliance FIX: Whitespace compliance --- README.md | 15 +- _config.php | 2 +- docs/en/AddingLinesToMaps.md | 72 +-- docs/en/AddingMapToADataObject.md | 32 +- docs/en/GoogleMapShortCodes.md | 28 +- docs/en/GoogleStreetViewShortCodes.md | 30 +- docs/en/Installation.md | 2 +- docs/en/MapLayers.md | 23 +- docs/en/MappingDataList.md | 55 ++- docs/en/MapsWithLines.md | 72 +-- docs/en/MultipleMapsSamePage.md | 20 +- docs/en/OpenStreetMap.md | 215 +++++---- docs/en/PointsOfInterest.md | 40 +- javascript/google/maputil.js | 57 ++- javascript/mapField.js | 619 ++++++++++++-------------- lang/en.yml | 2 +- 16 files changed, 671 insertions(+), 613 deletions(-) diff --git a/README.md b/README.md index 86b0702..7e48ad0 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,16 @@ ## Maintainers - * Gordon Anderson (Nickname: nontgor) - +* Gordon Anderson (Nickname: nontgor) + -## Introduction +##Introduction This module provides mapping functionality for SilverStripe DataObjects in a manner that is simple as possible for both the programmer and content editor. Short codes are also provided to render Google Street View and Google maps. -## Documentation +##Documentation * [Installation](./docs/en/Installation.md) * [Adding a Map to a DataObject](./docs/en/AddingMapToADataObject.md) * [Multiple Maps on the Same Page](./docs/en/MultipleMapsSamePage.md) @@ -26,10 +26,9 @@ Short codes are also provided to render Google Street View and Google maps. For more documentation about the module see the provided documentation located inside the docs folder. -## Requirements - - * SilverStripe 3.1 +##Requirements +* SilverStripe 3.1 ##TODO * Tests -* Add other mapping services such as Leaflet \ No newline at end of file +* Add other mapping services such as Leaflet diff --git a/_config.php b/_config.php index 0e80497..9e757ab 100755 --- a/_config.php +++ b/_config.php @@ -7,4 +7,4 @@ } ShortcodeParser::get('default')->register('GoogleStreetView',array('GoogleStreetViewShortCodeHandler','parse_googlestreetview')); -ShortcodeParser::get('default')->register('GoogleMap',array('GoogleMapShortCodeHandler','parse_googlemap')); \ No newline at end of file +ShortcodeParser::get('default')->register('GoogleMap',array('GoogleMapShortCodeHandler','parse_googlemap')); diff --git a/docs/en/AddingLinesToMaps.md b/docs/en/AddingLinesToMaps.md index 82740a7..d3b120e 100644 --- a/docs/en/AddingLinesToMaps.md +++ b/docs/en/AddingLinesToMaps.md @@ -1,48 +1,48 @@ #Adding Lines to Maps A line can be added to a map with the following API call: -``` - $map->addLine( $point1, $point2, $colorHexCode ); +```php +$map->addLine( $point1, $point2, $colorHexCode ); ``` -Each point is an array whose 0th element is the latitude and 1st element is the longitude. The third parameter is optional and represents the color of the line in standard CSS hex code colors (RGB). +Each point is an array whose 0th element is the latitude and 1st element is the longitude. +The third parameter is optional and represents the color of the line in standard CSS hex code +colors (RGB). An example method to draw a multicolored triangle on a map is as follows: -``` +```php /* - Render a triangle around the provided lat,lon, zoom from the editing functions, - */ - public function MapWithLines() { - $map = $this->owner->getRenderableMap(); - $map->setZoom( $this->ZoomLevel ); - $map->setAdditionalCSSClasses( 'fullWidthMap' ); - $map->setShowInlineMapDivStyle( true ); - - $scale = 0.3; - - // draw a triangle - $point1 = array( - $this->Lat - 0.5*$scale, $this->Lon - ); - $point2 = array( - $this->Lat + 0.5*$scale, $this->Lon-0.7*$scale - ); - - $point3 = array( - $this->Lat + 0.5*$scale, $this->Lon+0.7*$scale - ); - - $map->addLine( $point1, $point2 ); - $map->addLine( $point2, $point3, '#000077' ); - $map->addLine( $point3, $point1, '#007700' ); - - return $map; - } +Render a triangle around the provided lat,lon, zoom from the editing functions, +*/ +public function MapWithLines() { + $map = $this->owner->getRenderableMap(); + $map->setZoom( $this->ZoomLevel ); + $map->setAdditionalCSSClasses( 'fullWidthMap' ); + $map->setShowInlineMapDivStyle( true ); + + $scale = 0.3; + + // draw a triangle + $point1 = array( + $this->Lat - 0.5*$scale, $this->Lon + ); + $point2 = array( + $this->Lat + 0.5*$scale, $this->Lon-0.7*$scale + ); + + $point3 = array( + $this->Lat + 0.5*$scale, $this->Lon+0.7*$scale + ); + + $map->addLine( $point1, $point2 ); + $map->addLine( $point2, $point3, '#000077' ); + $map->addLine( $point3, $point1, '#007700' ); + + return $map; +} ``` +Instead of calling $BasicMap call $MapWithLines instead from the template. - - Instead of calling $BasicMap call $MapWithLines instead from the template. - - See http://demo.weboftalent.asia/mappable/map-with-lines/ for a working demo. +See http://demo.weboftalent.asia/mappable/map-with-lines/ for a working demo. diff --git a/docs/en/AddingMapToADataObject.md b/docs/en/AddingMapToADataObject.md index 0548287..cffc083 100644 --- a/docs/en/AddingMapToADataObject.md +++ b/docs/en/AddingMapToADataObject.md @@ -1,36 +1,44 @@ #Adding a Map to a DataObject ##Add Extension -Using the standard method of adding extensions in SilverStripe 3.1, add an extension called 'MapExtension' to relevant DataObjects. - -``` +Using the standard method of adding extensions in SilverStripe 3.1, add an extension called +'MapExtension' to relevant DataObjects. +```yml --- name: weboftalent-example-map-extensions --- PageWithMap: - extensions: - ['MapExtension'] - + extensions: + ['MapExtension'] ``` ##Editing -Utilising the extensions adds Latitude, Longitude and Zoom fields to the DataObject in question, in the example above 'PageWithMap'. In addition, the admin interface for PageWithMap now has a location tab. Location can be changed in 3 ways -* Use the geocoder and search for a place name ![Map Editing - Searching for a Place Name](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/mapedit-search.png?raw=true "Map Editing - Searching for a Place Name") +Utilising the extensions adds Latitude, Longitude and Zoom fields to the DataObject in question, +in the example above 'PageWithMap'. In addition, the admin interface for PageWithMap now has a +location tab. Location can be changed in 3 ways: +* Use the geocoder and search for a place name +![Map Editing - Searching for a Place Name] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/mapedit-search.png?raw=true +"Map Editing - Searching for a Place Name") * Drag the map pin -* Right click ![Map Editing - Right Clicking on a Map](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/mapedit-rightclick.png?raw=true "Map Editing - Right Clicking on a Map") +* Right click ![Map Editing - Right Clicking on a Map] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/mapedit-rightclick.png?raw=true +"Map Editing - Right Clicking on a Map") The zoom level set by the content editor is also saved. ##Templates To render a map in the template, simply called $BasicMap -``` +```php

$Title

$Content $BasicMap ``` -For an example of this, see http://demo.weboftalent.asia/mappable/quick-map-adding-a-map-to-a-dataobject/ +For an example of this, +see http://demo.weboftalent.asia/mappable/quick-map-adding-a-map-to-a-dataobject/ ##Custom Popup When clicking on the pin a popup with the name of the pin will occur. -If you want to change the information displayed there you have to setup a template named by the decorated DataObject suffixed with `_MapInfoWindow`, e.g. `MyPage_MapInfoWindow`. +If you want to change the information displayed there you have to setup a template named by the +decorated DataObject suffixed with `_MapInfoWindow`, e.g. `MyPage_MapInfoWindow`. diff --git a/docs/en/GoogleMapShortCodes.md b/docs/en/GoogleMapShortCodes.md index 755cc3d..8a5546b 100644 --- a/docs/en/GoogleMapShortCodes.md +++ b/docs/en/GoogleMapShortCodes.md @@ -13,24 +13,36 @@ Valid map types are # Map Type - Road ``` -[GoogleMap latitude='13.7402946' longitude='100.5525439' caption="Roads in Central Bangkok" zoom="14" maptype="road"] +[GoogleMap latitude='13.7402946' longitude='100.5525439' caption="Roads in Central Bangkok" +zoom="14" maptype="road"] ``` -![Google Maps - Map Type, Road](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/maptyperoad.png?raw=true "Google Maps - Map Type, Road") +![Google Maps - Map Type, Road] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/maptyperoad.png?raw=true +"Google Maps - Map Type, Road") # Map Type - Aerial ``` -[GoogleMap latitude='13.815483' longitude='100.5447213' caption="Bang Sue Train Depot, Thailand" zoom="20" maptype="aerial"] +[GoogleMap latitude='13.815483' longitude='100.5447213' caption="Bang Sue Train Depot, Thailand" +zoom="20" maptype="aerial"] ``` -![Google Maps - Map Type, Aerial](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/maptype-aerial.png?raw=true "Google Maps - Map Type, Aerial") +![Google Maps - Map Type, Aerial] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/maptype-aerial.png?raw=true +"Google Maps - Map Type, Aerial") # Map Type - Hybrid ``` -[GoogleMap latitude='13.8309545' longitude='100.5577219' caption="Junction in Bangkok, Thailand" zoom="18" maptype="hybrid"] +[GoogleMap latitude='13.8309545' longitude='100.5577219' caption="Junction in Bangkok, Thailand" +zoom="18" maptype="hybrid"] ``` -![Google Maps - Map Type, Hybrid](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/maptype-hybrid.png?raw=true "Google Maps - Map Type, Hybrid") +![Google Maps - Map Type, Hybrid] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/maptype-hybrid.png?raw=true +"Google Maps - Map Type, Hybrid") * Map Type - Terrain ``` -[GoogleMap latitude='18.8032393' longitude='98.9166518' caption="Mountains west of Chiang Mai" zoom="14" maptype="terrain"] +[GoogleMap latitude='18.8032393' longitude='98.9166518' caption="Mountains west of Chiang Mai" +zoom="14" maptype="terrain"] ``` -![Google Maps - Map Type, Terrain](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/maptype-terrain.png?raw=true "Google Maps - Map Type, Terrain") +![Google Maps - Map Type, Terrain] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/maptype-terrain.png?raw=true +"Google Maps - Map Type, Terrain") diff --git a/docs/en/GoogleStreetViewShortCodes.md b/docs/en/GoogleStreetViewShortCodes.md index 3d10dc3..c14e569 100644 --- a/docs/en/GoogleStreetViewShortCodes.md +++ b/docs/en/GoogleStreetViewShortCodes.md @@ -1,28 +1,32 @@ # Google Street View One can embed Google Street View in a page as an editor using a short code of the following format: - - [GoogleStreetView latitude="13.811841" longitude="100.527309" heading="162.43" pitch="-10" caption="Canal south from Pracha Rat 1 Soi 28"] - -The parameters latitude, longitude, and heading are required. If not provided the short code will return an empty string. +``` +[GoogleStreetView latitude="13.811841" longitude="100.527309" heading="162.43" pitch="-10" +caption="Canal south from Pracha Rat 1 Soi 28"] +``` +The parameters latitude, longitude, and heading are required. If not provided the short code will +return an empty string. For rendering purposes including CSS similar to below in your theme: -``` +```css .streetview { - width: 100%; - height: 500px; - background: #EEE; + width: 100%; + height: 500px; + background: #EEE; } /* Ensure map controls are correct aspect ration, and thatFirefox rendering work, see http://stackoverflow.com/questions/11340468/street-view-not-working-in-firefox */ .streetview img { - border: none !important; - max-width: none !important; + border: none !important; + max-width: none !important; } .streetviewContainer p.caption { - text-align: center; + text-align: center; } ``` -## Example Rendering -![Example Google Street View Rendering](https://raw.githubusercontent.com/gordonbanderson/Mappable/screenshots/screenshots/google-streeview-shortcode-example.png?raw=true "Example Google Street View Rendering") +##Example Rendering +![Example Google Street View Rendering] +(https://raw.githubusercontent.com/gordonbanderson/Mappable/screenshots/screenshots/google-streeview-shortcode-example.png?raw=true +"Example Google Street View Rendering") diff --git a/docs/en/Installation.md b/docs/en/Installation.md index e0d9457..5f936ea 100644 --- a/docs/en/Installation.md +++ b/docs/en/Installation.md @@ -11,4 +11,4 @@ composer require weboftalent/mappable git clone git@github.com:gordonbanderson/Mappable.git mappable cd mappable git checkout 3.1 -``` \ No newline at end of file +``` diff --git a/docs/en/MapLayers.md b/docs/en/MapLayers.md index 3ff0e48..f48f039 100644 --- a/docs/en/MapLayers.md +++ b/docs/en/MapLayers.md @@ -1,7 +1,7 @@ #Map Layers KML layers can be added through the CMS by adding an extension to the class in question. -``` +```php where( 'Lat != 0 AND Lon !=0' ); - if ( $flickrPhotos->count() == 0 ) { - return ''; // don't render a map - } - - $map = $flickrPhotos->getRenderableMap(); - $map->setZoom( $this->ZoomLevel ); - $map->setAdditionalCSSClasses( 'fullWidthMap' ); - $map->setShowInlineMapDivStyle( true ); - if ( $this->ClusterExampleDataset ) { - $map->setClusterer( true ); - } - - return $map; - } + $flickrPhotos = DataList::create( 'FlickrPhoto' )->where( 'Lat != 0 AND Lon !=0' ); + if ( $flickrPhotos->count() == 0 ) { + return ''; // don't render a map + } + + $map = $flickrPhotos->getRenderableMap(); + $map->setZoom( $this->ZoomLevel ); + $map->setAdditionalCSSClasses( 'fullWidthMap' ); + $map->setShowInlineMapDivStyle( true ); + if ( $this->ClusterExampleDataset ) { + $map->setClusterer( true ); + } + + return $map; +} ``` -The map is positioned so that it shows all of the points automatically. Also note that the host page of the map does not require to implement mappable or even have a location attached to it, as the map is rendered entirely from the DataList, in this case $flickrPhotos. +The map is positioned so that it shows all of the points automatically. Also note that the host +page of the map does not require to implement mappable or even have a location attached to it, as +the map is rendered entirely from the DataList, in this case $flickrPhotos. For clustered and unclustered examples, see: http://demo.weboftalent.asia/mappable/map-from-a-datalist-unclustered/ -![Non Clustered DataList](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/datalist-unclustered.png?raw=true "Non Clustered DataList") -http://demo.weboftalent.asia/mappable/map-with-datalist-clustered-markers/ -![Clustered DataList](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/datalist-clustered.png?raw=true "Clustered DataList") +![Non Clustered DataList] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/datalist-unclustered.png?raw=true +"Non Clustered DataList") +and http://demo.weboftalent.asia/mappable/map-with-datalist-clustered-markers/ +![Clustered DataList] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/datalist-clustered.png?raw=true +"Clustered DataList") diff --git a/docs/en/MapsWithLines.md b/docs/en/MapsWithLines.md index 443c897..552d9b6 100644 --- a/docs/en/MapsWithLines.md +++ b/docs/en/MapsWithLines.md @@ -2,47 +2,51 @@ A line can be added to a map with the following API call: ``` - $map->addLine( $point1, $point2, $colorHexCode ); + $map->addLine( $point1, $point2, $colorHexCode ); ``` -Each point is an array whose 0th element is the latitude and 1st element is the longitude. The third parameter is optional and represents the color of the line in standard CSS hex code colors (RGB). +Each point is an array whose 0th element is the latitude and 1st element is the longitude. The +third parameter is optional and represents the color of the line in standard CSS hex code colors +(RGB). An example method to draw a multicolored triangle on a map is as follows: -``` +```php /* - Render a triangle around the provided lat,lon, zoom from the editing functions, - */ - public function MapWithLines() { - $map = $this->owner->getRenderableMap(); - $map->setZoom( $this->ZoomLevel ); - $map->setAdditionalCSSClasses( 'fullWidthMap' ); - $map->setShowInlineMapDivStyle( true ); - - $scale = 0.3; - - // draw a triangle - $point1 = array( - $this->Lat - 0.5*$scale, $this->Lon - ); - $point2 = array( - $this->Lat + 0.5*$scale, $this->Lon-0.7*$scale - ); - - $point3 = array( - $this->Lat + 0.5*$scale, $this->Lon+0.7*$scale - ); - - $map->addLine( $point1, $point2 ); - $map->addLine( $point2, $point3, '#000077' ); - $map->addLine( $point3, $point1, '#007700' ); - - return $map; - } +Render a triangle around the provided lat,lon, zoom from the editing functions, +*/ +public function MapWithLines() { + $map = $this->owner->getRenderableMap(); + $map->setZoom( $this->ZoomLevel ); + $map->setAdditionalCSSClasses( 'fullWidthMap' ); + $map->setShowInlineMapDivStyle( true ); + + $scale = 0.3; + + // draw a triangle + $point1 = array( + $this->Lat - 0.5*$scale, $this->Lon + ); + $point2 = array( + $this->Lat + 0.5*$scale, $this->Lon-0.7*$scale + ); + + $point3 = array( + $this->Lat + 0.5*$scale, $this->Lon+0.7*$scale + ); + + $map->addLine( $point1, $point2 ); + $map->addLine( $point2, $point3, '#000077' ); + $map->addLine( $point3, $point1, '#007700' ); + + return $map; +} ``` - Instead of calling $BasicMap call $MapWithLines instead from the template. +Instead of calling $BasicMap call $MapWithLines instead from the template. - See http://demo.weboftalent.asia/mappable/map-with-lines/ for a working demo. +See http://demo.weboftalent.asia/mappable/map-with-lines/ for a working demo. -![Map With Lines](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/mapswithlines.png?raw=true "Map With Lines") \ No newline at end of file +![Map With Lines] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/mapswithlines.png?raw=true +Map With Lines") diff --git a/docs/en/MultipleMapsSamePage.md b/docs/en/MultipleMapsSamePage.md index 86ac50a..c9311b8 100644 --- a/docs/en/MultipleMapsSamePage.md +++ b/docs/en/MultipleMapsSamePage.md @@ -1,10 +1,14 @@ #Multiple Maps on the Same Page -Multiple maps can be added to a page. One option is to associate maps with a DataObject whose relationship to the parent page is 'has many', as in this example of contact page addresses. +Multiple maps can be added to a page. One option is to associate maps with a DataObject whose +relationship to the parent page is 'has many', as in this example of contact page addresses. ##Example -Often a company will have more than one office location that they wish to display, this is a an example of that use case. It would probably need expanding in order to show the likes of email address and telephone number, left as an exercise for the reader. +Often a company will have more than one office location that they wish to display, this is a an +example of that use case. It would probably need expanding in order to show the likes of email +address and telephone number, left as an exercise for the reader. -Firstly, create a parent container page called ContactPage, this has many locations of type ContactPageAddress. -``` +Firstly, create a parent container page called ContactPage, this has many locations of type +ContactPageAddress. +```php + ``` The latter contains the actual map for each location, configured as above using extensions.yml -``` +```php ``` The template simply loops through the contact page addresses, rendering a map. @@ -80,4 +82,4 @@ $BasicMap $Content ``` -See http://demo.weboftalent.asia/mappable/multiple-maps-on-the-same-page/ for a working demo. \ No newline at end of file +See http://demo.weboftalent.asia/mappable/multiple-maps-on-the-same-page/ for a working demo. diff --git a/docs/en/OpenStreetMap.md b/docs/en/OpenStreetMap.md index 79de700..2ae4a33 100644 --- a/docs/en/OpenStreetMap.md +++ b/docs/en/OpenStreetMap.md @@ -1,18 +1,24 @@ #OpenStreetMap ##Importing OSM Data -The import process is documented numerously on the internet, a good one for Ubuntu is https://switch2osm.org/serving-tiles/manually-building-a-tile-server-14-04/ - once set up an import of the OSM data for Thailand took around 20 minutes. +The import process is documented numerously on the internet, a good one for Ubuntu is +https://switch2osm.org/serving-tiles/manually-building-a-tile-server-14-04/ - once set up an import +of the OSM data for Thailand took around 20 minutes. + Snapshots of OSM data are available from http://download.geofabrik.de/ ##Extracting Points of Interest The process of extracting points of interest is as follows: -* Select the points you are interested in from the PostGIS database into which the OSM data was imported. Use SQL and output to a file. -* Parse the result output using a language of your choice to create SQL that can be imported into SilverStripe as points of interest. +* Select the points you are interested in from the PostGIS database into which the OSM data was +imported. Use SQL and output to a file. +* Parse the result output using a language of your choice to create SQL that can be imported into +SilverStripe as points of interest. * Import the resultant SQL into your SilverStripe database. ## Worked Example - Thai Railway Stations ###Extract Data from PostGIS OSM Database -Create a file of arbitrary name, e.g. osm.sql, with the following query, which extracts OSM id, name, latitude, longitude and tags for nodes: -``` +Create a file of arbitrary name, e.g. osm.sql, with the following query, which extracts OSM id, +name, latitude, longitude and tags for nodes: +```sql select osm_id,name, amenity, ST_Y((ST_Transform (way, 4326))) as Latitude, ST_X((ST_Transform (way, 4326))) as Longitude, @@ -24,7 +30,7 @@ on N.id = P.osm_id ``` At the command line execute this query and grep for railway stations: -``` +```bash psql gis < osm.sql | grep -i railway > trains.osm ``` Note that authentication to the Postgres database may differ. @@ -36,20 +42,23 @@ Analysis of the output shows that further grepping is require in order to extrac 3287950085 | Death Railway viaduct | | 14.1534710197064 | 99.1098331602021 | {tourism,attraction,name,"Death Railway viaduct"} 3288538284 | | | 9.14452066569457 | 99.1675465934402 | {railway,level_crossing} 3288549665 | - ``` - Execute the following to get just the stations: +``` +Execute the following to get just the stations: ``` grep -i station trains.osm > stations.osm ``` ###Create Points of Interest Layer -In the SilverStripe model admin interface create a new PointsOfInterestLayer to contain the railway stations. Note that a common +In the SilverStripe model admin interface create a new PointsOfInterestLayer to contain the railway +stations. Note that a common icon for the layer can be added here, if none is provided the standard Google Map pin is used. -![Adding a new points of interest layer](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-create-railway-layer.png?raw=true "Adding a new points of interest layer") +![Adding a new points of interest layer] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-create-railway-layer.png?raw=true +"Adding a new points of interest layer") Obtain the database ID, in this case the value 3. This is required for scripting purposes. -``` +```sql mysql> select * from PointsOfInterestLayer; +----+-----------------------+---------------------+---------------------+------------------------------+---------------+ | ID | ClassName | Created | LastEdited | Name | DefaultIconID | @@ -61,11 +70,12 @@ mysql> select * from PointsOfInterestLayer; ``` ###Convert Extracted Data to SQL for Import Into SilverStripe -The following is an example Ruby script to extract the English name from the tags field, if one is defined, and output -SQL that can be imported directly into the SilverStripe database for the site in question. It was saved as parse_osm.rb, the -name is of course arbitrary. +The following is an example Ruby script to extract the English name from the tags field, if one is +defined, and output +SQL that can be imported directly into the SilverStripe database for the site in question. It was +saved as parse_osm.rb, the name is of course arbitrary. -``` +```ruby filename = ARGV[0] layerid = ARGV[1] ctr = 0 @@ -73,63 +83,63 @@ ctr = 0 puts "/* Extracting from #{filename} */" puts "begin;" File.open(filename) do |file| - file.each {|line| \ - ctr = ctr + 1 - if ctr < 3 - next - end - splits = line.split('|') - if (splits.length == 6) - puts - osm_id = splits[0] - name = splits[1] - lat = splits[3] - lon = splits[4].strip - - #Tags are a comma separated list of key value pairs - tags = {} - tagtxt = splits[5].strip - tagtxt[0] = '' - tagtxt[-1] = '' - tagcols = tagtxt.split ',' - tagname = 'UNDEFINED' - while tagcols.length > 0 - value = tagcols.pop - key = tagcols.pop - tags[key] = value - end - - - if tags['name:en'] - tagname = tags['name:en'] - else - tagname = tags['name'] - end - - if tagname == nil - tagname = "UNDEFINED" - end - - # Remove quotation marks - if tagname[0] == '"' - tagname[0]='' - end - - if tagname[-1] == '"' - tagname[-1]='' - end - - tagname.strip! - - if tagname != 'UNDEFINED' - sql = "INSERT INTO PointOfInterest(OpenStreetMapID,Name,Lat,Lon,ZoomLevel,Created,LastEdited,MapPinEdited) VALUES (#{osm_id},'#{tagname}',#{lat},#{lon},16,now(),now(),true);" - puts sql - sql = "INSERT INTO PointsOfInterestLayer_PointsOfInterest(PointsOfInterestLayerID,PointOfInterestID) VALUES(#{layerid}, LAST_INSERT_ID());" - puts sql - end - - end - } + file.each {|line| \ + ctr = ctr + 1 + if ctr < 3 + next + end + splits = line.split('|') + if (splits.length == 6) + puts + osm_id = splits[0] + name = splits[1] + lat = splits[3] + lon = splits[4].strip + + #Tags are a comma separated list of key value pairs + tags = {} + tagtxt = splits[5].strip + tagtxt[0] = '' + tagtxt[-1] = '' + tagcols = tagtxt.split ',' + tagname = 'UNDEFINED' + while tagcols.length > 0 + value = tagcols.pop + key = tagcols.pop + tags[key] = value + end + + + if tags['name:en'] + tagname = tags['name:en'] + else + tagname = tags['name'] + end + + if tagname == nil + tagname = "UNDEFINED" + end + + # Remove quotation marks + if tagname[0] == '"' + tagname[0]='' + end + + if tagname[-1] == '"' + tagname[-1]='' + end + + tagname.strip! + + if tagname != 'UNDEFINED' + sql = "INSERT INTO PointOfInterest(OpenStreetMapID,Name,Lat,Lon,ZoomLevel,Created,LastEdited,MapPinEdited) VALUES (#{osm_id},'#{tagname}',#{lat},#{lon},16,now(),now(),true);" + puts sql + sql = "INSERT INTO PointsOfInterestLayer_PointsOfInterest(PointsOfInterestLayerID,PointOfInterestID) VALUES(#{layerid}, LAST_INSERT_ID());" + puts sql + end + + end + } end puts "commit;" @@ -144,31 +154,60 @@ so in the case of the example above ruby parse_osm.rb stations.osm 3 ``` -Output is many rows of SQL like this, the first line of each pair being the creation of the point of interest and the second associating it with the point of interest layer representing the stations. -``` -INSERT INTO PointOfInterest(OpenStreetMapID,Name,Lat,Lon,ZoomLevel,Created,LastEdited,MapPinEdited) VALUES ( 236480470 ,'Khlong Phutsa', 14.1860507762646 ,100.578314006055,16,now(),now(),true); -INSERT INTO PointsOfInterestLayer_PointsOfInterest(PointsOfInterestLayerID,PointOfInterestID) VALUES(3, LAST_INSERT_ID()); +Output is many rows of SQL like this, the first line of each pair being the creation of the point +of interest and the second associating it with the point of interest layer representing the +stations. -INSERT INTO PointOfInterest(OpenStreetMapID,Name,Lat,Lon,ZoomLevel,Created,LastEdited,MapPinEdited) VALUES ( 237445803 ,'Railway station Ayutthaya', 14.3567211927894 ,100.58319491232,16,now(),now(),true); -INSERT INTO PointsOfInterestLayer_PointsOfInterest(PointsOfInterestLayerID,PointOfInterestID) VALUES(3, LAST_INSERT_ID()); +```sql +INSERT INTO PointOfInterest(OpenStreetMapID,Name,Lat,Lon,ZoomLevel,Created,LastEdited,MapPinEdited) +VALUES ( 236480470 ,'Khlong Phutsa', 14.1860507762646 ,100.578314006055,16,now(),now(),true); +INSERT INTO PointsOfInterestLayer_PointsOfInterest(PointsOfInterestLayerID,PointOfInterestID) +VALUES(3, LAST_INSERT_ID()); + +INSERT INTO PointOfInterest(OpenStreetMapID,Name,Lat,Lon,ZoomLevel,Created,LastEdited,MapPinEdited) +VALUES ( 237445803 ,'Railway station Ayutthaya', 14.3567211927894 ,100.58319491232,16,now(),now(),true); +INSERT INTO PointsOfInterestLayer_PointsOfInterest(PointsOfInterestLayerID,PointOfInterestID) +VALUES(3, LAST_INSERT_ID()); ``` -Note that the method LAST_INSERT_ID() is MySQL centric. If your SilverStripe database is hosted using PostgreSQL then change this to 'currval()'. -An improvement to this script would be the addition of escaping quotes but it wasn't a necessity for the data being loaded. +Note that the method LAST_INSERT_ID() is MySQL centric. If your SilverStripe database is hosted +using PostgreSQL then change this to 'currval()'. + +An improvement to this script would be the addition of escaping quotes but it wasn't a necessity +for the data being loaded. ### Add Points Of Interest Layer to an Existing Page -Add the new layer in the 'Map Layers' tab for any page using the PointsOfInterestLayerExtension extension. Type the word 'Railway' into the search box to the right of 'Points of Interest Layer'. After a couple of seconds select 'Railway Stations of Thailand' and click 'Link Existing'. Save and publish the page. -* During the process of adding the railway stations layer ![Adding railway layer](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-adding-railway-layer.png?raw=true "Adding railway layer") -* After the railway stations layer was added ![Added railway layer](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-adding-railway-layer-2.png?raw=true "Added railway layer") +Add the new layer in the 'Map Layers' tab for any page using the PointsOfInterestLayerExtension +extension. Type the word 'Railway' into the search box to the right of 'Points of Interest Layer'. +After a couple of seconds select 'Railway Stations of Thailand' and click 'Link Existing'. +Save and publish the page. +* During the process of adding the railway stations layer +![Adding railway layer] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-adding-railway-layer.png?raw=true +"Adding railway layer") +* After the railway stations layer was added +![Added railway layer] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-adding-railway-layer-2.png?raw=true +"Added railway layer") ###View Data in SilverStripe The imported railway stations can now be seen and edited in the model admin interface. -* List of railway stations. ![Railway Stations as POIs in Model Admin](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-imported-railway-stations.png?raw=true "Railway Stations as POIs in Model Admin") -* Editing the entry for Bankrut Railway Station (1/2). ![Editing a single station](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-editing-bankrut.png?raw=true "Editing a single station (1/2)") -* Editing the entry for Bankrut Railway Station (2/2). ![Editing a single station](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-single-station-location.png?raw=true "Editing a single station (2/2)") +* List of railway stations. ![Railway Stations as POIs in Model Admin] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-imported-railway-stations.png?raw=true +"Railway Stations as POIs in Model Admin") +* Editing the entry for Bankrut Railway Station (1/2). ![Editing a single station] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-editing-bankrut.png?raw=true +"Editing a single station (1/2)") +* Editing the entry for Bankrut Railway Station (2/2). ![Editing a single station] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-single-station-location.png?raw=true +"Editing a single station (2/2)") ###Public Rendered View ####Map of Thailand with Railway Stations POIs Marked as Clusters -* The entire country ![Railway Stations in Thailand](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-clustered-stations.png?raw=true "Railway Stations in Thailand") -* Zoomed in to show individual stations ![Railway Stations in Thailand - Chachaengsao Area](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-clustered-stations-zoomin.png?raw=true "Railway Stations in Thailand - Chachaengsao Area") \ No newline at end of file +* The entire country ![Railway Stations in Thailand] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-clustered-stations.png?raw=true +"Railway Stations in Thailand") +* Zoomed in to show individual stations ![Railway Stations in Thailand - Chachaengsao Area] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-clustered-stations-zoomin.png?raw=true +"Railway Stations in Thailand - Chachaengsao Area") diff --git a/docs/en/PointsOfInterest.md b/docs/en/PointsOfInterest.md index 478bb28..b0c592d 100644 --- a/docs/en/PointsOfInterest.md +++ b/docs/en/PointsOfInterest.md @@ -1,38 +1,56 @@ #Points of Interest -A point of interest (or POI) denotes a location on a map that is of interest, for example a bridge, a railway station, or a level crossing. They are grouped together in layers, a PointOfInterest having a many many relationship with PointsOfInterestLayer. This allows for the use case where "Crewe Railway Station" can appear in a "Railway Stations of Great Britain" layer as well as a "Railway Stations of England" layer. +A point of interest (or POI) denotes a location on a map that is of interest, for example a bridge, +a railway station, or a level crossing. They are grouped together in layers, a PointOfInterest +having a many many relationship with PointsOfInterestLayer. This allows for the use case where +"Crewe Railway Station" can appear in a "Railway Stations of Great Britain" layer as well as a +"Railway Stations of England" layer. #Adding Points of Interest to a Page Type -Whilst points of interest layers are available to edit after installing the Mappable module, they need to be added to a DataObject for rendering purposes. Do this using the standard extensions mechanism in a yml file. Note that MapExtension is also required, as this has the method +Whilst points of interest layers are available to edit after installing the Mappable module, they +need to be added to a DataObject for rendering purposes. Do this using the standard extensions +mechanism in a yml file. Note that MapExtension is also required, as this has the method BasicMap() for rendering the map in a template. -``` + +```yml PageWithPointsOfInterest: extensions: ['MapExtension', 'PointsOfInterestLayerExtension'] ``` If you wish to remove the Location tab in this case add the following in your equivalent class: + ```php function getCMSFields() { $fields = parent::getCMSFields(); $fields->removeByName('Location'); return $fields; } -```php +``` #Editing -A new model admin tab is available called "Points of Interest". Here you can add new layers or edit existing ones. +A new model admin tab is available called "Points of Interest". Here you can add new layers or +edit existing ones. ##Adding a New Layer Add a new layer, in this case "Supermarkets of Bangkok" and save it. -![Adding a new layer](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/001-poi-create-new-layer.png?raw=true "Adding a new layer") +![Adding a new layer] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/001-poi-create-new-layer.png?raw=true +"Adding a new layer") ##Adding a new Point of Interest to that Layer -![Adding a new supermarket](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/003-poi-create-new-point-location.png?raw=true "Adding a new supermarket") -![Editing Location](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/004-poi-create-new-point-location.png?raw=true "Editing Location") -![Point of Interest Saved](https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/005-poi-saved.png?raw=true "Point of Interest Saved") +![Adding a new supermarket] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/003-poi-create-new-point-location.png?raw=true +"Adding a new supermarket") +![Editing Location] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/004-poi-create-new-point-location.png?raw=true "Editing Location") +![Point of Interest Saved] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/005-poi-saved.png?raw=true +"Point of Interest Saved") #Rendering -The method BasicMap in MapExtension takes into account points of interest when rendering a map. If you full control of the rendering from within the object containing POIs then use this code as a basis: +The method BasicMap in MapExtension takes into account points of interest when rendering a map. +If you full control of the rendering from within the object containing POIs then use this code as +a basis: ```php public function BicycleRideMap() { @@ -75,4 +93,4 @@ public function BicycleRideMap() { #Page with Points of Interest Layer A page type, POIMapPage, is included in the Mappable module. It is the same as a page, with the addition of MapExtension and PointsOfInterestLayerException. You -will probably have to override the template, POIMapPage.ss in your theme. \ No newline at end of file +will probably have to override the template, POIMapPage.ss in your theme. diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index 636dfe9..4afe7bd 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -1,10 +1,10 @@ var mappableMapCtr = 0; -function createMarker(map,lat, lng, html, category, icon, useClusterer, enableWindowZoom, defaultHideMarker) { + +function createMarker(map, lat, lng, html, category, icon, useClusterer, enableWindowZoom, defaultHideMarker) { mapId = map.getDiv().getAttribute('id'); var marker = new google.maps.Marker(); - marker.setPosition(new google.maps.LatLng(lat, lng)); marker.mycategory = category; @@ -33,52 +33,48 @@ function createMarker(map,lat, lng, html, category, icon, useClusterer, enableWi if (defaultHideMarker) { marker.hide(); } - } - // JS public function to get current Lat & Lng - function getCurrentLat() { return current_lat; } + function getCurrentLng() { return current_lng; } - // JS public function to center the gmaps dynamically - function showAddress(address) { if (geocoder) { geocoder.getLatLng( - address, - - function(point) { - if (!point) { - alert(address + " not found"); - } else { - map.setCenter(point); - map.setZoom($Zoom); - } - }); + address, + + function(point) { + if (!point) { + alert(address + " not found"); + } else { + map.setCenter(point); + map.setZoom($Zoom); + } + }); } } - function addAllMarkers(map, markers, useClusterer, enableWindowZoom, defaultHideMarker) { for (var i = 0; i < markers.length; i++) { var marker = markers[i]; - createMarker(map,marker.latitude, marker.longitude, marker.html, marker.category, marker.icon, - useClusterer, enableWindowZoom, defaultHideMarker); + createMarker(map, marker.latitude, marker.longitude, marker.html, marker.category, marker.icon, + useClusterer, enableWindowZoom, defaultHideMarker); } } + function addLines(map, lines) { for (i = 0; i < lines.length; i++) { var line = lines[i]; @@ -95,6 +91,7 @@ function addLines(map, lines) { } } + function addKmlFiles(map, kmlFiles) { for (var i = 0; i < kmlFiles.length; i++) { var kmlFile = kmlFiles[i]; @@ -107,7 +104,7 @@ function addKmlFiles(map, kmlFiles) { } function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLat, maxLng, mapType, markers, lines, kmlFiles, - jsonMapStyles, enableAutomaticCenterZoom, useClusterer, allowFullScreen) { + jsonMapStyles, enableAutomaticCenterZoom, useClusterer, allowFullScreen) { var newMap = []; newMap.googleMapID = googleMapID; newMap.zoom = zoom; @@ -132,7 +129,10 @@ function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLa // initialise gmarkers array for this map gmarkers[googleMapID] = []; - var infoWindow = new google.maps.InfoWindow({ content: 'test', maxWidth: 400 }); + var infoWindow = new google.maps.InfoWindow({ + content: 'test', + maxWidth: 400 + }); infoWindows[googleMapID] = infoWindow; mapLayers[googleMapID] = kmlFiles; @@ -141,7 +141,6 @@ function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLa } - function loadedGoogleMapsAPI() { for (var i = 1; i <= mappableMapCtr; i++) { var map_info = mappableMaps['google_map_' + i]; @@ -156,17 +155,17 @@ function loadedGoogleMapsAPI() { // FIXME - to do // if (map_info.jsonMapStyles) { - //map.setOptions({styles: map_info.jsonMapStyles}); + //map.setOptions({styles: map_info.jsonMapStyles}); //}; if (map_info.allowFullScreen) { - map.controls[google.maps.ControlPosition.TOP_RIGHT].push( - FullScreenControl(map, "Full Screen", "Original Size") + map.controls[google.maps.ControlPosition.TOP_RIGHT].push( + FullScreenControl(map, "Full Screen", "Original Size") ); } if (map_info.enableAutomaticCenterZoom) { centre = map_info.centreCoordinates; - map.setCenter(new google.maps.LatLng(centre.lat,centre.lng)); + map.setCenter(new google.maps.LatLng(centre.lat, centre.lng)); var bds = new google.maps.LatLngBounds(new google.maps.LatLng(map_info.minLat, map_info.minLng), new google.maps.LatLng(map_info.maxLat, map_info.maxLng)); @@ -175,7 +174,7 @@ function loadedGoogleMapsAPI() { map.setZoom(map_info.zoom); } else { var centre = map_info.centreCoordinates; - map.setCenter(new google.maps.LatLng(centre.lat,centre.lng)); + map.setCenter(new google.maps.LatLng(centre.lat, centre.lng)); map.setZoom(map_info.zoom); } @@ -187,7 +186,7 @@ function loadedGoogleMapsAPI() { addAllMarkers(map, map_info.markers, map_info.useClusterer, map_info.enableAutomaticCenterZoom, map_info.defaultHideMarker); addLines(map, map_info.lines); - addKmlFiles(map,map_info.kmlFiles); + addKmlFiles(map, map_info.kmlFiles); if (map_info.useClusterer) { fluster.initialize(); } diff --git a/javascript/mapField.js b/javascript/mapField.js index 78e6979..40d8153 100755 --- a/javascript/mapField.js +++ b/javascript/mapField.js @@ -1,335 +1,290 @@ // FIXME avoid global var marker; -var bounds ; - - - function gmloaded() { - initLivequery(); - } - - // initialise the map - - function initMap() { - var myOptions = { - zoom: 16, - disableDefaultUI: false, - mapTypeId: google.maps.MapTypeId.ROADMAP, - disableDoubleClickZoom: false, - draggable: true, - keyboardShortcuts: false, - scrollwheel: true - }; - - (function($) { - var gm = $('#GoogleMap'); - var latFieldName = gm.attr('data-latfieldname'); - - var latField = $('input[name='+gm.attr('data-latfieldname')+']'); - var lonField = $('input[name='+gm.attr('data-lonfieldname')+']'); - var zoomField = $('input[name='+gm.attr('data-zoomfieldname')+']'); - var guidePointsAttr = gm.attr('data-GuidePoints'); - - // if we have emtpy initial values, set them appropriately, otherwise google maps code goes into an infinite tailspin - if (latField.val() === '') { - latField.val(0); - } - - if (lonField.val() === '') { - lonField.val(0); - } - - if (zoomField.val() === '') { - zoomField.val(2); - } - - - var guidePoints = []; - if (typeof guidePointsAttr != "undefined") { - guidePoints = JSON.parse(guidePointsAttr); - } - - - myOptions.center = new google.maps.LatLng(latField.val(), lonField.val()); - - if (zoomField.length) { - myOptions.zoom = parseInt(zoomField.val(),10); - } - - - map = new google.maps.Map(document.getElementById("GoogleMap"), myOptions); - bounds = new google.maps.LatLngBounds (); - - // guide points are grey marked out pins that are used as contextual hints to the current desired location - // An example of this would be photographs taken on the same bike ride or walk - if (guidePoints.length) { - var sumlat = 0; - var sumlon = 0; - for (var i = guidePoints.length - 1; i >= 0; i--) { - var lat = guidePoints[i].latitude; - var lon = guidePoints[i].longitude; - addGuideMarker(lat,lon); - var latlng = new google.maps.LatLng(lat, lon); - sumlat = sumlat + parseFloat(lat); - sumlon = sumlon + parseFloat(lon); - - // extend bounds - bounds.extend (latlng); - } - - - if ((latField.val() === 0) && (lonField.val() === 0)) { - var nPoints = guidePoints.length; - var newMarkerPos = new google.maps.LatLng(sumlat/nPoints, sumlon/nPoints); - } - - map.fitBounds(bounds); - } - - if (latField.val() && lonField.val()) { - marker = null; - - setMarker(myOptions.center, true); - } - - - // when one right clicks, set the red marker flag to that coordinate - google.maps.event.addListener(map, "rightclick", function(event) { - var lat = event.latLng.lat(); - var lng = event.latLng.lng(); - latField.val(lat); - lonField.val(lng); - setMarker(event.latLng, false); - statusMessage('Location changed to '+lat+','+lng); - }); - - google.maps.event.addListener(map, "zoom_changed", function(e) { - if (zoomField.length) { - zoomField.val(map.getZoom()); - } - }); - - google.maps.event.trigger(map, 'resize'); - map.setZoom( map.getZoom() ); - - - // When any tab is clicked, resize the map - $('.ui-tabs-anchor').click(function() { - google.maps.event.trigger(map, 'resize'); - var gm = $('#GoogleMap'); - var useMapBounds = gm.attr('data-usemapbounds'); - if (useMapBounds) { - map.fitBounds(bounds); - } else { - map.setCenter(marker.getPosition()); - } - }); - - })(jQuery); - - } - - - // utility functions - - function addGuideMarker(lat,lon) { - var latlng = new google.maps.LatLng(lat, lon); - var pinColor = "CCCCCC"; - var pinImage = new google.maps.MarkerImage("//chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColor, - new google.maps.Size(21, 34), - new google.maps.Point(0,0), - new google.maps.Point(10, 34)); - var pinShadow = new google.maps.MarkerImage("//chart.apis.google.com/chart?chst=d_map_pin_shadow", - new google.maps.Size(40, 37), - new google.maps.Point(0, 0), - new google.maps.Point(12, 35)); - var guideMarker = new google.maps.Marker({ - position: latlng, - title: "Marker", - icon: pinImage, - shadow: pinShadow - }); - guideMarker.setMap(map); - - } - - function setMarker(location, recenter) { - if (marker !== null) { - marker.setPosition(location); - } else { - marker = new google.maps.Marker({ - position: location, - title: "Position", - draggable: true - }); - marker.setMap(map); - google.maps.event.addListener(marker, 'dragend', setCoordByMarker); - } - - if (recenter) { - map.setCenter(location); - } - } - - - - function setCoordByMarker(event) { - (function($) { - - var gm = $('#GoogleMap'); - - var latField = $('input[name='+gm.attr('data-latfieldname')+']'); - var lonField = $('input[name='+gm.attr('data-lonfieldname')+']'); - var zoomField = $('input[name='+gm.attr('data-zoomfieldname')+']'); - - var lat = event.latLng.lat(); - var lng = event.latLng.lng(); - latField.val(lat); - lonField.val(lng); - setMarker(event.latLng, true); - - - statusMessage('Location changed to '+lat+','+lng); - - - if (zoomField.length) { - zoomField.val(map.getZoom()); - } - - map.setCenter(event.latLng); - - })(jQuery); - - } - - - - function searchForAddress(address) { - (function($) { - - var geocoder = new google.maps.Geocoder(); - var elevator = new google.maps.ElevationService(); - - - if (geocoder) { - statusMessage("Searching for:" + address); - geocoder.geocode({ - 'address': address - }, function(results, status) { - if (status == google.maps.GeocoderStatus.OK) { - var l = results.length; - - if (l > 0) { - statusMessage("Places found"); - } else if (l === 0) { - errorMessage("No places found"); - } - - var html = '
    '; - //mapSearchResults - $.each(results, function(index, value) { - var address = []; - $.each(value.address_components, function(i, v) { - address.push(v.long_name); - }); - - html = html + '
  • ' + address + "
  • "; - }); - - html = html + "
"; - - $('#mapSearchResults').html(html); - - - // setMarker(results[0].geometry.location.lat); - } else { - errorMessage("Unable to find any geocoded results"); - } - }); - - } - - })(jQuery); - - } - - - - // prime livequery events - - - function initLivequery() { - (function($) { - - //triggers - $('input[name=action_GetCoords]').livequery('click', function(e) { - // get the data needed to ask coords - var location = $('#Form_EditForm_Location').val(); - setCoordByAddress(location); - return false; - }); - - - $('#searchLocationButton').livequery('click', function(e) { - // get the data needed to ask coords - var location = $('#location_search').val(); - searchForAddress(location); - return false; - }); - - //geocodedSearchResults - $('.geocodedSearchResults li').livequery('click', function(e) { - // get the data needed to ask coords - var t = $(this); - var lat = t.attr("lat"); - var lon = t.attr("lon"); - var address = t.html(); - var latlng = new google.maps.LatLng(lat, lon); - statusMessage("Setting map to " + address); - $('.geocodedSearchResults').html(''); - $('#Form_EditForm_Latitude').val(lat); - $('#Form_EditForm_Longitude').val(lon); - - var gm = $('#GoogleMap'); - - - var latField = $('input[name='+gm.attr('data-latfieldname')+']'); - var lonField = $('input[name='+gm.attr('data-lonfieldname')+']'); - var zoomField = $('input[name='+gm.attr('data-zoomfieldname')+']'); - - latField.val(lat); - lonField.val(lon); - - // zoom in to an appropriate level - map.setZoom(12); - - setMarker(latlng, true); - return false; - }); - - - $('#GoogleMap').livequery(function() { - initMap(); - }); - - })(jQuery); - - - } - - - - (function($) { - function loadGoogleMapsAPI() { - var script = document.createElement("script"); - script.type = "text/javascript"; - script.src = "//maps.googleapis.com/maps/api/js?sensor=false&callback=gmloaded"; - document.body.appendChild(script); - } - - - // deal with document ready - note this only gets called once due to the way silverstripe works, until the CMS is refreshed - $(document).ready(function() { - loadGoogleMapsAPI(); - }); - })(jQuery); \ No newline at end of file +var bounds; + +function gmloaded() { + initLivequery(); +} + + +// initialise the map +function initMap() { + var myOptions = { + zoom: 16, + disableDefaultUI: false, + mapTypeId: google.maps.MapTypeId.ROADMAP, + disableDoubleClickZoom: false, + draggable: true, + keyboardShortcuts: false, + scrollwheel: true + }; + + (function($) { + var gm = $('#GoogleMap'); + var latFieldName = gm.attr('data-latfieldname'); + + var latField = $('input[name=' + gm.attr('data-latfieldname') + ']'); + var lonField = $('input[name=' + gm.attr('data-lonfieldname') + ']'); + var zoomField = $('input[name=' + gm.attr('data-zoomfieldname') + ']'); + var guidePointsAttr = gm.attr('data-GuidePoints'); + + // if we have emtpy initial values, set them appropriately, + // otherwise googlemaps codegoes into an infinite tailspin + if (latField.val() === '') { + latField.val(0); + } + + if (lonField.val() === '') { + lonField.val(0); + } + + if (zoomField.val() === '') { + zoomField.val(2); + } + + var guidePoints = []; + if (typeof guidePointsAttr != "undefined") { + guidePoints = JSON.parse(guidePointsAttr); + } + + myOptions.center = new google.maps.LatLng(latField.val(), lonField.val()); + if (zoomField.length) { + myOptions.zoom = parseInt(zoomField.val(), 10); + } + + map = new google.maps.Map(document.getElementById("GoogleMap"), myOptions); + bounds = new google.maps.LatLngBounds(); + + // guide points are grey marked out pins that are used as contextual hints to the current + // desired location. An example of this would be photographs taken on the same bike + // ride or a walk + if (guidePoints.length) { + var sumlat = 0; + var sumlon = 0; + for (var i = guidePoints.length - 1; i >= 0; i--) { + var lat = guidePoints[i].latitude; + var lon = guidePoints[i].longitude; + addGuideMarker(lat, lon); + var latlng = new google.maps.LatLng(lat, lon); + sumlat = sumlat + parseFloat(lat); + sumlon = sumlon + parseFloat(lon); + + // extend bounds + bounds.extend(latlng); + } + + if ((latField.val() === 0) && (lonField.val() === 0)) { + var nPoints = guidePoints.length; + var newMarkerPos = new google.maps.LatLng(sumlat / nPoints, sumlon / nPoints); + } + + map.fitBounds(bounds); + } + + if (latField.val() && lonField.val()) { + marker = null; + setMarker(myOptions.center, true); + } + + // when one right clicks, set the red marker flag to that coordinate + google.maps.event.addListener(map, "rightclick", function(event) { + var lat = event.latLng.lat(); + var lng = event.latLng.lng(); + latField.val(lat); + lonField.val(lng); + setMarker(event.latLng, false); + statusMessage('Location changed to ' + lat + ',' + lng); + }); + + google.maps.event.addListener(map, "zoom_changed", function(e) { + if (zoomField.length) { + zoomField.val(map.getZoom()); + } + }); + + google.maps.event.trigger(map, 'resize'); + map.setZoom(map.getZoom()); + + // When any tab is clicked, resize the map + $('.ui-tabs-anchor').click(function() { + google.maps.event.trigger(map, 'resize'); + var gm = $('#GoogleMap'); + var useMapBounds = gm.attr('data-usemapbounds'); + if (useMapBounds) { + map.fitBounds(bounds); + } else { + map.setCenter(marker.getPosition()); + } + }); + + })(jQuery); + +} + + +// utility functions +function addGuideMarker(lat, lon) { + var latlng = new google.maps.LatLng(lat, lon); + var pinColor = "CCCCCC"; + var pinImage = new google.maps.MarkerImage("//chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColor, + new google.maps.Size(21, 34), + new google.maps.Point(0, 0), + new google.maps.Point(10, 34)); + var pinShadow = new google.maps.MarkerImage("//chart.apis.google.com/chart?chst=d_map_pin_shadow", + new google.maps.Size(40, 37), + new google.maps.Point(0, 0), + new google.maps.Point(12, 35)); + var guideMarker = new google.maps.Marker({ + position: latlng, + title: "Marker", + icon: pinImage, + shadow: pinShadow + }); + guideMarker.setMap(map); + +} + + +function setMarker(location, recenter) { + if (marker !== null) { + marker.setPosition(location); + } else { + marker = new google.maps.Marker({ + position: location, + title: "Position", + draggable: true + }); + marker.setMap(map); + google.maps.event.addListener(marker, 'dragend', setCoordByMarker); + } + + if (recenter) { + map.setCenter(location); + } +} + + +function setCoordByMarker(event) { + (function($) { + var gm = $('#GoogleMap'); + var latField = $('input[name=' + gm.attr('data-latfieldname') + ']'); + var lonField = $('input[name=' + gm.attr('data-lonfieldname') + ']'); + var zoomField = $('input[name=' + gm.attr('data-zoomfieldname') + ']'); + var lat = event.latLng.lat(); + var lng = event.latLng.lng(); + latField.val(lat); + lonField.val(lng); + setMarker(event.latLng, true); + statusMessage('Location changed to ' + lat + ',' + lng); + if (zoomField.length) { + zoomField.val(map.getZoom()); + } + map.setCenter(event.latLng); + })(jQuery); +} + + +function searchForAddress(address) { + (function($) { + var geocoder = new google.maps.Geocoder(); + var elevator = new google.maps.ElevationService(); + if (geocoder) { + statusMessage("Searching for:" + address); + geocoder.geocode({ + 'address': address + }, function(results, status) { + if (status == google.maps.GeocoderStatus.OK) { + var l = results.length; + if (l > 0) { + statusMessage("Places found"); + } else if (l === 0) { + errorMessage("No places found"); + } + var html = '
    '; + //mapSearchResults + $.each(results, function(index, value) { + var address = []; + $.each(value.address_components, function(i, v) { + address.push(v.long_name); + }); + html = html + '
  • ' + address + "
  • "; + }); + html = html + "
"; + $('#mapSearchResults').html(html); + } else { + errorMessage("Unable to find any geocoded results"); + } + }); + } + })(jQuery); +} + + +// prime livequery events +function initLivequery() { + (function($) { + + //triggers + $('input[name=action_GetCoords]').livequery('click', function(e) { + // get the data needed to ask coords + var location = $('#Form_EditForm_Location').val(); + setCoordByAddress(location); + return false; + }); + + $('#searchLocationButton').livequery('click', function(e) { + // get the data needed to ask coords + var location = $('#location_search').val(); + searchForAddress(location); + return false; + }); + + //geocodedSearchResults + $('.geocodedSearchResults li').livequery('click', function(e) { + // get the data needed to ask coords + var t = $(this); + var lat = t.attr("lat"); + var lon = t.attr("lon"); + var address = t.html(); + var latlng = new google.maps.LatLng(lat, lon); + statusMessage("Setting map to " + address); + $('.geocodedSearchResults').html(''); + $('#Form_EditForm_Latitude').val(lat); + $('#Form_EditForm_Longitude').val(lon); + + var gm = $('#GoogleMap'); + var latField = $('input[name=' + gm.attr('data-latfieldname') + ']'); + var lonField = $('input[name=' + gm.attr('data-lonfieldname') + ']'); + var zoomField = $('input[name=' + gm.attr('data-zoomfieldname') + ']'); + + latField.val(lat); + lonField.val(lon); + + // zoom in to an appropriate level + map.setZoom(12); + + setMarker(latlng, true); + return false; + }); + $('#GoogleMap').livequery(function() { + initMap(); + }); + })(jQuery); +} + + +(function($) { + function loadGoogleMapsAPI() { + var script = document.createElement("script"); + script.type = "text/javascript"; + script.src = "//maps.googleapis.com/maps/api/js?sensor=false&callback=gmloaded"; + document.body.appendChild(script); + } + + + // deal with document ready - note this only gets called once due to the way silverstripe works, until the CMS is refreshed + $(document).ready(function() { + loadGoogleMapsAPI(); + }); +})(jQuery); diff --git a/lang/en.yml b/lang/en.yml index e65c8c6..19fbaa3 100644 --- a/lang/en.yml +++ b/lang/en.yml @@ -2,4 +2,4 @@ en: POIMapPage: PLURALNAME: 'Points of Interest Pages' SINGULARNAME: 'Points of Interest Page' - DESCRIPTION: 'Page containing layers of points of interest that can be rendered as a map' \ No newline at end of file + DESCRIPTION: 'Page containing layers of points of interest that can be rendered as a map' From 7ccfad85f3e0739551b4f2546ffd398492e60a8f Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 17 Mar 2015 16:24:17 +0700 Subject: [PATCH 186/354] FIX: Can now use escape key to exit fullscreen. Fixes #9 --- javascript/google/FullScreenControl.js | 265 +++++++++++++------------ 1 file changed, 137 insertions(+), 128 deletions(-) diff --git a/javascript/google/FullScreenControl.js b/javascript/google/FullScreenControl.js index 6368b4b..ff20f2d 100644 --- a/javascript/google/FullScreenControl.js +++ b/javascript/google/FullScreenControl.js @@ -1,128 +1,137 @@ -/// -function FullScreenControl(map, enterFull, exitFull) { - if (enterFull === void 0) { enterFull = null; } - if (exitFull === void 0) { exitFull = null; } - if (enterFull == null) { - enterFull = "Full screen"; - } - if (exitFull == null) { - exitFull = "Exit full screen"; - } - var controlDiv = document.createElement("div"); - controlDiv.className = "fullScreen"; - controlDiv.index = 1; - controlDiv.style.padding = "5px"; - // Set CSS for the control border. - var controlUI = document.createElement("div"); - controlUI.style.backgroundColor = "white"; - controlUI.style.borderStyle = "solid"; - controlUI.style.borderWidth = "1px"; - controlUI.style.borderColor = "#717b87"; - controlUI.style.cursor = "pointer"; - controlUI.style.textAlign = "center"; - controlUI.style.boxShadow = "rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px"; - controlDiv.appendChild(controlUI); - // Set CSS for the control interior. - var controlText = document.createElement("div"); - controlText.style.fontFamily = "Roboto,Arial,sans-serif"; - controlText.style.fontSize = "11px"; - controlText.style.fontWeight = "400"; - controlText.style.paddingTop = "1px"; - controlText.style.paddingBottom = "1px"; - controlText.style.paddingLeft = "6px"; - controlText.style.paddingRight = "6px"; - controlText.innerHTML = "" + enterFull + ""; - controlUI.appendChild(controlText); - // set print CSS so the control is hidden - var head = document.getElementsByTagName("head")[0]; - var newStyle = document.createElement("style"); - newStyle.setAttribute("type", "text/css"); - newStyle.setAttribute("media", "print"); - var cssText = ".fullScreen { display: none;}"; - var texNode = document.createTextNode(cssText); - try { - newStyle.appendChild(texNode); - } - catch (e) { - // IE8 hack - newStyle.styleSheet.cssText = cssText; - } - head.appendChild(newStyle); - var fullScreen = false; - var interval; - var mapDiv = map.getDiv(); - var divStyle = mapDiv.style; - if (mapDiv.runtimeStyle) { - divStyle = mapDiv.runtimeStyle; - } - var originalPos = divStyle.position; - var originalWidth = divStyle.width; - var originalHeight = divStyle.height; - // IE8 hack - if (originalWidth === "") { - originalWidth = mapDiv.style.width; - } - if (originalHeight === "") { - originalHeight = mapDiv.style.height; - } - var originalTop = divStyle.top; - var originalLeft = divStyle.left; - var originalZIndex = divStyle.zIndex; - var bodyStyle = document.body.style; - if (document.body.runtimeStyle) { - bodyStyle = document.body.runtimeStyle; - } - var originalOverflow = bodyStyle.overflow; - controlDiv.goFullScreen = function () { - var center = map.getCenter(); - mapDiv.style.position = "fixed"; - mapDiv.style.width = "100%"; - mapDiv.style.height = "100%"; - mapDiv.style.top = "0"; - mapDiv.style.left = "0"; - mapDiv.style.zIndex = "100"; - document.body.style.overflow = "hidden"; - controlText.innerHTML = "" + exitFull + ""; - fullScreen = true; - google.maps.event.trigger(map, "resize"); - map.setCenter(center); - // this works around street view causing the map to disappear, which is caused by Google Maps setting the - // CSS position back to relative. There is no event triggered when Street View is shown hence the use of setInterval - interval = setInterval(function () { - if (mapDiv.style.position !== "fixed") { - mapDiv.style.position = "fixed"; - google.maps.event.trigger(map, "resize"); - } - }, 100); - }; - controlDiv.exitFullScreen = function () { - var center = map.getCenter(); - if (originalPos === "") { - mapDiv.style.position = "relative"; - } - else { - mapDiv.style.position = originalPos; - } - mapDiv.style.width = originalWidth; - mapDiv.style.height = originalHeight; - mapDiv.style.top = originalTop; - mapDiv.style.left = originalLeft; - mapDiv.style.zIndex = originalZIndex; - document.body.style.overflow = originalOverflow; - controlText.innerHTML = "" + enterFull + ""; - fullScreen = false; - google.maps.event.trigger(map, "resize"); - map.setCenter(center); - clearInterval(interval); - }; - // Setup the click event listener - google.maps.event.addDomListener(controlUI, "click", function () { - if (!fullScreen) { - controlDiv.goFullScreen(); - } - else { - controlDiv.exitFullScreen(); - } - }); - return controlDiv; -} +/// +function FullScreenControl(map, enterFull, exitFull) { + if (enterFull === void 0) { enterFull = null; } + if (exitFull === void 0) { exitFull = null; } + if (enterFull == null) { + enterFull = "Full screen"; + } + if (exitFull == null) { + exitFull = "Exit full screen"; + } + var controlDiv = document.createElement("div"); + controlDiv.className = "fullScreen"; + controlDiv.index = 1; + controlDiv.style.padding = "5px"; + // Set CSS for the control border. + var controlUI = document.createElement("div"); + controlUI.style.backgroundColor = "white"; + controlUI.style.borderStyle = "solid"; + controlUI.style.borderWidth = "1px"; + controlUI.style.borderColor = "#717b87"; + controlUI.style.cursor = "pointer"; + controlUI.style.textAlign = "center"; + controlUI.style.boxShadow = "rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px"; + controlDiv.appendChild(controlUI); + // Set CSS for the control interior. + var controlText = document.createElement("div"); + controlText.style.fontFamily = "Roboto,Arial,sans-serif"; + controlText.style.fontSize = "11px"; + controlText.style.fontWeight = "400"; + controlText.style.paddingTop = "1px"; + controlText.style.paddingBottom = "1px"; + controlText.style.paddingLeft = "6px"; + controlText.style.paddingRight = "6px"; + controlText.innerHTML = "" + enterFull + ""; + controlUI.appendChild(controlText); + // set print CSS so the control is hidden + var head = document.getElementsByTagName("head")[0]; + var newStyle = document.createElement("style"); + newStyle.setAttribute("type", "text/css"); + newStyle.setAttribute("media", "print"); + var cssText = ".fullScreen { display: none;}"; + var texNode = document.createTextNode(cssText); + try { + newStyle.appendChild(texNode); + } + catch (e) { + // IE8 hack + newStyle.styleSheet.cssText = cssText; + } + head.appendChild(newStyle); + var fullScreen = false; + var interval; + var mapDiv = map.getDiv(); + var divStyle = mapDiv.style; + if (mapDiv.runtimeStyle) { + divStyle = mapDiv.runtimeStyle; + } + var originalPos = divStyle.position; + var originalWidth = divStyle.width; + var originalHeight = divStyle.height; + // IE8 hack + if (originalWidth === "") { + originalWidth = mapDiv.style.width; + } + if (originalHeight === "") { + originalHeight = mapDiv.style.height; + } + var originalTop = divStyle.top; + var originalLeft = divStyle.left; + var originalZIndex = divStyle.zIndex; + var bodyStyle = document.body.style; + if (document.body.runtimeStyle) { + bodyStyle = document.body.runtimeStyle; + } + var originalOverflow = bodyStyle.overflow; + controlDiv.goFullScreen = function () { + var center = map.getCenter(); + mapDiv.style.position = "fixed"; + mapDiv.style.width = "100%"; + mapDiv.style.height = "100%"; + mapDiv.style.top = "0"; + mapDiv.style.left = "0"; + mapDiv.style.zIndex = "100"; + document.body.style.overflow = "hidden"; + controlText.innerHTML = "" + exitFull + ""; + fullScreen = true; + google.maps.event.trigger(map, "resize"); + map.setCenter(center); + // this works around street view causing the map to disappear, which is caused by Google Maps setting the + // CSS position back to relative. There is no event triggered when Street View is shown hence the use of setInterval + interval = setInterval(function () { + if (mapDiv.style.position !== "fixed") { + mapDiv.style.position = "fixed"; + google.maps.event.trigger(map, "resize"); + } + }, 100); + }; + controlDiv.exitFullScreen = function () { + var center = map.getCenter(); + if (originalPos === "") { + mapDiv.style.position = "relative"; + } + else { + mapDiv.style.position = originalPos; + } + mapDiv.style.width = originalWidth; + mapDiv.style.height = originalHeight; + mapDiv.style.top = originalTop; + mapDiv.style.left = originalLeft; + mapDiv.style.zIndex = originalZIndex; + document.body.style.overflow = originalOverflow; + controlText.innerHTML = "" + enterFull + ""; + fullScreen = false; + google.maps.event.trigger(map, "resize"); + map.setCenter(center); + clearInterval(interval); + }; + // Setup the click event listener + google.maps.event.addDomListener(controlUI, "click", function () { + if (!fullScreen) { + controlDiv.goFullScreen(); + } + else { + controlDiv.exitFullScreen(); + } + }); + + document.onkeydown = function(evt) { + evt = evt || window.event; + if (evt.keyCode == 27) { + if (fullScreen) { + controlDiv.exitFullScreen(); + } + } + }; + return controlDiv; +} From 743fad1e3bd89c1bae2d9d60ed038136a89e36ee Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 18 Mar 2015 20:58:51 +0700 Subject: [PATCH 187/354] ENHANCEMENT: Switch to new google version of map clusterer. --- javascript/Fluster2.js | 221 ---- javascript/Fluster2.packed.js | 24 - javascript/Fluster2Cluster.js | 191 ---- javascript/Fluster2ClusterMarker.js | 113 -- javascript/Fluster2ProjectionOverlay.js | 43 - javascript/clusterer.js | 21 - javascript/google/markerclusterer.js | 1290 +++++++++++++++++++++++ 7 files changed, 1290 insertions(+), 613 deletions(-) delete mode 100644 javascript/Fluster2.js delete mode 100644 javascript/Fluster2.packed.js delete mode 100644 javascript/Fluster2Cluster.js delete mode 100644 javascript/Fluster2ClusterMarker.js delete mode 100644 javascript/Fluster2ProjectionOverlay.js delete mode 100644 javascript/clusterer.js create mode 100644 javascript/google/markerclusterer.js diff --git a/javascript/Fluster2.js b/javascript/Fluster2.js deleted file mode 100644 index fc444b2..0000000 --- a/javascript/Fluster2.js +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Fluster2 0.1.1 - * Copyright (C) 2009 Fusonic GmbH - * - * This file is part of Fluster2. - * - * Fluster2 is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * Fluster2 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -/** - * Creates a new Fluster to manage masses of markers in a Google Maps v3. - * - * @constructor - * @param {google.maps.Map} the Google Map v3 - * @param {bool} run in debug mode or not - */ -function Fluster2(_map, _debug) -{ - // Private variables - var map = _map; - var projection = new Fluster2ProjectionOverlay(map); - var me = this; - var clusters = new Object(); - var markersLeft = new Object(); - - // Properties - this.debugEnabled = _debug; - this.gridSize = 60; - this.markers = new Array(); - this.currentZoomLevel = -1; - this.styles = { - 0: { - image: '//gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m1.png', - textColor: '#FFFFFF', - width: 53, - height: 52 - }, - 10: { - image: '//gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m2.png', - textColor: '#FFFFFF', - width: 56, - height: 55 - }, - 20: { - image: '//gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m3.png', - textColor: '#FFFFFF', - width: 66, - height: 65 - } - }; - - // Timeouts - var zoomChangedTimeout = null; - - /** - * Create clusters for the current zoom level and assign markers. - */ - function createClusters() - { - var zoom = map.getZoom(); - - if(clusters[zoom]) - { - me.debug('Clusters for zoom level ' + zoom + ' already initialized.'); - } - else - { - // Create clusters array - var clustersThisZoomLevel = new Array(); - - // Set cluster count - var clusterCount = 0; - - // Get marker count - var markerCount = me.markers.length; - - // Walk all markers - for(var i = 0; i < markerCount; i++) - { - var marker = me.markers[i]; - var markerPosition = marker.getPosition(); - var done = false; - - // Find a cluster which contains the marker - for(var j = clusterCount - 1; j >= 0; j--) - { - var cluster = clustersThisZoomLevel[j]; - if(cluster.contains(markerPosition)) - { - cluster.addMarker(marker); - done = true; - break; - } - } - - if(!done) - { - // No cluster found, create a new one - var cluster = new Fluster2Cluster(me, marker); - clustersThisZoomLevel.push(cluster); - - // Increase cluster count - clusterCount++; - } - } - - clusters[zoom] = clustersThisZoomLevel; - - me.debug('Initialized ' + clusters[zoom].length + ' clusters for zoom level ' + zoom + '.'); - } - - // Hide markers of previous zoom level - if(clusters[me.currentZoomLevel]) - { - for(var i = 0; i < clusters[me.currentZoomLevel].length; i++) - { - clusters[me.currentZoomLevel][i].hide(); - } - } - - // Set current zoom level - me.currentZoomLevel = zoom; - - // Show clusters - showClustersInBounds(); - } - - /** - * Displays all clusters inside the current map bounds. - */ - function showClustersInBounds() - { - var mapBounds = map.getBounds(); - - for(var i = 0; i < clusters[me.currentZoomLevel].length; i++) - { - var cluster = clusters[me.currentZoomLevel][i]; - if(mapBounds.contains(cluster.getPosition())) - { - cluster.show(); - } - } - } - - /** - * Callback which is executed 500ms after the map's zoom level has changed. - */ - this.zoomChanged = function() - { - window.clearInterval(zoomChangedTimeout); - zoomChangedTimeout = window.setTimeout(createClusters, 500); - }; - - /** - * Returns the map assigned to this Fluster. - */ - this.getMap = function() - { - return map; - }; - - /** - * Returns the map projection. - */ - this.getProjection = function() - { - return projection.getP(); - }; - - /** - * Prints debug messages to console if debugging is enabled. - */ - this.debug = function(message) - { - if(me.debugEnabled) - { - console.log('Fluster2: ' + message); - } - }; - - /** - * Adds a marker to the Fluster. - */ - this.addMarker = function(_marker) - { - me.markers.push(_marker); - }; - - /** - * Returns the currently assigned styles. - */ - this.getStyles = function() - { - return me.styles; - }; - - /** - * Sets map event handlers and setup's the markers for the current - * map state. - */ - this.initialize = function() - { - // Add event listeners - google.maps.event.addListener(map, 'zoom_changed', this.zoomChanged); - google.maps.event.addListener(map, 'dragend', showClustersInBounds); - - // Setup markers for the current state - window.setTimeout(createClusters, 1000); - }; -} \ No newline at end of file diff --git a/javascript/Fluster2.packed.js b/javascript/Fluster2.packed.js deleted file mode 100644 index 7370b4f..0000000 --- a/javascript/Fluster2.packed.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Fluster2 0.1.1 - * Copyright (C) 2009 Fusonic GmbH - * - * This file is part of Fluster2. - * - * Fluster2 is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * Fluster2 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -function Fluster2(_map,_debug){var map=_map;var projection=new Fluster2ProjectionOverlay(map);var me=this;var clusters=new Object();var markersLeft=new Object();this.debugEnabled=_debug;this.gridSize=60;this.markers=new Array();this.currentZoomLevel=-1;this.styles={0:{image:'//gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m1.png',textColor:'#FFFFFF',width:53,height:52},10:{image:'//gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m2.png',textColor:'#FFFFFF',width:56,height:55},20:{image:'//gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m3.png',textColor:'#FFFFFF',width:66,height:65}};var zoomChangedTimeout=null;function createClusters(){var zoom=map.getZoom();if(clusters[zoom]){me.debug('Clusters for zoom level '+zoom+' already initialized.')}else{var clustersThisZoomLevel=new Array();var clusterCount=0;var markerCount=me.markers.length;for(var i=0;i=0;j--){var cluster=clustersThisZoomLevel[j];if(cluster.contains(markerPosition)){cluster.addMarker(marker);done=true;break}}if(!done){var cluster=new Fluster2Cluster(me,marker);clustersThisZoomLevel.push(cluster);clusterCount++}}clusters[zoom]=clustersThisZoomLevel;me.debug('Initialized '+clusters[zoom].length+' clusters for zoom level '+zoom+'.')}if(clusters[me.currentZoomLevel]){for(var i=0;i1){for(var i=0;ii){this.style=styles[i]}else{break}}google.maps.OverlayView.call(this);this.setMap(this.map);this.draw()};Fluster2ClusterMarker.prototype=new google.maps.OverlayView();Fluster2ClusterMarker.prototype.draw=function(){if(this.div==null){var me=this;this.div=document.createElement('div');this.div.style.position='absolute';this.div.style.width=this.style.width+'px';this.div.style.height=this.style.height+'px';this.div.style.lineHeight=this.style.height+'px';this.div.style.background='transparent url("'+this.style.image+'") 50% 50% no-repeat';this.div.style.color=this.style.textColor;this.div.style.textAlign='center';this.div.style.fontFamily='Arial, Helvetica';this.div.style.fontSize='11px';this.div.style.fontWeight='bold';this.div.innerHTML=this.markerCount;this.div.style.cursor='pointer';google.maps.event.addDomListener(this.div,'click',function(){me.map.fitBounds(me.cluster.getMarkerBounds())});this.getPanes().overlayLayer.appendChild(this.div)}var position=this.getProjection().fromLatLngToDivPixel(this.position);this.div.style.left=(position.x-parseInt(this.style.width/2))+'px';this.div.style.top=(position.y-parseInt(this.style.height/2))+'px'};Fluster2ClusterMarker.prototype.hide=function(){this.div.style.display='none'};Fluster2ClusterMarker.prototype.show=function(){this.div.style.display='block'}; -function Fluster2ProjectionOverlay(map){google.maps.OverlayView.call(this);this.setMap(map);this.getP=function(){return this.getProjection()}}Fluster2ProjectionOverlay.prototype=new google.maps.OverlayView();Fluster2ProjectionOverlay.prototype.draw=function(){}; \ No newline at end of file diff --git a/javascript/Fluster2Cluster.js b/javascript/Fluster2Cluster.js deleted file mode 100644 index 234b9f1..0000000 --- a/javascript/Fluster2Cluster.js +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Fluster2 0.1.1 - * Copyright (C) 2009 Fusonic GmbH - * - * This file is part of Fluster2. - * - * Fluster2 is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * Fluster2 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -/** - * Cluster which holds one or more markers of the map. - * - * @constructor - * @private - * @param {Fluster2} the Fluster2 itself - * @param {google.maps.Marker} the first marker - */ -function Fluster2Cluster(_fluster, _marker) -{ - // Get properties from marker - var markerPosition = _marker.getPosition(); - - // Properties - this.fluster = _fluster; - this.markers = []; - this.bounds = null; - this.marker = null; - this.lngSum = 0; - this.latSum = 0; - this.center = markerPosition; - this.map = this.fluster.getMap(); - - var me = this; - - // Get properties from fluster - var projection = _fluster.getProjection(); - var gridSize = _fluster.gridSize; - - // Calculate bounds - var position = projection.fromLatLngToDivPixel(markerPosition); - var positionSW = new google.maps.Point( - position.x - gridSize, - position.y + gridSize - ); - var positionNE = new google.maps.Point( - position.x + gridSize, - position.y - gridSize - ); - this.bounds = new google.maps.LatLngBounds( - projection.fromDivPixelToLatLng(positionSW), - projection.fromDivPixelToLatLng(positionNE) - ); - - /** - * Adds a marker to the cluster. - */ - this.addMarker = function(_marker) - { - this.markers.push(_marker); - }; - - /** - * Shows either the only marker or a cluster marker instead. - */ - this.show = function() - { - // Show marker if there is only 1 - if(this.markers.length == 1) - { - this.markers[0].setMap(me.map); - } - else if(this.markers.length > 1) - { - // Hide all markers - for(var i = 0; i < this.markers.length; i++) - { - this.markers[i].setMap(null); - } - - // Create marker - if(this.marker == null) - { - this.marker = new Fluster2ClusterMarker(this.fluster, this); - - if(this.fluster.debugEnabled) - { - google.maps.event.addListener(this.marker, 'mouseover', me.debugShowMarkers); - google.maps.event.addListener(this.marker, 'mouseout', me.debugHideMarkers); - } - } - - // Show marker - this.marker.show(); - } - }; - - /** - * Hides the cluster - */ - this.hide = function() - { - if(this.marker != null) - { - this.marker.hide(); - } - }; - - /** - * Shows all markers included by this cluster (debugging only). - */ - this.debugShowMarkers = function() - { - for(var i = 0; i < me.markers.length; i++) - { - me.markers[i].setVisible(true); - } - }; - - /** - * Hides all markers included by this cluster (debugging only). - */ - this.debugHideMarkers = function() - { - for(var i = 0; i < me.markers.length; i++) - { - me.markers[i].setVisible(false); - } - }; - - /** - * Returns the number of markers in this cluster. - */ - this.getMarkerCount = function() - { - return this.markers.length; - }; - - /** - * Checks if the cluster bounds contains the given position. - */ - this.contains = function(_position) - { - return me.bounds.contains(_position); - }; - - /** - * Returns the central point of this cluster's bounds. - */ - this.getPosition = function() - { - return this.center; - }; - - /** - * Returns this cluster's bounds. - */ - this.getBounds = function() - { - return this.bounds; - }; - - /** - * Return the bounds calculated on the markers in this cluster. - */ - this.getMarkerBounds = function() - { - var bounds = new google.maps.LatLngBounds( - me.markers[0].getPosition(), - me.markers[0].getPosition() - ); - for(var i = 1; i < me.markers.length; i++) - { - bounds.extend(me.markers[i].getPosition()); - } - return bounds; - }; - - // Add the first marker - this.addMarker(_marker); -} \ No newline at end of file diff --git a/javascript/Fluster2ClusterMarker.js b/javascript/Fluster2ClusterMarker.js deleted file mode 100644 index 86624cd..0000000 --- a/javascript/Fluster2ClusterMarker.js +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Fluster2 0.1.1 - * Copyright (C) 2009 Fusonic GmbH - * - * This file is part of Fluster2. - * - * Fluster2 is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * Fluster2 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -/** - * A cluster marker which shows a background image and the marker count - * of the assigned cluster. - * - * @constructor - * @private - * @param {Fluster2} the Fluster2 itself - * @param {Fluster2Cluster} the Fluster2Cluster assigned to this marker - */ -function Fluster2ClusterMarker(_fluster, _cluster) -{ - this.fluster = _fluster; - this.cluster = _cluster; - this.position = this.cluster.getPosition(); - this.markerCount = this.cluster.getMarkerCount(); - this.map = this.fluster.getMap(); - this.style = null; - this.div = null; - - // Assign style - var styles = this.fluster.getStyles(); - for(var i in styles) - { - if(this.markerCount > i) - { - this.style = styles[i]; - } - else - { - break; - } - } - - // Basics - google.maps.OverlayView.call(this); - this.setMap(this.map); - - // Draw - this.draw(); -}; - -Fluster2ClusterMarker.prototype = new google.maps.OverlayView(); - -Fluster2ClusterMarker.prototype.draw = function() -{ - if(this.div == null) - { - var me = this; - - // Create div - this.div = document.createElement('div'); - - // Set styles - this.div.style.position = 'absolute'; - this.div.style.width = this.style.width + 'px'; - this.div.style.height = this.style.height + 'px'; - this.div.style.lineHeight = this.style.height + 'px'; - this.div.style.background = 'transparent url("' + this.style.image + '") 50% 50% no-repeat'; - this.div.style.color = this.style.textColor; - - // Marker count - this.div.style.textAlign = 'center'; - this.div.style.fontFamily = 'Arial, Helvetica'; - this.div.style.fontSize = '11px'; - this.div.style.fontWeight = 'bold'; - this.div.innerHTML = this.markerCount; - - // Cursor and onlick - this.div.style.cursor = 'pointer'; - google.maps.event.addDomListener(this.div, 'click', function() { - me.map.fitBounds(me.cluster.getMarkerBounds()); - }); - - this.getPanes().overlayLayer.appendChild(this.div); - } - - // Position - var position = this.getProjection().fromLatLngToDivPixel(this.position); - this.div.style.left = (position.x - parseInt(this.style.width / 2)) + 'px'; - this.div.style.top = (position.y - parseInt(this.style.height / 2)) + 'px'; -}; - -Fluster2ClusterMarker.prototype.hide = function() -{ - // Hide div - this.div.style.display = 'none'; -}; - -Fluster2ClusterMarker.prototype.show = function() -{ - // Show div - this.div.style.display = 'block'; -}; \ No newline at end of file diff --git a/javascript/Fluster2ProjectionOverlay.js b/javascript/Fluster2ProjectionOverlay.js deleted file mode 100644 index dad6921..0000000 --- a/javascript/Fluster2ProjectionOverlay.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Fluster2 0.1.1 - * Copyright (C) 2009 Fusonic GmbH - * - * This file is part of Fluster2. - * - * Fluster2 is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * Fluster2 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - */ - -/** - * An empty overlay which is used to retrieve the map projection panes. - * - * @constructor - * @private - * @param {google.maps.Map} the Google Maps v3 - */ -function Fluster2ProjectionOverlay(map) -{ - google.maps.OverlayView.call(this); - this.setMap(map); - - this.getP = function() - { - return this.getProjection(); - }; -} - -Fluster2ProjectionOverlay.prototype = new google.maps.OverlayView(); - -Fluster2ProjectionOverlay.prototype.draw = function() -{ -}; \ No newline at end of file diff --git a/javascript/clusterer.js b/javascript/clusterer.js deleted file mode 100644 index 4ddf304..0000000 --- a/javascript/clusterer.js +++ /dev/null @@ -1,21 +0,0 @@ -(function(){var d=null;function e(a){return function(b){this[a]=b}}function h(a){return function(){return this[a]}}var j; -function k(a,b,c){this.extend(k,google.maps.OverlayView);this.c=a;this.a=[];this.f=[];this.ca=[53,56,66,78,90];this.j=[];this.A=!1;c=c||{};this.g=c.gridSize||60;this.l=c.minimumClusterSize||2;this.J=c.maxZoom||d;this.j=c.styles||[];this.X=c.imagePath||this.Q;this.W=c.imageExtension||this.P;this.O=!0;if(c.zoomOnClick!=void 0)this.O=c.zoomOnClick;this.r=!1;if(c.averageCenter!=void 0)this.r=c.averageCenter;l(this);this.setMap(a);this.K=this.c.getZoom();var f=this;google.maps.event.addListener(this.c, -"zoom_changed",function(){var a=f.c.getZoom();if(f.K!=a)f.K=a,f.m()});google.maps.event.addListener(this.c,"idle",function(){f.i()});b&&b.length&&this.C(b,!1)}j=k.prototype;j.Q="http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/m";j.P="png";j.extend=function(a,b){return function(a){for(var b in a.prototype)this.prototype[b]=a.prototype[b];return this}.apply(a,[b])};j.onAdd=function(){if(!this.A)this.A=!0,n(this)};j.draw=function(){}; -function l(a){if(!a.j.length)for(var b=0,c;c=a.ca[b];b++)a.j.push({url:a.X+(b+1)+"."+a.W,height:c,width:c})}j.S=function(){for(var a=this.o(),b=new google.maps.LatLngBounds,c=0,f;f=a[c];c++)b.extend(f.getPosition());this.c.fitBounds(b)};j.z=h("j");j.o=h("a");j.V=function(){return this.a.length};j.ba=e("J");j.I=h("J");j.G=function(a,b){for(var c=0,f=a.length,g=f;g!==0;)g=parseInt(g/10,10),c++;c=Math.min(c,b);return{text:f,index:c}};j.$=e("G");j.H=h("G"); -j.C=function(a,b){for(var c=0,f;f=a[c];c++)q(this,f);b||this.i()};function q(a,b){b.s=!1;b.draggable&&google.maps.event.addListener(b,"dragend",function(){b.s=!1;a.L()});a.a.push(b)}j.q=function(a,b){q(this,a);b||this.i()};function r(a,b){var c=-1;if(a.a.indexOf)c=a.a.indexOf(b);else for(var f=0,g;g=a.a[f];f++)if(g==b){c=f;break}if(c==-1)return!1;b.setMap(d);a.a.splice(c,1);return!0}j.Y=function(a,b){var c=r(this,a);return!b&&c?(this.m(),this.i(),!0):!1}; -j.Z=function(a,b){for(var c=!1,f=0,g;g=a[f];f++)g=r(this,g),c=c||g;if(!b&&c)return this.m(),this.i(),!0};j.U=function(){return this.f.length};j.getMap=h("c");j.setMap=e("c");j.w=h("g");j.aa=e("g"); -j.v=function(a){var b=this.getProjection(),c=new google.maps.LatLng(a.getNorthEast().lat(),a.getNorthEast().lng()),f=new google.maps.LatLng(a.getSouthWest().lat(),a.getSouthWest().lng()),c=b.fromLatLngToDivPixel(c);c.x+=this.g;c.y-=this.g;f=b.fromLatLngToDivPixel(f);f.x-=this.g;f.y+=this.g;c=b.fromDivPixelToLatLng(c);b=b.fromDivPixelToLatLng(f);a.extend(c);a.extend(b);return a};j.R=function(){this.m(!0);this.a=[]}; -j.m=function(a){for(var b=0,c;c=this.f[b];b++)c.remove();for(b=0;c=this.a[b];b++)c.s=!1,a&&c.setMap(d);this.f=[]};j.L=function(){var a=this.f.slice();this.f.length=0;this.m();this.i();window.setTimeout(function(){for(var b=0,c;c=a[b];b++)c.remove()},0)};j.i=function(){n(this)}; -function n(a){if(a.A)for(var b=a.v(new google.maps.LatLngBounds(a.c.getBounds().getSouthWest(),a.c.getBounds().getNorthEast())),c=0,f;f=a.a[c];c++)if(!f.s&&b.contains(f.getPosition())){for(var g=a,u=4E4,o=d,v=0,m=void 0;m=g.f[v];v++){var i=m.getCenter();if(i){var p=f.getPosition();if(!i||!p)i=0;else var w=(p.lat()-i.lat())*Math.PI/180,x=(p.lng()-i.lng())*Math.PI/180,i=Math.sin(w/2)*Math.sin(w/2)+Math.cos(i.lat()*Math.PI/180)*Math.cos(p.lat()*Math.PI/180)*Math.sin(x/2)*Math.sin(x/2),i=6371*2*Math.atan2(Math.sqrt(i), -Math.sqrt(1-i));i=this.l&&a.setMap(d); -a=this.c.getZoom();if((b=this.k.I())&&a>b)for(a=0;b=this.a[a];a++)b.setMap(this.c);else if(this.a.length0&&a.e[0]0&&a.e[1] + * This is a v3 implementation of the + * v2 MarkerClusterer. + */ + +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * A Marker Clusterer that clusters markers. + * + * @param {google.maps.Map} map The Google map to attach to. + * @param {Array.=} opt_markers Optional markers to add to + * the cluster. + * @param {Object=} opt_options support the following options: + * 'gridSize': (number) The grid size of a cluster in pixels. + * 'maxZoom': (number) The maximum zoom level that a marker can be part of a + * cluster. + * 'zoomOnClick': (boolean) Whether the default behaviour of clicking on a + * cluster is to zoom into it. + * 'averageCenter': (boolean) Wether the center of each cluster should be + * the average of all markers in the cluster. + * 'minimumClusterSize': (number) The minimum number of markers to be in a + * cluster before the markers are hidden and a count + * is shown. + * 'styles': (object) An object that has style properties: + * 'url': (string) The image url. + * 'height': (number) The image height. + * 'width': (number) The image width. + * 'anchor': (Array) The anchor position of the label text. + * 'textColor': (string) The text color. + * 'textSize': (number) The text size. + * 'backgroundPosition': (string) The position of the backgound x, y. + * @constructor + * @extends google.maps.OverlayView + */ +function MarkerClusterer(map, opt_markers, opt_options) { + // MarkerClusterer implements google.maps.OverlayView interface. We use the + // extend function to extend MarkerClusterer with google.maps.OverlayView + // because it might not always be available when the code is defined so we + // look for it at the last possible moment. If it doesn't exist now then + // there is no point going ahead :) + this.extend(MarkerClusterer, google.maps.OverlayView); + this.map_ = map; + + /** + * @type {Array.} + * @private + */ + this.markers_ = []; + + /** + * @type {Array.} + */ + this.clusters_ = []; + + this.sizes = [53, 56, 66, 78, 90]; + + /** + * @private + */ + this.styles_ = []; + + /** + * @type {boolean} + * @private + */ + this.ready_ = false; + + var options = opt_options || {}; + + /** + * @type {number} + * @private + */ + this.gridSize_ = options['gridSize'] || 60; + + /** + * @private + */ + this.minClusterSize_ = options['minimumClusterSize'] || 2; + + + /** + * @type {?number} + * @private + */ + this.maxZoom_ = options['maxZoom'] || null; + + this.styles_ = options['styles'] || []; + + /** + * @type {string} + * @private + */ + this.imagePath_ = options['imagePath'] || + this.MARKER_CLUSTER_IMAGE_PATH_; + + /** + * @type {string} + * @private + */ + this.imageExtension_ = options['imageExtension'] || + this.MARKER_CLUSTER_IMAGE_EXTENSION_; + + /** + * @type {boolean} + * @private + */ + this.zoomOnClick_ = true; + + if (options['zoomOnClick'] != undefined) { + this.zoomOnClick_ = options['zoomOnClick']; + } + + /** + * @type {boolean} + * @private + */ + this.averageCenter_ = false; + + if (options['averageCenter'] != undefined) { + this.averageCenter_ = options['averageCenter']; + } + + this.setupStyles_(); + + this.setMap(map); + + /** + * @type {number} + * @private + */ + this.prevZoom_ = this.map_.getZoom(); + + // Add the map event listeners + var that = this; + google.maps.event.addListener(this.map_, 'zoom_changed', function() { + var zoom = that.map_.getZoom(); + + if (that.prevZoom_ != zoom) { + that.prevZoom_ = zoom; + that.resetViewport(); + } + }); + + google.maps.event.addListener(this.map_, 'idle', function() { + that.redraw(); + }); + + // Finally, add the markers + if (opt_markers && opt_markers.length) { + this.addMarkers(opt_markers, false); + } +} + + +/** + * The marker cluster image path. + * + * @type {string} + * @private + */ +MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_ = + 'http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/' + + 'images/m'; + + +/** + * The marker cluster image path. + * + * @type {string} + * @private + */ +MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_ = 'png'; + + +/** + * Extends a objects prototype by anothers. + * + * @param {Object} obj1 The object to be extended. + * @param {Object} obj2 The object to extend with. + * @return {Object} The new extended object. + * @ignore + */ +MarkerClusterer.prototype.extend = function(obj1, obj2) { + return (function(object) { + for (var property in object.prototype) { + this.prototype[property] = object.prototype[property]; + } + return this; + }).apply(obj1, [obj2]); +}; + + +/** + * Implementaion of the interface method. + * @ignore + */ +MarkerClusterer.prototype.onAdd = function() { + this.setReady_(true); +}; + +/** + * Implementaion of the interface method. + * @ignore + */ +MarkerClusterer.prototype.draw = function() {}; + +/** + * Sets up the styles object. + * + * @private + */ +MarkerClusterer.prototype.setupStyles_ = function() { + if (this.styles_.length) { + return; + } + + for (var i = 0, size; size = this.sizes[i]; i++) { + this.styles_.push({ + url: this.imagePath_ + (i + 1) + '.' + this.imageExtension_, + height: size, + width: size + }); + } +}; + +/** + * Fit the map to the bounds of the markers in the clusterer. + */ +MarkerClusterer.prototype.fitMapToMarkers = function() { + var markers = this.getMarkers(); + var bounds = new google.maps.LatLngBounds(); + for (var i = 0, marker; marker = markers[i]; i++) { + bounds.extend(marker.getPosition()); + } + + this.map_.fitBounds(bounds); +}; + + +/** + * Sets the styles. + * + * @param {Object} styles The style to set. + */ +MarkerClusterer.prototype.setStyles = function(styles) { + this.styles_ = styles; +}; + + +/** + * Gets the styles. + * + * @return {Object} The styles object. + */ +MarkerClusterer.prototype.getStyles = function() { + return this.styles_; +}; + + +/** + * Whether zoom on click is set. + * + * @return {boolean} True if zoomOnClick_ is set. + */ +MarkerClusterer.prototype.isZoomOnClick = function() { + return this.zoomOnClick_; +}; + +/** + * Whether average center is set. + * + * @return {boolean} True if averageCenter_ is set. + */ +MarkerClusterer.prototype.isAverageCenter = function() { + return this.averageCenter_; +}; + + +/** + * Returns the array of markers in the clusterer. + * + * @return {Array.} The markers. + */ +MarkerClusterer.prototype.getMarkers = function() { + return this.markers_; +}; + + +/** + * Returns the number of markers in the clusterer + * + * @return {Number} The number of markers. + */ +MarkerClusterer.prototype.getTotalMarkers = function() { + return this.markers_.length; +}; + + +/** + * Sets the max zoom for the clusterer. + * + * @param {number} maxZoom The max zoom level. + */ +MarkerClusterer.prototype.setMaxZoom = function(maxZoom) { + this.maxZoom_ = maxZoom; +}; + + +/** + * Gets the max zoom for the clusterer. + * + * @return {number} The max zoom level. + */ +MarkerClusterer.prototype.getMaxZoom = function() { + return this.maxZoom_; +}; + + +/** + * The function for calculating the cluster icon image. + * + * @param {Array.} markers The markers in the clusterer. + * @param {number} numStyles The number of styles available. + * @return {Object} A object properties: 'text' (string) and 'index' (number). + * @private + */ +MarkerClusterer.prototype.calculator_ = function(markers, numStyles) { + var index = 0; + var count = markers.length; + var dv = count; + while (dv !== 0) { + dv = parseInt(dv / 10, 10); + index++; + } + + index = Math.min(index, numStyles); + return { + text: count, + index: index + }; +}; + + +/** + * Set the calculator function. + * + * @param {function(Array, number)} calculator The function to set as the + * calculator. The function should return a object properties: + * 'text' (string) and 'index' (number). + * + */ +MarkerClusterer.prototype.setCalculator = function(calculator) { + this.calculator_ = calculator; +}; + + +/** + * Get the calculator function. + * + * @return {function(Array, number)} the calculator function. + */ +MarkerClusterer.prototype.getCalculator = function() { + return this.calculator_; +}; + + +/** + * Add an array of markers to the clusterer. + * + * @param {Array.} markers The markers to add. + * @param {boolean=} opt_nodraw Whether to redraw the clusters. + */ +MarkerClusterer.prototype.addMarkers = function(markers, opt_nodraw) { + for (var i = 0, marker; marker = markers[i]; i++) { + this.pushMarkerTo_(marker); + } + if (!opt_nodraw) { + this.redraw(); + } +}; + + +/** + * Pushes a marker to the clusterer. + * + * @param {google.maps.Marker} marker The marker to add. + * @private + */ +MarkerClusterer.prototype.pushMarkerTo_ = function(marker) { + marker.isAdded = false; + if (marker['draggable']) { + // If the marker is draggable add a listener so we update the clusters on + // the drag end. + var that = this; + google.maps.event.addListener(marker, 'dragend', function() { + marker.isAdded = false; + that.repaint(); + }); + } + this.markers_.push(marker); +}; + + +/** + * Adds a marker to the clusterer and redraws if needed. + * + * @param {google.maps.Marker} marker The marker to add. + * @param {boolean=} opt_nodraw Whether to redraw the clusters. + */ +MarkerClusterer.prototype.addMarker = function(marker, opt_nodraw) { + this.pushMarkerTo_(marker); + if (!opt_nodraw) { + this.redraw(); + } +}; + + +/** + * Removes a marker and returns true if removed, false if not + * + * @param {google.maps.Marker} marker The marker to remove + * @return {boolean} Whether the marker was removed or not + * @private + */ +MarkerClusterer.prototype.removeMarker_ = function(marker) { + var index = -1; + if (this.markers_.indexOf) { + index = this.markers_.indexOf(marker); + } else { + for (var i = 0, m; m = this.markers_[i]; i++) { + if (m == marker) { + index = i; + break; + } + } + } + + if (index == -1) { + // Marker is not in our list of markers. + return false; + } + + marker.setMap(null); + + this.markers_.splice(index, 1); + + return true; +}; + + +/** + * Remove a marker from the cluster. + * + * @param {google.maps.Marker} marker The marker to remove. + * @param {boolean=} opt_nodraw Optional boolean to force no redraw. + * @return {boolean} True if the marker was removed. + */ +MarkerClusterer.prototype.removeMarker = function(marker, opt_nodraw) { + var removed = this.removeMarker_(marker); + + if (!opt_nodraw && removed) { + this.resetViewport(); + this.redraw(); + return true; + } else { + return false; + } +}; + + +/** + * Removes an array of markers from the cluster. + * + * @param {Array.} markers The markers to remove. + * @param {boolean=} opt_nodraw Optional boolean to force no redraw. + */ +MarkerClusterer.prototype.removeMarkers = function(markers, opt_nodraw) { + var removed = false; + + for (var i = 0, marker; marker = markers[i]; i++) { + var r = this.removeMarker_(marker); + removed = removed || r; + } + + if (!opt_nodraw && removed) { + this.resetViewport(); + this.redraw(); + return true; + } +}; + + +/** + * Sets the clusterer's ready state. + * + * @param {boolean} ready The state. + * @private + */ +MarkerClusterer.prototype.setReady_ = function(ready) { + if (!this.ready_) { + this.ready_ = ready; + this.createClusters_(); + } +}; + + +/** + * Returns the number of clusters in the clusterer. + * + * @return {number} The number of clusters. + */ +MarkerClusterer.prototype.getTotalClusters = function() { + return this.clusters_.length; +}; + + +/** + * Returns the google map that the clusterer is associated with. + * + * @return {google.maps.Map} The map. + */ +MarkerClusterer.prototype.getMap = function() { + return this.map_; +}; + + +/** + * Sets the google map that the clusterer is associated with. + * + * @param {google.maps.Map} map The map. + */ +MarkerClusterer.prototype.setMap = function(map) { + this.map_ = map; +}; + + +/** + * Returns the size of the grid. + * + * @return {number} The grid size. + */ +MarkerClusterer.prototype.getGridSize = function() { + return this.gridSize_; +}; + + +/** + * Sets the size of the grid. + * + * @param {number} size The grid size. + */ +MarkerClusterer.prototype.setGridSize = function(size) { + this.gridSize_ = size; +}; + + +/** + * Returns the min cluster size. + * + * @return {number} The grid size. + */ +MarkerClusterer.prototype.getMinClusterSize = function() { + return this.minClusterSize_; +}; + +/** + * Sets the min cluster size. + * + * @param {number} size The grid size. + */ +MarkerClusterer.prototype.setMinClusterSize = function(size) { + this.minClusterSize_ = size; +}; + + +/** + * Extends a bounds object by the grid size. + * + * @param {google.maps.LatLngBounds} bounds The bounds to extend. + * @return {google.maps.LatLngBounds} The extended bounds. + */ +MarkerClusterer.prototype.getExtendedBounds = function(bounds) { + var projection = this.getProjection(); + + // Turn the bounds into latlng. + var tr = new google.maps.LatLng(bounds.getNorthEast().lat(), + bounds.getNorthEast().lng()); + var bl = new google.maps.LatLng(bounds.getSouthWest().lat(), + bounds.getSouthWest().lng()); + + // Convert the points to pixels and the extend out by the grid size. + var trPix = projection.fromLatLngToDivPixel(tr); + trPix.x += this.gridSize_; + trPix.y -= this.gridSize_; + + var blPix = projection.fromLatLngToDivPixel(bl); + blPix.x -= this.gridSize_; + blPix.y += this.gridSize_; + + // Convert the pixel points back to LatLng + var ne = projection.fromDivPixelToLatLng(trPix); + var sw = projection.fromDivPixelToLatLng(blPix); + + // Extend the bounds to contain the new bounds. + bounds.extend(ne); + bounds.extend(sw); + + return bounds; +}; + + +/** + * Determins if a marker is contained in a bounds. + * + * @param {google.maps.Marker} marker The marker to check. + * @param {google.maps.LatLngBounds} bounds The bounds to check against. + * @return {boolean} True if the marker is in the bounds. + * @private + */ +MarkerClusterer.prototype.isMarkerInBounds_ = function(marker, bounds) { + return bounds.contains(marker.getPosition()); +}; + + +/** + * Clears all clusters and markers from the clusterer. + */ +MarkerClusterer.prototype.clearMarkers = function() { + this.resetViewport(true); + + // Set the markers a empty array. + this.markers_ = []; +}; + + +/** + * Clears all existing clusters and recreates them. + * @param {boolean} opt_hide To also hide the marker. + */ +MarkerClusterer.prototype.resetViewport = function(opt_hide) { + // Remove all the clusters + for (var i = 0, cluster; cluster = this.clusters_[i]; i++) { + cluster.remove(); + } + + // Reset the markers to not be added and to be invisible. + for (var i = 0, marker; marker = this.markers_[i]; i++) { + marker.isAdded = false; + if (opt_hide) { + marker.setMap(null); + } + } + + this.clusters_ = []; +}; + +/** + * + */ +MarkerClusterer.prototype.repaint = function() { + var oldClusters = this.clusters_.slice(); + this.clusters_.length = 0; + this.resetViewport(); + this.redraw(); + + // Remove the old clusters. + // Do it in a timeout so the other clusters have been drawn first. + window.setTimeout(function() { + for (var i = 0, cluster; cluster = oldClusters[i]; i++) { + cluster.remove(); + } + }, 0); +}; + + +/** + * Redraws the clusters. + */ +MarkerClusterer.prototype.redraw = function() { + this.createClusters_(); +}; + + +/** + * Calculates the distance between two latlng locations in km. + * @see http://www.movable-type.co.uk/scripts/latlong.html + * + * @param {google.maps.LatLng} p1 The first lat lng point. + * @param {google.maps.LatLng} p2 The second lat lng point. + * @return {number} The distance between the two points in km. + * @private +*/ +MarkerClusterer.prototype.distanceBetweenPoints_ = function(p1, p2) { + if (!p1 || !p2) { + return 0; + } + + var R = 6371; // Radius of the Earth in km + var dLat = (p2.lat() - p1.lat()) * Math.PI / 180; + var dLon = (p2.lng() - p1.lng()) * Math.PI / 180; + var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) * + Math.sin(dLon / 2) * Math.sin(dLon / 2); + var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + var d = R * c; + return d; +}; + + +/** + * Add a marker to a cluster, or creates a new cluster. + * + * @param {google.maps.Marker} marker The marker to add. + * @private + */ +MarkerClusterer.prototype.addToClosestCluster_ = function(marker) { + var distance = 40000; // Some large number + var clusterToAddTo = null; + var pos = marker.getPosition(); + for (var i = 0, cluster; cluster = this.clusters_[i]; i++) { + var center = cluster.getCenter(); + if (center) { + var d = this.distanceBetweenPoints_(center, marker.getPosition()); + if (d < distance) { + distance = d; + clusterToAddTo = cluster; + } + } + } + + if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) { + clusterToAddTo.addMarker(marker); + } else { + var cluster = new Cluster(this); + cluster.addMarker(marker); + this.clusters_.push(cluster); + } +}; + + +/** + * Creates the clusters. + * + * @private + */ +MarkerClusterer.prototype.createClusters_ = function() { + if (!this.ready_) { + return; + } + + // Get our current map view bounds. + // Create a new bounds object so we don't affect the map. + var mapBounds = new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(), + this.map_.getBounds().getNorthEast()); + var bounds = this.getExtendedBounds(mapBounds); + + for (var i = 0, marker; marker = this.markers_[i]; i++) { + if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) { + this.addToClosestCluster_(marker); + } + } +}; + + +/** + * A cluster that contains markers. + * + * @param {MarkerClusterer} markerClusterer The markerclusterer that this + * cluster is associated with. + * @constructor + * @ignore + */ +function Cluster(markerClusterer) { + this.markerClusterer_ = markerClusterer; + this.map_ = markerClusterer.getMap(); + this.gridSize_ = markerClusterer.getGridSize(); + this.minClusterSize_ = markerClusterer.getMinClusterSize(); + this.averageCenter_ = markerClusterer.isAverageCenter(); + this.center_ = null; + this.markers_ = []; + this.bounds_ = null; + this.clusterIcon_ = new ClusterIcon(this, markerClusterer.getStyles(), + markerClusterer.getGridSize()); +} + +/** + * Determins if a marker is already added to the cluster. + * + * @param {google.maps.Marker} marker The marker to check. + * @return {boolean} True if the marker is already added. + */ +Cluster.prototype.isMarkerAlreadyAdded = function(marker) { + if (this.markers_.indexOf) { + return this.markers_.indexOf(marker) != -1; + } else { + for (var i = 0, m; m = this.markers_[i]; i++) { + if (m == marker) { + return true; + } + } + } + return false; +}; + + +/** + * Add a marker the cluster. + * + * @param {google.maps.Marker} marker The marker to add. + * @return {boolean} True if the marker was added. + */ +Cluster.prototype.addMarker = function(marker) { + if (this.isMarkerAlreadyAdded(marker)) { + return false; + } + + if (!this.center_) { + this.center_ = marker.getPosition(); + this.calculateBounds_(); + } else { + if (this.averageCenter_) { + var l = this.markers_.length + 1; + var lat = (this.center_.lat() * (l-1) + marker.getPosition().lat()) / l; + var lng = (this.center_.lng() * (l-1) + marker.getPosition().lng()) / l; + this.center_ = new google.maps.LatLng(lat, lng); + this.calculateBounds_(); + } + } + + marker.isAdded = true; + this.markers_.push(marker); + + var len = this.markers_.length; + if (len < this.minClusterSize_ && marker.getMap() != this.map_) { + // Min cluster size not reached so show the marker. + marker.setMap(this.map_); + } + + if (len == this.minClusterSize_) { + // Hide the markers that were showing. + for (var i = 0; i < len; i++) { + this.markers_[i].setMap(null); + } + } + + if (len >= this.minClusterSize_) { + marker.setMap(null); + } + + this.updateIcon(); + return true; +}; + + +/** + * Returns the marker clusterer that the cluster is associated with. + * + * @return {MarkerClusterer} The associated marker clusterer. + */ +Cluster.prototype.getMarkerClusterer = function() { + return this.markerClusterer_; +}; + + +/** + * Returns the bounds of the cluster. + * + * @return {google.maps.LatLngBounds} the cluster bounds. + */ +Cluster.prototype.getBounds = function() { + var bounds = new google.maps.LatLngBounds(this.center_, this.center_); + var markers = this.getMarkers(); + for (var i = 0, marker; marker = markers[i]; i++) { + bounds.extend(marker.getPosition()); + } + return bounds; +}; + + +/** + * Removes the cluster + */ +Cluster.prototype.remove = function() { + this.clusterIcon_.remove(); + this.markers_.length = 0; + delete this.markers_; +}; + + +/** + * Returns the center of the cluster. + * + * @return {number} The cluster center. + */ +Cluster.prototype.getSize = function() { + return this.markers_.length; +}; + + +/** + * Returns the center of the cluster. + * + * @return {Array.} The cluster center. + */ +Cluster.prototype.getMarkers = function() { + return this.markers_; +}; + + +/** + * Returns the center of the cluster. + * + * @return {google.maps.LatLng} The cluster center. + */ +Cluster.prototype.getCenter = function() { + return this.center_; +}; + + +/** + * Calculated the extended bounds of the cluster with the grid. + * + * @private + */ +Cluster.prototype.calculateBounds_ = function() { + var bounds = new google.maps.LatLngBounds(this.center_, this.center_); + this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds); +}; + + +/** + * Determines if a marker lies in the clusters bounds. + * + * @param {google.maps.Marker} marker The marker to check. + * @return {boolean} True if the marker lies in the bounds. + */ +Cluster.prototype.isMarkerInClusterBounds = function(marker) { + return this.bounds_.contains(marker.getPosition()); +}; + + +/** + * Returns the map that the cluster is associated with. + * + * @return {google.maps.Map} The map. + */ +Cluster.prototype.getMap = function() { + return this.map_; +}; + + +/** + * Updates the cluster icon + */ +Cluster.prototype.updateIcon = function() { + var zoom = this.map_.getZoom(); + var mz = this.markerClusterer_.getMaxZoom(); + + if (mz && zoom > mz) { + // The zoom is greater than our max zoom so show all the markers in cluster. + for (var i = 0, marker; marker = this.markers_[i]; i++) { + marker.setMap(this.map_); + } + return; + } + + if (this.markers_.length < this.minClusterSize_) { + // Min cluster size not yet reached. + this.clusterIcon_.hide(); + return; + } + + var numStyles = this.markerClusterer_.getStyles().length; + var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles); + this.clusterIcon_.setCenter(this.center_); + this.clusterIcon_.setSums(sums); + this.clusterIcon_.show(); +}; + + +/** + * A cluster icon + * + * @param {Cluster} cluster The cluster to be associated with. + * @param {Object} styles An object that has style properties: + * 'url': (string) The image url. + * 'height': (number) The image height. + * 'width': (number) The image width. + * 'anchor': (Array) The anchor position of the label text. + * 'textColor': (string) The text color. + * 'textSize': (number) The text size. + * 'backgroundPosition: (string) The background postition x, y. + * @param {number=} opt_padding Optional padding to apply to the cluster icon. + * @constructor + * @extends google.maps.OverlayView + * @ignore + */ +function ClusterIcon(cluster, styles, opt_padding) { + cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView); + + this.styles_ = styles; + this.padding_ = opt_padding || 0; + this.cluster_ = cluster; + this.center_ = null; + this.map_ = cluster.getMap(); + this.div_ = null; + this.sums_ = null; + this.visible_ = false; + + this.setMap(this.map_); +} + + +/** + * Triggers the clusterclick event and zoom's if the option is set. + */ +ClusterIcon.prototype.triggerClusterClick = function() { + var markerClusterer = this.cluster_.getMarkerClusterer(); + + // Trigger the clusterclick event. + google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster_); + + if (markerClusterer.isZoomOnClick()) { + // Zoom into the cluster. + this.map_.fitBounds(this.cluster_.getBounds()); + } +}; + + +/** + * Adding the cluster icon to the dom. + * @ignore + */ +ClusterIcon.prototype.onAdd = function() { + this.div_ = document.createElement('DIV'); + if (this.visible_) { + var pos = this.getPosFromLatLng_(this.center_); + this.div_.style.cssText = this.createCss(pos); + this.div_.innerHTML = this.sums_.text; + } + + var panes = this.getPanes(); + panes.overlayMouseTarget.appendChild(this.div_); + + var that = this; + google.maps.event.addDomListener(this.div_, 'click', function() { + that.triggerClusterClick(); + }); +}; + + +/** + * Returns the position to place the div dending on the latlng. + * + * @param {google.maps.LatLng} latlng The position in latlng. + * @return {google.maps.Point} The position in pixels. + * @private + */ +ClusterIcon.prototype.getPosFromLatLng_ = function(latlng) { + var pos = this.getProjection().fromLatLngToDivPixel(latlng); + pos.x -= parseInt(this.width_ / 2, 10); + pos.y -= parseInt(this.height_ / 2, 10); + return pos; +}; + + +/** + * Draw the icon. + * @ignore + */ +ClusterIcon.prototype.draw = function() { + if (this.visible_) { + var pos = this.getPosFromLatLng_(this.center_); + this.div_.style.top = pos.y + 'px'; + this.div_.style.left = pos.x + 'px'; + } +}; + + +/** + * Hide the icon. + */ +ClusterIcon.prototype.hide = function() { + if (this.div_) { + this.div_.style.display = 'none'; + } + this.visible_ = false; +}; + + +/** + * Position and show the icon. + */ +ClusterIcon.prototype.show = function() { + if (this.div_) { + var pos = this.getPosFromLatLng_(this.center_); + this.div_.style.cssText = this.createCss(pos); + this.div_.style.display = ''; + } + this.visible_ = true; +}; + + +/** + * Remove the icon from the map + */ +ClusterIcon.prototype.remove = function() { + this.setMap(null); +}; + + +/** + * Implementation of the onRemove interface. + * @ignore + */ +ClusterIcon.prototype.onRemove = function() { + if (this.div_ && this.div_.parentNode) { + this.hide(); + this.div_.parentNode.removeChild(this.div_); + this.div_ = null; + } +}; + + +/** + * Set the sums of the icon. + * + * @param {Object} sums The sums containing: + * 'text': (string) The text to display in the icon. + * 'index': (number) The style index of the icon. + */ +ClusterIcon.prototype.setSums = function(sums) { + this.sums_ = sums; + this.text_ = sums.text; + this.index_ = sums.index; + if (this.div_) { + this.div_.innerHTML = sums.text; + } + + this.useStyle(); +}; + + +/** + * Sets the icon to the the styles. + */ +ClusterIcon.prototype.useStyle = function() { + var index = Math.max(0, this.sums_.index - 1); + index = Math.min(this.styles_.length - 1, index); + var style = this.styles_[index]; + this.url_ = style['url']; + this.height_ = style['height']; + this.width_ = style['width']; + this.textColor_ = style['textColor']; + this.anchor_ = style['anchor']; + this.textSize_ = style['textSize']; + this.backgroundPosition_ = style['backgroundPosition']; +}; + + +/** + * Sets the center of the icon. + * + * @param {google.maps.LatLng} center The latlng to set as the center. + */ +ClusterIcon.prototype.setCenter = function(center) { + this.center_ = center; +}; + + +/** + * Create the css text based on the position of the icon. + * + * @param {google.maps.Point} pos The position. + * @return {string} The css style text. + */ +ClusterIcon.prototype.createCss = function(pos) { + var style = []; + style.push('background-image:url(' + this.url_ + ');'); + var backgroundPosition = this.backgroundPosition_ ? this.backgroundPosition_ : '0 0'; + style.push('background-position:' + backgroundPosition + ';'); + + if (typeof this.anchor_ === 'object') { + if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 && + this.anchor_[0] < this.height_) { + style.push('height:' + (this.height_ - this.anchor_[0]) + + 'px; padding-top:' + this.anchor_[0] + 'px;'); + } else { + style.push('height:' + this.height_ + 'px; line-height:' + this.height_ + + 'px;'); + } + if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 && + this.anchor_[1] < this.width_) { + style.push('width:' + (this.width_ - this.anchor_[1]) + + 'px; padding-left:' + this.anchor_[1] + 'px;'); + } else { + style.push('width:' + this.width_ + 'px; text-align:center;'); + } + } else { + style.push('height:' + this.height_ + 'px; line-height:' + + this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;'); + } + + var txtColor = this.textColor_ ? this.textColor_ : 'black'; + var txtSize = this.textSize_ ? this.textSize_ : 11; + + style.push('cursor:pointer; top:' + pos.y + 'px; left:' + + pos.x + 'px; color:' + txtColor + '; position:absolute; font-size:' + + txtSize + 'px; font-family:Arial,sans-serif; font-weight:bold'); + return style.join(''); +}; + + +// Export Symbols for Closure +// If you are not going to compile with closure then you can remove the +// code below. +window['MarkerClusterer'] = MarkerClusterer; +MarkerClusterer.prototype['addMarker'] = MarkerClusterer.prototype.addMarker; +MarkerClusterer.prototype['addMarkers'] = MarkerClusterer.prototype.addMarkers; +MarkerClusterer.prototype['clearMarkers'] = + MarkerClusterer.prototype.clearMarkers; +MarkerClusterer.prototype['fitMapToMarkers'] = + MarkerClusterer.prototype.fitMapToMarkers; +MarkerClusterer.prototype['getCalculator'] = + MarkerClusterer.prototype.getCalculator; +MarkerClusterer.prototype['getGridSize'] = + MarkerClusterer.prototype.getGridSize; +MarkerClusterer.prototype['getExtendedBounds'] = + MarkerClusterer.prototype.getExtendedBounds; +MarkerClusterer.prototype['getMap'] = MarkerClusterer.prototype.getMap; +MarkerClusterer.prototype['getMarkers'] = MarkerClusterer.prototype.getMarkers; +MarkerClusterer.prototype['getMaxZoom'] = MarkerClusterer.prototype.getMaxZoom; +MarkerClusterer.prototype['getStyles'] = MarkerClusterer.prototype.getStyles; +MarkerClusterer.prototype['getTotalClusters'] = + MarkerClusterer.prototype.getTotalClusters; +MarkerClusterer.prototype['getTotalMarkers'] = + MarkerClusterer.prototype.getTotalMarkers; +MarkerClusterer.prototype['redraw'] = MarkerClusterer.prototype.redraw; +MarkerClusterer.prototype['removeMarker'] = + MarkerClusterer.prototype.removeMarker; +MarkerClusterer.prototype['removeMarkers'] = + MarkerClusterer.prototype.removeMarkers; +MarkerClusterer.prototype['resetViewport'] = + MarkerClusterer.prototype.resetViewport; +MarkerClusterer.prototype['repaint'] = + MarkerClusterer.prototype.repaint; +MarkerClusterer.prototype['setCalculator'] = + MarkerClusterer.prototype.setCalculator; +MarkerClusterer.prototype['setGridSize'] = + MarkerClusterer.prototype.setGridSize; +MarkerClusterer.prototype['setMaxZoom'] = + MarkerClusterer.prototype.setMaxZoom; +MarkerClusterer.prototype['onAdd'] = MarkerClusterer.prototype.onAdd; +MarkerClusterer.prototype['draw'] = MarkerClusterer.prototype.draw; + +Cluster.prototype['getCenter'] = Cluster.prototype.getCenter; +Cluster.prototype['getSize'] = Cluster.prototype.getSize; +Cluster.prototype['getMarkers'] = Cluster.prototype.getMarkers; + +ClusterIcon.prototype['onAdd'] = ClusterIcon.prototype.onAdd; +ClusterIcon.prototype['draw'] = ClusterIcon.prototype.draw; +ClusterIcon.prototype['onRemove'] = ClusterIcon.prototype.onRemove; From 41109bc54a3e5bfb9a4e299cf6d9aa7f8294757d Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 18 Mar 2015 21:00:13 +0700 Subject: [PATCH 188/354] ENHANCEMENT: Use google clusterer. Fixes #5 --- javascript/google/maputil.js | 356 +++++++++++++------------ templates/Includes/GoogleJavaScript.ss | 11 +- 2 files changed, 195 insertions(+), 172 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index 4afe7bd..1f14c99 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -1,194 +1,218 @@ +/** + * Counter used for uniquely identifying each Google map + * @type {Number} + */ var mappableMapCtr = 0; - +/** + * Create a google map pin from data provided + * @param {GoogleMap} map Instance of a google map + * @param {Number} lat Latitude of pin + * @param {Number} lng Longitude of pin + * @param {String} html HTML for information window + * @param {String} icon URL of alternative map icon, or blank for default + * @param {boolean} useClusterer Whether or not to use clusterer + * @param {boolean} enableWindowZoom Whether or not to enable zoom on the rendered map + * @param {boolean} defaultHideMarker Whether or not to hide markers initially + * @return {MapMarker} Google map pin object + */ function createMarker(map, lat, lng, html, category, icon, useClusterer, enableWindowZoom, defaultHideMarker) { - mapId = map.getDiv().getAttribute('id'); - - var marker = new google.maps.Marker(); - marker.setPosition(new google.maps.LatLng(lat, lng)); - marker.mycategory = category; - - if (icon && icon !== '') { - var image = new google.maps.MarkerImage(icon); - marker.setIcon(image); - } - - if (useClusterer) { - fluster.addMarker(marker); - } else { - marker.setMap(map); - } - - google.maps.event.addListener(marker, "click", function() { - if (enableWindowZoom) { - map.setCenter(new google.maps.LatLng(lat, lng), 12); // $InfoWindowZoom); - } - var infoWindow = infoWindows[mapId]; - infoWindow.setContent(html); - infoWindow.open(map, this); - }); - - gmarkers[mapId].push(marker); - - if (defaultHideMarker) { - marker.hide(); - } + mapId = map.getDiv().getAttribute('id'); + + var marker = new google.maps.Marker(); + marker.setPosition(new google.maps.LatLng(lat, lng)); + marker.mycategory = category; + + if (icon && icon !== '') { + var image = new google.maps.MarkerImage(icon); + marker.setIcon(image); + } + + if (!useClusterer) { + marker.setMap(map); + } + + google.maps.event.addListener(marker, "click", function() { + if (enableWindowZoom) { + map.setCenter(new google.maps.LatLng(lat, lng), 12); // $InfoWindowZoom); + } + var infoWindow = infoWindows[mapId]; + infoWindow.setContent(html); + infoWindow.open(map, this); + }); + + gmarkers[mapId].push(marker); + + if (defaultHideMarker) { + marker.hide(); + } + return marker; } -// JS public function to get current Lat & Lng +/** + * Get the current latitude + * @return {float} Current latitude + */ function getCurrentLat() { - return current_lat; + return current_lat; } - +/** + * Get the current longitude + * @return {float} The current longitude + */ function getCurrentLng() { - return current_lng; -} - - -// JS public function to center the gmaps dynamically -function showAddress(address) { - if (geocoder) { - geocoder.getLatLng( - address, - - function(point) { - if (!point) { - alert(address + " not found"); - } else { - map.setCenter(point); - map.setZoom($Zoom); - } - }); - } + return current_lng; } +/** + * Convert JSON point data into google map markers + * @param {GoogleMap} map Google Map instance + * @param {array} markers point data loaded from JSON + * @param {boolean} useClusterer Whether or not to use the clusterer + * @param {boolean} enableWindowZoom Whether or not zoom is enabled + * @param {boolean} defaultHideMarker Whether or not to hide markers + * * @return array of Google map markers converted from the JSON data + */ function addAllMarkers(map, markers, useClusterer, enableWindowZoom, defaultHideMarker) { - for (var i = 0; i < markers.length; i++) { - var marker = markers[i]; - createMarker(map, marker.latitude, marker.longitude, marker.html, marker.category, marker.icon, - useClusterer, enableWindowZoom, defaultHideMarker); - } - + var allmarkers = []; + for (var i = 0; i < markers.length; i++) { + var markerinfo = markers[i]; + var marker = createMarker(map, markerinfo.latitude, markerinfo.longitude, markerinfo.html, + markerinfo.category, markerinfo.icon, useClusterer, enableWindowZoom, + defaultHideMarker); + allmarkers.push(marker); + } + return allmarkers; } - +/** + * Add lines to a Google map + * @param {googleMap} map Google map instance + * @param {array} lines Line data loaded from json, lat1,lon1 to lat2,lon2 + */ function addLines(map, lines) { - for (i = 0; i < lines.length; i++) { - var line = lines[i]; - var point1 = new google.maps.LatLng(line.lat1, line.lon1); - var point2 = new google.maps.LatLng(line.lat2, line.lon2); - var points = [point1, point2]; - var pl = new google.maps.Polyline({ - path: points, - strokeColor: line.color, - strokeWeight: 4, - strokeOpacity: 0.8 - }); - pl.setMap(map); - } + for (i = 0; i < lines.length; i++) { + var line = lines[i]; + var point1 = new google.maps.LatLng(line.lat1, line.lon1); + var point2 = new google.maps.LatLng(line.lat2, line.lon2); + var points = [point1, point2]; + var pl = new google.maps.Polyline({ + path: points, + strokeColor: line.color, + strokeWeight: 4, + strokeOpacity: 0.8 + }); + pl.setMap(map); + } } - +/** + * Add one or more (max of 25) KML files to a Google map + * @param {GoogleMap} map A Google Map instance + * @param {array} kmlFiles array of URLs for KML files + */ function addKmlFiles(map, kmlFiles) { - for (var i = 0; i < kmlFiles.length; i++) { - var kmlFile = kmlFiles[i]; - var kmlLayer = new google.maps.KmlLayer(kmlFile, { - suppressInfoWindows: true, - map: map - }); - - } + for (var i = 0; i < kmlFiles.length; i++) { + var kmlFile = kmlFiles[i]; + var kmlLayer = new google.maps.KmlLayer(kmlFile, { + suppressInfoWindows: true, + map: map + }); + + } } -function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLat, maxLng, mapType, markers, lines, kmlFiles, - jsonMapStyles, enableAutomaticCenterZoom, useClusterer, allowFullScreen) { - var newMap = []; - newMap.googleMapID = googleMapID; - newMap.zoom = zoom; - newMap.centreCoordinates = centreCoordinates; - newMap.minLat = minLat; - newMap.minLng = minLng; - newMap.maxLng = maxLng; - newMap.maxLat = maxLat; - newMap.markers = markers; - newMap.googleMapID = googleMapID; - newMap.mapType = mapType; - newMap.lines = lines; - newMap.kmlFiles = kmlFiles; - newMap.jsonMapStyles = jsonMapStyles; - newMap.enableAutomaticCenterZoom = enableAutomaticCenterZoom; - newMap.useClusterer = useClusterer; - newMap.allowFullScreen = allowFullScreen; - mappableMaps[googleMapID] = newMap; - - // increment map counter - mappableMapCtr++; - - // initialise gmarkers array for this map - gmarkers[googleMapID] = []; - var infoWindow = new google.maps.InfoWindow({ - content: 'test', - maxWidth: 400 - }); - infoWindows[googleMapID] = infoWindow; - - mapLayers[googleMapID] = kmlFiles; - mapLines[googleMapID] = lines; +function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLat, maxLng, mapType, markers, lines, kmlFiles, + jsonMapStyles, enableAutomaticCenterZoom, useClusterer, allowFullScreen) { + var newMap = []; + newMap.googleMapID = googleMapID; + newMap.zoom = zoom; + newMap.centreCoordinates = centreCoordinates; + newMap.minLat = minLat; + newMap.minLng = minLng; + newMap.maxLng = maxLng; + newMap.maxLat = maxLat; + newMap.markers = markers; + newMap.googleMapID = googleMapID; + newMap.mapType = mapType; + newMap.lines = lines; + newMap.kmlFiles = kmlFiles; + newMap.jsonMapStyles = jsonMapStyles; + newMap.enableAutomaticCenterZoom = enableAutomaticCenterZoom; + newMap.useClusterer = useClusterer; + newMap.allowFullScreen = allowFullScreen; + mappableMaps[googleMapID] = newMap; + + // increment map counter + mappableMapCtr++; + + // initialise gmarkers array for this map + gmarkers[googleMapID] = []; + var infoWindow = new google.maps.InfoWindow({ + content: 'test', + maxWidth: 400 + }); + infoWindows[googleMapID] = infoWindow; + + mapLayers[googleMapID] = kmlFiles; + mapLines[googleMapID] = lines; } +/** + * Callback function after the Google Maps API has been loaded - renders the maps along with + * associated points of interest and layers + */ function loadedGoogleMapsAPI() { - for (var i = 1; i <= mappableMapCtr; i++) { - var map_info = mappableMaps['google_map_' + i]; - console.log(map_info); - var map = new google.maps.Map(document.getElementById(map_info.googleMapID)); - - if (map_info.useClusterer) { - fluster = new Fluster2(map); - } - // <% end_if %> - geocoder = new google.maps.Geocoder(); - - // FIXME - to do - // if (map_info.jsonMapStyles) { - //map.setOptions({styles: map_info.jsonMapStyles}); - //}; - - if (map_info.allowFullScreen) { - map.controls[google.maps.ControlPosition.TOP_RIGHT].push( - FullScreenControl(map, "Full Screen", "Original Size") - ); - } - if (map_info.enableAutomaticCenterZoom) { - centre = map_info.centreCoordinates; - map.setCenter(new google.maps.LatLng(centre.lat, centre.lng)); - - var bds = new google.maps.LatLngBounds(new google.maps.LatLng(map_info.minLat, map_info.minLng), - new google.maps.LatLng(map_info.maxLat, map_info.maxLng)); - map.fitBounds(bds); - - map.setZoom(map_info.zoom); - } else { - var centre = map_info.centreCoordinates; - map.setCenter(new google.maps.LatLng(centre.lat, centre.lng)); - map.setZoom(map_info.zoom); - } - - if (map_info.mapType) { - map.setMapTypeId(map_info.mapType); - } else { - map.setMapTypeId(google.maps.MapTypeId.ROADMAP); - } - - addAllMarkers(map, map_info.markers, map_info.useClusterer, map_info.enableAutomaticCenterZoom, map_info.defaultHideMarker); - addLines(map, map_info.lines); - addKmlFiles(map, map_info.kmlFiles); - if (map_info.useClusterer) { - fluster.initialize(); - } - } + for (var i = 1; i <= mappableMapCtr; i++) { + var map_info = mappableMaps['google_map_' + i]; + var map = new google.maps.Map(document.getElementById(map_info.googleMapID)); + + // initialise geocoder + geocoder = new google.maps.Geocoder(); + + // TODO + // if (map_info.jsonMapStyles) { + // map.setOptions({styles: map_info.jsonMapStyles}); + // }; + + if (map_info.allowFullScreen) { + map.controls[google.maps.ControlPosition.TOP_RIGHT].push( + FullScreenControl(map, "Full Screen", "Original Size") + ); + } + if (map_info.enableAutomaticCenterZoom) { + centre = map_info.centreCoordinates; + map.setCenter(new google.maps.LatLng(centre.lat, centre.lng)); + + var bds = new google.maps.LatLngBounds(new google.maps.LatLng(map_info.minLat, map_info.minLng), + new google.maps.LatLng(map_info.maxLat, map_info.maxLng)); + map.fitBounds(bds); + map.setZoom(map_info.zoom); + } else { + var centre = map_info.centreCoordinates; + map.setCenter(new google.maps.LatLng(centre.lat, centre.lng)); + map.setZoom(map_info.zoom); + } + + if (map_info.mapType) { + map.setMapTypeId(map_info.mapType); + } else { + map.setMapTypeId(google.maps.MapTypeId.ROADMAP); + } + + var markers = addAllMarkers(map, map_info.markers, map_info.useClusterer, map_info.enableAutomaticCenterZoom, map_info.defaultHideMarker); + + if (map_info.useClusterer) { + var mcOptions = {gridSize: 50, maxZoom: 17}; + var markerCluster = new MarkerClusterer(map,markers,mcOptions); + } + + addLines(map, map_info.lines); + addKmlFiles(map, map_info.kmlFiles); + } } diff --git a/templates/Includes/GoogleJavaScript.ss b/templates/Includes/GoogleJavaScript.ss index 7a39183..497e1f0 100644 --- a/templates/Includes/GoogleJavaScript.ss +++ b/templates/Includes/GoogleJavaScript.ss @@ -2,16 +2,15 @@ <% if DelayLoadMapFunction %> <% else %> - + + + <% end_if %> - -<% end_if %> -<% if UseClusterer %> - -<% end_if %> \ No newline at end of file + +<% end_if %> From 1015154d9b66a83bcc733b41e803ba49ea4e4895 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 18 Mar 2015 21:02:27 +0700 Subject: [PATCH 189/354] MINOR: Whitespace fixing --- code/MapExtension.php | 45 +++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/code/MapExtension.php b/code/MapExtension.php index d911944..9437185 100644 --- a/code/MapExtension.php +++ b/code/MapExtension.php @@ -146,32 +146,31 @@ public function BasicMap() { $map->setEnableAutomaticCenterZoom(true); } - // add points of interest taking into account the default icon of the layer as an override - if (Object::has_extension($this->owner->ClassName, 'PointsOfInterestLayerExtension')) { - foreach($this->owner->PointsOfInterestLayers() as $layer) { - $layericon = $layer->DefaultIcon(); - if ($layericon->ID === 0) { - $layericon = null; - } - foreach ($layer->PointsOfInterest() as $poi) { - if ($poi->MapPinEdited) { - if ($poi->MapPinIconID == 0) { - $poi->CachedMapPin = $layericon; + // add points of interest taking into account the default icon of the layer as an override + if (Object::has_extension($this->owner->ClassName, 'PointsOfInterestLayerExtension')) { + foreach($this->owner->PointsOfInterestLayers() as $layer) { + $layericon = $layer->DefaultIcon(); + if ($layericon->ID === 0) { + $layericon = null; + } + foreach ($layer->PointsOfInterest() as $poi) { + if ($poi->MapPinEdited) { + if ($poi->MapPinIconID == 0) { + $poi->CachedMapPin = $layericon; + } + $map->addMarkerAsObject($poi); + } + } } - $map->addMarkerAsObject($poi); - } + $map->setClusterer(true); + $map->setEnableAutomaticCenterZoom(true); } - } - $map->setClusterer(true); - $map->setEnableAutomaticCenterZoom(true); - } - $map->setZoom(10); - $map->setAdditionalCSSClasses('fullWidthMap'); - $map->setShowInlineMapDivStyle(true); - $map->setClusterer(true); + $map->setZoom(10); + $map->setAdditionalCSSClasses('fullWidthMap'); + $map->setShowInlineMapDivStyle(true); + $map->setClusterer(true); - return $map; + return $map; } - } From f407549fe6d0e70278da41eb71175aabb53a112f Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 18 Mar 2015 21:02:44 +0700 Subject: [PATCH 190/354] MINOR: Removed whitespace --- code/MappableData.php | 1 - 1 file changed, 1 deletion(-) diff --git a/code/MappableData.php b/code/MappableData.php index 07894c6..d99463e 100644 --- a/code/MappableData.php +++ b/code/MappableData.php @@ -15,7 +15,6 @@ public function getRenderableMap($width = null, $height = null, $zoom = 9) { $gmap->setSize($w,$h); $gmap->setZoom($zoom); $gmap->setEnableAutomaticCenterZoom(false); - if ($this->owner->MapPinEdited) { $gmap->setLatLongCenter(array( '200', From 78c7b38afc7ecb72ff8d2647a654393124f944ef Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 18 Mar 2015 21:15:24 +0700 Subject: [PATCH 191/354] FIX: whitespace and line length --- javascript/google/FullScreenControl.js | 252 +++--- javascript/google/MapGoogle.ss | 19 +- javascript/google/maputil.js | 12 +- javascript/google/markerclusterer.js | 1126 ++++++++++++------------ 4 files changed, 707 insertions(+), 702 deletions(-) diff --git a/javascript/google/FullScreenControl.js b/javascript/google/FullScreenControl.js index ff20f2d..8e5a3ac 100644 --- a/javascript/google/FullScreenControl.js +++ b/javascript/google/FullScreenControl.js @@ -1,131 +1,131 @@ /// function FullScreenControl(map, enterFull, exitFull) { - if (enterFull === void 0) { enterFull = null; } - if (exitFull === void 0) { exitFull = null; } - if (enterFull == null) { - enterFull = "Full screen"; - } - if (exitFull == null) { - exitFull = "Exit full screen"; - } - var controlDiv = document.createElement("div"); - controlDiv.className = "fullScreen"; - controlDiv.index = 1; - controlDiv.style.padding = "5px"; - // Set CSS for the control border. - var controlUI = document.createElement("div"); - controlUI.style.backgroundColor = "white"; - controlUI.style.borderStyle = "solid"; - controlUI.style.borderWidth = "1px"; - controlUI.style.borderColor = "#717b87"; - controlUI.style.cursor = "pointer"; - controlUI.style.textAlign = "center"; - controlUI.style.boxShadow = "rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px"; - controlDiv.appendChild(controlUI); - // Set CSS for the control interior. - var controlText = document.createElement("div"); - controlText.style.fontFamily = "Roboto,Arial,sans-serif"; - controlText.style.fontSize = "11px"; - controlText.style.fontWeight = "400"; - controlText.style.paddingTop = "1px"; - controlText.style.paddingBottom = "1px"; - controlText.style.paddingLeft = "6px"; - controlText.style.paddingRight = "6px"; - controlText.innerHTML = "" + enterFull + ""; - controlUI.appendChild(controlText); - // set print CSS so the control is hidden - var head = document.getElementsByTagName("head")[0]; - var newStyle = document.createElement("style"); - newStyle.setAttribute("type", "text/css"); - newStyle.setAttribute("media", "print"); - var cssText = ".fullScreen { display: none;}"; - var texNode = document.createTextNode(cssText); - try { - newStyle.appendChild(texNode); - } - catch (e) { - // IE8 hack - newStyle.styleSheet.cssText = cssText; - } - head.appendChild(newStyle); - var fullScreen = false; - var interval; - var mapDiv = map.getDiv(); - var divStyle = mapDiv.style; - if (mapDiv.runtimeStyle) { - divStyle = mapDiv.runtimeStyle; - } - var originalPos = divStyle.position; - var originalWidth = divStyle.width; - var originalHeight = divStyle.height; - // IE8 hack - if (originalWidth === "") { - originalWidth = mapDiv.style.width; - } - if (originalHeight === "") { - originalHeight = mapDiv.style.height; - } - var originalTop = divStyle.top; - var originalLeft = divStyle.left; - var originalZIndex = divStyle.zIndex; - var bodyStyle = document.body.style; - if (document.body.runtimeStyle) { - bodyStyle = document.body.runtimeStyle; - } - var originalOverflow = bodyStyle.overflow; - controlDiv.goFullScreen = function () { - var center = map.getCenter(); - mapDiv.style.position = "fixed"; - mapDiv.style.width = "100%"; - mapDiv.style.height = "100%"; - mapDiv.style.top = "0"; - mapDiv.style.left = "0"; - mapDiv.style.zIndex = "100"; - document.body.style.overflow = "hidden"; - controlText.innerHTML = "" + exitFull + ""; - fullScreen = true; - google.maps.event.trigger(map, "resize"); - map.setCenter(center); - // this works around street view causing the map to disappear, which is caused by Google Maps setting the - // CSS position back to relative. There is no event triggered when Street View is shown hence the use of setInterval - interval = setInterval(function () { - if (mapDiv.style.position !== "fixed") { - mapDiv.style.position = "fixed"; - google.maps.event.trigger(map, "resize"); - } - }, 100); - }; - controlDiv.exitFullScreen = function () { - var center = map.getCenter(); - if (originalPos === "") { - mapDiv.style.position = "relative"; - } - else { - mapDiv.style.position = originalPos; - } - mapDiv.style.width = originalWidth; - mapDiv.style.height = originalHeight; - mapDiv.style.top = originalTop; - mapDiv.style.left = originalLeft; - mapDiv.style.zIndex = originalZIndex; - document.body.style.overflow = originalOverflow; - controlText.innerHTML = "" + enterFull + ""; - fullScreen = false; - google.maps.event.trigger(map, "resize"); - map.setCenter(center); - clearInterval(interval); - }; - // Setup the click event listener - google.maps.event.addDomListener(controlUI, "click", function () { - if (!fullScreen) { - controlDiv.goFullScreen(); - } - else { - controlDiv.exitFullScreen(); - } - }); + if (enterFull === void 0) { enterFull = null; } + if (exitFull === void 0) { exitFull = null; } + if (enterFull == null) { + enterFull = "Full screen"; + } + if (exitFull == null) { + exitFull = "Exit full screen"; + } + var controlDiv = document.createElement("div"); + controlDiv.className = "fullScreen"; + controlDiv.index = 1; + controlDiv.style.padding = "5px"; + // Set CSS for the control border. + var controlUI = document.createElement("div"); + controlUI.style.backgroundColor = "white"; + controlUI.style.borderStyle = "solid"; + controlUI.style.borderWidth = "1px"; + controlUI.style.borderColor = "#717b87"; + controlUI.style.cursor = "pointer"; + controlUI.style.textAlign = "center"; + controlUI.style.boxShadow = "rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px"; + controlDiv.appendChild(controlUI); + // Set CSS for the control interior. + var controlText = document.createElement("div"); + controlText.style.fontFamily = "Roboto,Arial,sans-serif"; + controlText.style.fontSize = "11px"; + controlText.style.fontWeight = "400"; + controlText.style.paddingTop = "1px"; + controlText.style.paddingBottom = "1px"; + controlText.style.paddingLeft = "6px"; + controlText.style.paddingRight = "6px"; + controlText.innerHTML = "" + enterFull + ""; + controlUI.appendChild(controlText); + // set print CSS so the control is hidden + var head = document.getElementsByTagName("head")[0]; + var newStyle = document.createElement("style"); + newStyle.setAttribute("type", "text/css"); + newStyle.setAttribute("media", "print"); + var cssText = ".fullScreen { display: none;}"; + var texNode = document.createTextNode(cssText); + try { + newStyle.appendChild(texNode); + } + catch (e) { + // IE8 hack + newStyle.styleSheet.cssText = cssText; + } + head.appendChild(newStyle); + var fullScreen = false; + var interval; + var mapDiv = map.getDiv(); + var divStyle = mapDiv.style; + if (mapDiv.runtimeStyle) { + divStyle = mapDiv.runtimeStyle; + } + var originalPos = divStyle.position; + var originalWidth = divStyle.width; + var originalHeight = divStyle.height; + // IE8 hack + if (originalWidth === "") { + originalWidth = mapDiv.style.width; + } + if (originalHeight === "") { + originalHeight = mapDiv.style.height; + } + var originalTop = divStyle.top; + var originalLeft = divStyle.left; + var originalZIndex = divStyle.zIndex; + var bodyStyle = document.body.style; + if (document.body.runtimeStyle) { + bodyStyle = document.body.runtimeStyle; + } + var originalOverflow = bodyStyle.overflow; + controlDiv.goFullScreen = function () { + var center = map.getCenter(); + mapDiv.style.position = "fixed"; + mapDiv.style.width = "100%"; + mapDiv.style.height = "100%"; + mapDiv.style.top = "0"; + mapDiv.style.left = "0"; + mapDiv.style.zIndex = "100"; + document.body.style.overflow = "hidden"; + controlText.innerHTML = "" + exitFull + ""; + fullScreen = true; + google.maps.event.trigger(map, "resize"); + map.setCenter(center); + // this works around street view causing the map to disappear, which is caused by Google Maps setting the + // CSS position back to relative. There is no event triggered when Street View is shown hence the use of setInterval + interval = setInterval(function () { + if (mapDiv.style.position !== "fixed") { + mapDiv.style.position = "fixed"; + google.maps.event.trigger(map, "resize"); + } + }, 100); + }; + controlDiv.exitFullScreen = function () { + var center = map.getCenter(); + if (originalPos === "") { + mapDiv.style.position = "relative"; + } + else { + mapDiv.style.position = originalPos; + } + mapDiv.style.width = originalWidth; + mapDiv.style.height = originalHeight; + mapDiv.style.top = originalTop; + mapDiv.style.left = originalLeft; + mapDiv.style.zIndex = originalZIndex; + document.body.style.overflow = originalOverflow; + controlText.innerHTML = "" + enterFull + ""; + fullScreen = false; + google.maps.event.trigger(map, "resize"); + map.setCenter(center); + clearInterval(interval); + }; + // Setup the click event listener + google.maps.event.addDomListener(controlUI, "click", function () { + if (!fullScreen) { + controlDiv.goFullScreen(); + } + else { + controlDiv.exitFullScreen(); + } + }); - document.onkeydown = function(evt) { + document.onkeydown = function(evt) { evt = evt || window.event; if (evt.keyCode == 27) { if (fullScreen) { @@ -133,5 +133,5 @@ function FullScreenControl(map, enterFull, exitFull) { } } }; - return controlDiv; + return controlDiv; } diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogle.ss index 6d4594f..94fddb4 100644 --- a/javascript/google/MapGoogle.ss +++ b/javascript/google/MapGoogle.ss @@ -1,15 +1,16 @@ <% include GoogleJavaScript %> - -
style="width:{$Width}; height: {$Height};"<% end_if %><% if AdditionalCssClasses %> class="$AdditionalCssClasses"<% end_if %>> -
\ No newline at end of file +
style="width:{$Width}; height: {$Height};" +<% end_if %><% if AdditionalCssClasses %> class="$AdditionalCssClasses"<% end_if %>> +
diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index 1f14c99..78aa741 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -16,7 +16,9 @@ var mappableMapCtr = 0; * @param {boolean} defaultHideMarker Whether or not to hide markers initially * @return {MapMarker} Google map pin object */ -function createMarker(map, lat, lng, html, category, icon, useClusterer, enableWindowZoom, defaultHideMarker) { +function createMarker(map, lat, lng, html, category, icon, useClusterer, enableWindowZoom, + defaultHideMarker) { + mapId = map.getDiv().getAttribute('id'); var marker = new google.maps.Marker(); @@ -126,8 +128,9 @@ function addKmlFiles(map, kmlFiles) { } -function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLat, maxLng, mapType, markers, lines, kmlFiles, - jsonMapStyles, enableAutomaticCenterZoom, useClusterer, allowFullScreen) { +function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLat, maxLng, mapType, + markers, lines, kmlFiles, jsonMapStyles, enableAutomaticCenterZoom, useClusterer, + allowFullScreen) { var newMap = []; newMap.googleMapID = googleMapID; newMap.zoom = zoom; @@ -205,7 +208,8 @@ function loadedGoogleMapsAPI() { map.setMapTypeId(google.maps.MapTypeId.ROADMAP); } - var markers = addAllMarkers(map, map_info.markers, map_info.useClusterer, map_info.enableAutomaticCenterZoom, map_info.defaultHideMarker); + var markers = addAllMarkers(map, map_info.markers, map_info.useClusterer, + map_info.enableAutomaticCenterZoom, map_info.defaultHideMarker); if (map_info.useClusterer) { var mcOptions = {gridSize: 50, maxZoom: 17}; diff --git a/javascript/google/markerclusterer.js b/javascript/google/markerclusterer.js index 445cd1b..2783082 100644 --- a/javascript/google/markerclusterer.js +++ b/javascript/google/markerclusterer.js @@ -60,123 +60,123 @@ * @extends google.maps.OverlayView */ function MarkerClusterer(map, opt_markers, opt_options) { - // MarkerClusterer implements google.maps.OverlayView interface. We use the - // extend function to extend MarkerClusterer with google.maps.OverlayView - // because it might not always be available when the code is defined so we - // look for it at the last possible moment. If it doesn't exist now then - // there is no point going ahead :) - this.extend(MarkerClusterer, google.maps.OverlayView); - this.map_ = map; - - /** - * @type {Array.} - * @private - */ - this.markers_ = []; - - /** - * @type {Array.} - */ - this.clusters_ = []; - - this.sizes = [53, 56, 66, 78, 90]; - - /** - * @private - */ - this.styles_ = []; - - /** - * @type {boolean} - * @private - */ - this.ready_ = false; - - var options = opt_options || {}; - - /** - * @type {number} - * @private - */ - this.gridSize_ = options['gridSize'] || 60; - - /** - * @private - */ - this.minClusterSize_ = options['minimumClusterSize'] || 2; - - - /** - * @type {?number} - * @private - */ - this.maxZoom_ = options['maxZoom'] || null; - - this.styles_ = options['styles'] || []; - - /** - * @type {string} - * @private - */ - this.imagePath_ = options['imagePath'] || - this.MARKER_CLUSTER_IMAGE_PATH_; - - /** - * @type {string} - * @private - */ - this.imageExtension_ = options['imageExtension'] || - this.MARKER_CLUSTER_IMAGE_EXTENSION_; - - /** - * @type {boolean} - * @private - */ - this.zoomOnClick_ = true; - - if (options['zoomOnClick'] != undefined) { - this.zoomOnClick_ = options['zoomOnClick']; - } - - /** - * @type {boolean} - * @private - */ - this.averageCenter_ = false; - - if (options['averageCenter'] != undefined) { - this.averageCenter_ = options['averageCenter']; - } - - this.setupStyles_(); - - this.setMap(map); - - /** - * @type {number} - * @private - */ - this.prevZoom_ = this.map_.getZoom(); - - // Add the map event listeners - var that = this; - google.maps.event.addListener(this.map_, 'zoom_changed', function() { - var zoom = that.map_.getZoom(); - - if (that.prevZoom_ != zoom) { - that.prevZoom_ = zoom; - that.resetViewport(); - } - }); - - google.maps.event.addListener(this.map_, 'idle', function() { - that.redraw(); - }); - - // Finally, add the markers - if (opt_markers && opt_markers.length) { - this.addMarkers(opt_markers, false); - } + // MarkerClusterer implements google.maps.OverlayView interface. We use the + // extend function to extend MarkerClusterer with google.maps.OverlayView + // because it might not always be available when the code is defined so we + // look for it at the last possible moment. If it doesn't exist now then + // there is no point going ahead :) + this.extend(MarkerClusterer, google.maps.OverlayView); + this.map_ = map; + + /** + * @type {Array.} + * @private + */ + this.markers_ = []; + + /** + * @type {Array.} + */ + this.clusters_ = []; + + this.sizes = [53, 56, 66, 78, 90]; + + /** + * @private + */ + this.styles_ = []; + + /** + * @type {boolean} + * @private + */ + this.ready_ = false; + + var options = opt_options || {}; + + /** + * @type {number} + * @private + */ + this.gridSize_ = options['gridSize'] || 60; + + /** + * @private + */ + this.minClusterSize_ = options['minimumClusterSize'] || 2; + + + /** + * @type {?number} + * @private + */ + this.maxZoom_ = options['maxZoom'] || null; + + this.styles_ = options['styles'] || []; + + /** + * @type {string} + * @private + */ + this.imagePath_ = options['imagePath'] || + this.MARKER_CLUSTER_IMAGE_PATH_; + + /** + * @type {string} + * @private + */ + this.imageExtension_ = options['imageExtension'] || + this.MARKER_CLUSTER_IMAGE_EXTENSION_; + + /** + * @type {boolean} + * @private + */ + this.zoomOnClick_ = true; + + if (options['zoomOnClick'] != undefined) { + this.zoomOnClick_ = options['zoomOnClick']; + } + + /** + * @type {boolean} + * @private + */ + this.averageCenter_ = false; + + if (options['averageCenter'] != undefined) { + this.averageCenter_ = options['averageCenter']; + } + + this.setupStyles_(); + + this.setMap(map); + + /** + * @type {number} + * @private + */ + this.prevZoom_ = this.map_.getZoom(); + + // Add the map event listeners + var that = this; + google.maps.event.addListener(this.map_, 'zoom_changed', function() { + var zoom = that.map_.getZoom(); + + if (that.prevZoom_ != zoom) { + that.prevZoom_ = zoom; + that.resetViewport(); + } + }); + + google.maps.event.addListener(this.map_, 'idle', function() { + that.redraw(); + }); + + // Finally, add the markers + if (opt_markers && opt_markers.length) { + this.addMarkers(opt_markers, false); + } } @@ -187,8 +187,8 @@ function MarkerClusterer(map, opt_markers, opt_options) { * @private */ MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_ = - 'http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/' + - 'images/m'; + 'http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/' + + 'images/m'; /** @@ -209,12 +209,12 @@ MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_ = 'png'; * @ignore */ MarkerClusterer.prototype.extend = function(obj1, obj2) { - return (function(object) { - for (var property in object.prototype) { - this.prototype[property] = object.prototype[property]; - } - return this; - }).apply(obj1, [obj2]); + return (function(object) { + for (var property in object.prototype) { + this.prototype[property] = object.prototype[property]; + } + return this; + }).apply(obj1, [obj2]); }; @@ -223,7 +223,7 @@ MarkerClusterer.prototype.extend = function(obj1, obj2) { * @ignore */ MarkerClusterer.prototype.onAdd = function() { - this.setReady_(true); + this.setReady_(true); }; /** @@ -238,30 +238,30 @@ MarkerClusterer.prototype.draw = function() {}; * @private */ MarkerClusterer.prototype.setupStyles_ = function() { - if (this.styles_.length) { - return; - } - - for (var i = 0, size; size = this.sizes[i]; i++) { - this.styles_.push({ - url: this.imagePath_ + (i + 1) + '.' + this.imageExtension_, - height: size, - width: size - }); - } + if (this.styles_.length) { + return; + } + + for (var i = 0, size; size = this.sizes[i]; i++) { + this.styles_.push({ + url: this.imagePath_ + (i + 1) + '.' + this.imageExtension_, + height: size, + width: size + }); + } }; /** * Fit the map to the bounds of the markers in the clusterer. */ MarkerClusterer.prototype.fitMapToMarkers = function() { - var markers = this.getMarkers(); - var bounds = new google.maps.LatLngBounds(); - for (var i = 0, marker; marker = markers[i]; i++) { - bounds.extend(marker.getPosition()); - } + var markers = this.getMarkers(); + var bounds = new google.maps.LatLngBounds(); + for (var i = 0, marker; marker = markers[i]; i++) { + bounds.extend(marker.getPosition()); + } - this.map_.fitBounds(bounds); + this.map_.fitBounds(bounds); }; @@ -271,7 +271,7 @@ MarkerClusterer.prototype.fitMapToMarkers = function() { * @param {Object} styles The style to set. */ MarkerClusterer.prototype.setStyles = function(styles) { - this.styles_ = styles; + this.styles_ = styles; }; @@ -281,7 +281,7 @@ MarkerClusterer.prototype.setStyles = function(styles) { * @return {Object} The styles object. */ MarkerClusterer.prototype.getStyles = function() { - return this.styles_; + return this.styles_; }; @@ -291,7 +291,7 @@ MarkerClusterer.prototype.getStyles = function() { * @return {boolean} True if zoomOnClick_ is set. */ MarkerClusterer.prototype.isZoomOnClick = function() { - return this.zoomOnClick_; + return this.zoomOnClick_; }; /** @@ -300,7 +300,7 @@ MarkerClusterer.prototype.isZoomOnClick = function() { * @return {boolean} True if averageCenter_ is set. */ MarkerClusterer.prototype.isAverageCenter = function() { - return this.averageCenter_; + return this.averageCenter_; }; @@ -310,7 +310,7 @@ MarkerClusterer.prototype.isAverageCenter = function() { * @return {Array.} The markers. */ MarkerClusterer.prototype.getMarkers = function() { - return this.markers_; + return this.markers_; }; @@ -320,7 +320,7 @@ MarkerClusterer.prototype.getMarkers = function() { * @return {Number} The number of markers. */ MarkerClusterer.prototype.getTotalMarkers = function() { - return this.markers_.length; + return this.markers_.length; }; @@ -330,7 +330,7 @@ MarkerClusterer.prototype.getTotalMarkers = function() { * @param {number} maxZoom The max zoom level. */ MarkerClusterer.prototype.setMaxZoom = function(maxZoom) { - this.maxZoom_ = maxZoom; + this.maxZoom_ = maxZoom; }; @@ -340,7 +340,7 @@ MarkerClusterer.prototype.setMaxZoom = function(maxZoom) { * @return {number} The max zoom level. */ MarkerClusterer.prototype.getMaxZoom = function() { - return this.maxZoom_; + return this.maxZoom_; }; @@ -353,19 +353,19 @@ MarkerClusterer.prototype.getMaxZoom = function() { * @private */ MarkerClusterer.prototype.calculator_ = function(markers, numStyles) { - var index = 0; - var count = markers.length; - var dv = count; - while (dv !== 0) { - dv = parseInt(dv / 10, 10); - index++; - } - - index = Math.min(index, numStyles); - return { - text: count, - index: index - }; + var index = 0; + var count = markers.length; + var dv = count; + while (dv !== 0) { + dv = parseInt(dv / 10, 10); + index++; + } + + index = Math.min(index, numStyles); + return { + text: count, + index: index + }; }; @@ -378,7 +378,7 @@ MarkerClusterer.prototype.calculator_ = function(markers, numStyles) { * */ MarkerClusterer.prototype.setCalculator = function(calculator) { - this.calculator_ = calculator; + this.calculator_ = calculator; }; @@ -388,7 +388,7 @@ MarkerClusterer.prototype.setCalculator = function(calculator) { * @return {function(Array, number)} the calculator function. */ MarkerClusterer.prototype.getCalculator = function() { - return this.calculator_; + return this.calculator_; }; @@ -399,12 +399,12 @@ MarkerClusterer.prototype.getCalculator = function() { * @param {boolean=} opt_nodraw Whether to redraw the clusters. */ MarkerClusterer.prototype.addMarkers = function(markers, opt_nodraw) { - for (var i = 0, marker; marker = markers[i]; i++) { - this.pushMarkerTo_(marker); - } - if (!opt_nodraw) { - this.redraw(); - } + for (var i = 0, marker; marker = markers[i]; i++) { + this.pushMarkerTo_(marker); + } + if (!opt_nodraw) { + this.redraw(); + } }; @@ -415,17 +415,17 @@ MarkerClusterer.prototype.addMarkers = function(markers, opt_nodraw) { * @private */ MarkerClusterer.prototype.pushMarkerTo_ = function(marker) { - marker.isAdded = false; - if (marker['draggable']) { - // If the marker is draggable add a listener so we update the clusters on - // the drag end. - var that = this; - google.maps.event.addListener(marker, 'dragend', function() { - marker.isAdded = false; - that.repaint(); - }); - } - this.markers_.push(marker); + marker.isAdded = false; + if (marker['draggable']) { + // If the marker is draggable add a listener so we update the clusters on + // the drag end. + var that = this; + google.maps.event.addListener(marker, 'dragend', function() { + marker.isAdded = false; + that.repaint(); + }); + } + this.markers_.push(marker); }; @@ -436,10 +436,10 @@ MarkerClusterer.prototype.pushMarkerTo_ = function(marker) { * @param {boolean=} opt_nodraw Whether to redraw the clusters. */ MarkerClusterer.prototype.addMarker = function(marker, opt_nodraw) { - this.pushMarkerTo_(marker); - if (!opt_nodraw) { - this.redraw(); - } + this.pushMarkerTo_(marker); + if (!opt_nodraw) { + this.redraw(); + } }; @@ -451,28 +451,28 @@ MarkerClusterer.prototype.addMarker = function(marker, opt_nodraw) { * @private */ MarkerClusterer.prototype.removeMarker_ = function(marker) { - var index = -1; - if (this.markers_.indexOf) { - index = this.markers_.indexOf(marker); - } else { - for (var i = 0, m; m = this.markers_[i]; i++) { - if (m == marker) { - index = i; - break; - } - } - } - - if (index == -1) { - // Marker is not in our list of markers. - return false; - } - - marker.setMap(null); - - this.markers_.splice(index, 1); - - return true; + var index = -1; + if (this.markers_.indexOf) { + index = this.markers_.indexOf(marker); + } else { + for (var i = 0, m; m = this.markers_[i]; i++) { + if (m == marker) { + index = i; + break; + } + } + } + + if (index == -1) { + // Marker is not in our list of markers. + return false; + } + + marker.setMap(null); + + this.markers_.splice(index, 1); + + return true; }; @@ -484,15 +484,15 @@ MarkerClusterer.prototype.removeMarker_ = function(marker) { * @return {boolean} True if the marker was removed. */ MarkerClusterer.prototype.removeMarker = function(marker, opt_nodraw) { - var removed = this.removeMarker_(marker); - - if (!opt_nodraw && removed) { - this.resetViewport(); - this.redraw(); - return true; - } else { - return false; - } + var removed = this.removeMarker_(marker); + + if (!opt_nodraw && removed) { + this.resetViewport(); + this.redraw(); + return true; + } else { + return false; + } }; @@ -503,18 +503,18 @@ MarkerClusterer.prototype.removeMarker = function(marker, opt_nodraw) { * @param {boolean=} opt_nodraw Optional boolean to force no redraw. */ MarkerClusterer.prototype.removeMarkers = function(markers, opt_nodraw) { - var removed = false; - - for (var i = 0, marker; marker = markers[i]; i++) { - var r = this.removeMarker_(marker); - removed = removed || r; - } - - if (!opt_nodraw && removed) { - this.resetViewport(); - this.redraw(); - return true; - } + var removed = false; + + for (var i = 0, marker; marker = markers[i]; i++) { + var r = this.removeMarker_(marker); + removed = removed || r; + } + + if (!opt_nodraw && removed) { + this.resetViewport(); + this.redraw(); + return true; + } }; @@ -525,10 +525,10 @@ MarkerClusterer.prototype.removeMarkers = function(markers, opt_nodraw) { * @private */ MarkerClusterer.prototype.setReady_ = function(ready) { - if (!this.ready_) { - this.ready_ = ready; - this.createClusters_(); - } + if (!this.ready_) { + this.ready_ = ready; + this.createClusters_(); + } }; @@ -538,7 +538,7 @@ MarkerClusterer.prototype.setReady_ = function(ready) { * @return {number} The number of clusters. */ MarkerClusterer.prototype.getTotalClusters = function() { - return this.clusters_.length; + return this.clusters_.length; }; @@ -548,7 +548,7 @@ MarkerClusterer.prototype.getTotalClusters = function() { * @return {google.maps.Map} The map. */ MarkerClusterer.prototype.getMap = function() { - return this.map_; + return this.map_; }; @@ -558,7 +558,7 @@ MarkerClusterer.prototype.getMap = function() { * @param {google.maps.Map} map The map. */ MarkerClusterer.prototype.setMap = function(map) { - this.map_ = map; + this.map_ = map; }; @@ -568,7 +568,7 @@ MarkerClusterer.prototype.setMap = function(map) { * @return {number} The grid size. */ MarkerClusterer.prototype.getGridSize = function() { - return this.gridSize_; + return this.gridSize_; }; @@ -578,7 +578,7 @@ MarkerClusterer.prototype.getGridSize = function() { * @param {number} size The grid size. */ MarkerClusterer.prototype.setGridSize = function(size) { - this.gridSize_ = size; + this.gridSize_ = size; }; @@ -588,7 +588,7 @@ MarkerClusterer.prototype.setGridSize = function(size) { * @return {number} The grid size. */ MarkerClusterer.prototype.getMinClusterSize = function() { - return this.minClusterSize_; + return this.minClusterSize_; }; /** @@ -597,7 +597,7 @@ MarkerClusterer.prototype.getMinClusterSize = function() { * @param {number} size The grid size. */ MarkerClusterer.prototype.setMinClusterSize = function(size) { - this.minClusterSize_ = size; + this.minClusterSize_ = size; }; @@ -608,32 +608,32 @@ MarkerClusterer.prototype.setMinClusterSize = function(size) { * @return {google.maps.LatLngBounds} The extended bounds. */ MarkerClusterer.prototype.getExtendedBounds = function(bounds) { - var projection = this.getProjection(); + var projection = this.getProjection(); - // Turn the bounds into latlng. - var tr = new google.maps.LatLng(bounds.getNorthEast().lat(), - bounds.getNorthEast().lng()); - var bl = new google.maps.LatLng(bounds.getSouthWest().lat(), - bounds.getSouthWest().lng()); + // Turn the bounds into latlng. + var tr = new google.maps.LatLng(bounds.getNorthEast().lat(), + bounds.getNorthEast().lng()); + var bl = new google.maps.LatLng(bounds.getSouthWest().lat(), + bounds.getSouthWest().lng()); - // Convert the points to pixels and the extend out by the grid size. - var trPix = projection.fromLatLngToDivPixel(tr); - trPix.x += this.gridSize_; - trPix.y -= this.gridSize_; + // Convert the points to pixels and the extend out by the grid size. + var trPix = projection.fromLatLngToDivPixel(tr); + trPix.x += this.gridSize_; + trPix.y -= this.gridSize_; - var blPix = projection.fromLatLngToDivPixel(bl); - blPix.x -= this.gridSize_; - blPix.y += this.gridSize_; + var blPix = projection.fromLatLngToDivPixel(bl); + blPix.x -= this.gridSize_; + blPix.y += this.gridSize_; - // Convert the pixel points back to LatLng - var ne = projection.fromDivPixelToLatLng(trPix); - var sw = projection.fromDivPixelToLatLng(blPix); + // Convert the pixel points back to LatLng + var ne = projection.fromDivPixelToLatLng(trPix); + var sw = projection.fromDivPixelToLatLng(blPix); - // Extend the bounds to contain the new bounds. - bounds.extend(ne); - bounds.extend(sw); + // Extend the bounds to contain the new bounds. + bounds.extend(ne); + bounds.extend(sw); - return bounds; + return bounds; }; @@ -646,7 +646,7 @@ MarkerClusterer.prototype.getExtendedBounds = function(bounds) { * @private */ MarkerClusterer.prototype.isMarkerInBounds_ = function(marker, bounds) { - return bounds.contains(marker.getPosition()); + return bounds.contains(marker.getPosition()); }; @@ -654,10 +654,10 @@ MarkerClusterer.prototype.isMarkerInBounds_ = function(marker, bounds) { * Clears all clusters and markers from the clusterer. */ MarkerClusterer.prototype.clearMarkers = function() { - this.resetViewport(true); + this.resetViewport(true); - // Set the markers a empty array. - this.markers_ = []; + // Set the markers a empty array. + this.markers_ = []; }; @@ -666,38 +666,38 @@ MarkerClusterer.prototype.clearMarkers = function() { * @param {boolean} opt_hide To also hide the marker. */ MarkerClusterer.prototype.resetViewport = function(opt_hide) { - // Remove all the clusters - for (var i = 0, cluster; cluster = this.clusters_[i]; i++) { - cluster.remove(); - } - - // Reset the markers to not be added and to be invisible. - for (var i = 0, marker; marker = this.markers_[i]; i++) { - marker.isAdded = false; - if (opt_hide) { - marker.setMap(null); - } - } - - this.clusters_ = []; + // Remove all the clusters + for (var i = 0, cluster; cluster = this.clusters_[i]; i++) { + cluster.remove(); + } + + // Reset the markers to not be added and to be invisible. + for (var i = 0, marker; marker = this.markers_[i]; i++) { + marker.isAdded = false; + if (opt_hide) { + marker.setMap(null); + } + } + + this.clusters_ = []; }; /** * */ MarkerClusterer.prototype.repaint = function() { - var oldClusters = this.clusters_.slice(); - this.clusters_.length = 0; - this.resetViewport(); - this.redraw(); - - // Remove the old clusters. - // Do it in a timeout so the other clusters have been drawn first. - window.setTimeout(function() { - for (var i = 0, cluster; cluster = oldClusters[i]; i++) { - cluster.remove(); - } - }, 0); + var oldClusters = this.clusters_.slice(); + this.clusters_.length = 0; + this.resetViewport(); + this.redraw(); + + // Remove the old clusters. + // Do it in a timeout so the other clusters have been drawn first. + window.setTimeout(function() { + for (var i = 0, cluster; cluster = oldClusters[i]; i++) { + cluster.remove(); + } + }, 0); }; @@ -705,7 +705,7 @@ MarkerClusterer.prototype.repaint = function() { * Redraws the clusters. */ MarkerClusterer.prototype.redraw = function() { - this.createClusters_(); + this.createClusters_(); }; @@ -719,19 +719,19 @@ MarkerClusterer.prototype.redraw = function() { * @private */ MarkerClusterer.prototype.distanceBetweenPoints_ = function(p1, p2) { - if (!p1 || !p2) { - return 0; - } - - var R = 6371; // Radius of the Earth in km - var dLat = (p2.lat() - p1.lat()) * Math.PI / 180; - var dLon = (p2.lng() - p1.lng()) * Math.PI / 180; - var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + - Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) * - Math.sin(dLon / 2) * Math.sin(dLon / 2); - var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - var d = R * c; - return d; + if (!p1 || !p2) { + return 0; + } + + var R = 6371; // Radius of the Earth in km + var dLat = (p2.lat() - p1.lat()) * Math.PI / 180; + var dLon = (p2.lng() - p1.lng()) * Math.PI / 180; + var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) * + Math.sin(dLon / 2) * Math.sin(dLon / 2); + var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + var d = R * c; + return d; }; @@ -742,27 +742,27 @@ MarkerClusterer.prototype.distanceBetweenPoints_ = function(p1, p2) { * @private */ MarkerClusterer.prototype.addToClosestCluster_ = function(marker) { - var distance = 40000; // Some large number - var clusterToAddTo = null; - var pos = marker.getPosition(); - for (var i = 0, cluster; cluster = this.clusters_[i]; i++) { - var center = cluster.getCenter(); - if (center) { - var d = this.distanceBetweenPoints_(center, marker.getPosition()); - if (d < distance) { - distance = d; - clusterToAddTo = cluster; - } - } - } - - if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) { - clusterToAddTo.addMarker(marker); - } else { - var cluster = new Cluster(this); - cluster.addMarker(marker); - this.clusters_.push(cluster); - } + var distance = 40000; // Some large number + var clusterToAddTo = null; + var pos = marker.getPosition(); + for (var i = 0, cluster; cluster = this.clusters_[i]; i++) { + var center = cluster.getCenter(); + if (center) { + var d = this.distanceBetweenPoints_(center, marker.getPosition()); + if (d < distance) { + distance = d; + clusterToAddTo = cluster; + } + } + } + + if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) { + clusterToAddTo.addMarker(marker); + } else { + var cluster = new Cluster(this); + cluster.addMarker(marker); + this.clusters_.push(cluster); + } }; @@ -772,21 +772,21 @@ MarkerClusterer.prototype.addToClosestCluster_ = function(marker) { * @private */ MarkerClusterer.prototype.createClusters_ = function() { - if (!this.ready_) { - return; - } - - // Get our current map view bounds. - // Create a new bounds object so we don't affect the map. - var mapBounds = new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(), - this.map_.getBounds().getNorthEast()); - var bounds = this.getExtendedBounds(mapBounds); - - for (var i = 0, marker; marker = this.markers_[i]; i++) { - if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) { - this.addToClosestCluster_(marker); - } - } + if (!this.ready_) { + return; + } + + // Get our current map view bounds. + // Create a new bounds object so we don't affect the map. + var mapBounds = new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(), + this.map_.getBounds().getNorthEast()); + var bounds = this.getExtendedBounds(mapBounds); + + for (var i = 0, marker; marker = this.markers_[i]; i++) { + if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) { + this.addToClosestCluster_(marker); + } + } }; @@ -799,16 +799,16 @@ MarkerClusterer.prototype.createClusters_ = function() { * @ignore */ function Cluster(markerClusterer) { - this.markerClusterer_ = markerClusterer; - this.map_ = markerClusterer.getMap(); - this.gridSize_ = markerClusterer.getGridSize(); - this.minClusterSize_ = markerClusterer.getMinClusterSize(); - this.averageCenter_ = markerClusterer.isAverageCenter(); - this.center_ = null; - this.markers_ = []; - this.bounds_ = null; - this.clusterIcon_ = new ClusterIcon(this, markerClusterer.getStyles(), - markerClusterer.getGridSize()); + this.markerClusterer_ = markerClusterer; + this.map_ = markerClusterer.getMap(); + this.gridSize_ = markerClusterer.getGridSize(); + this.minClusterSize_ = markerClusterer.getMinClusterSize(); + this.averageCenter_ = markerClusterer.isAverageCenter(); + this.center_ = null; + this.markers_ = []; + this.bounds_ = null; + this.clusterIcon_ = new ClusterIcon(this, markerClusterer.getStyles(), + markerClusterer.getGridSize()); } /** @@ -818,16 +818,16 @@ function Cluster(markerClusterer) { * @return {boolean} True if the marker is already added. */ Cluster.prototype.isMarkerAlreadyAdded = function(marker) { - if (this.markers_.indexOf) { - return this.markers_.indexOf(marker) != -1; - } else { - for (var i = 0, m; m = this.markers_[i]; i++) { - if (m == marker) { - return true; - } - } - } - return false; + if (this.markers_.indexOf) { + return this.markers_.indexOf(marker) != -1; + } else { + for (var i = 0, m; m = this.markers_[i]; i++) { + if (m == marker) { + return true; + } + } + } + return false; }; @@ -838,45 +838,45 @@ Cluster.prototype.isMarkerAlreadyAdded = function(marker) { * @return {boolean} True if the marker was added. */ Cluster.prototype.addMarker = function(marker) { - if (this.isMarkerAlreadyAdded(marker)) { - return false; - } - - if (!this.center_) { - this.center_ = marker.getPosition(); - this.calculateBounds_(); - } else { - if (this.averageCenter_) { - var l = this.markers_.length + 1; - var lat = (this.center_.lat() * (l-1) + marker.getPosition().lat()) / l; - var lng = (this.center_.lng() * (l-1) + marker.getPosition().lng()) / l; - this.center_ = new google.maps.LatLng(lat, lng); - this.calculateBounds_(); - } - } - - marker.isAdded = true; - this.markers_.push(marker); - - var len = this.markers_.length; - if (len < this.minClusterSize_ && marker.getMap() != this.map_) { - // Min cluster size not reached so show the marker. - marker.setMap(this.map_); - } - - if (len == this.minClusterSize_) { - // Hide the markers that were showing. - for (var i = 0; i < len; i++) { - this.markers_[i].setMap(null); - } - } - - if (len >= this.minClusterSize_) { - marker.setMap(null); - } - - this.updateIcon(); - return true; + if (this.isMarkerAlreadyAdded(marker)) { + return false; + } + + if (!this.center_) { + this.center_ = marker.getPosition(); + this.calculateBounds_(); + } else { + if (this.averageCenter_) { + var l = this.markers_.length + 1; + var lat = (this.center_.lat() * (l-1) + marker.getPosition().lat()) / l; + var lng = (this.center_.lng() * (l-1) + marker.getPosition().lng()) / l; + this.center_ = new google.maps.LatLng(lat, lng); + this.calculateBounds_(); + } + } + + marker.isAdded = true; + this.markers_.push(marker); + + var len = this.markers_.length; + if (len < this.minClusterSize_ && marker.getMap() != this.map_) { + // Min cluster size not reached so show the marker. + marker.setMap(this.map_); + } + + if (len == this.minClusterSize_) { + // Hide the markers that were showing. + for (var i = 0; i < len; i++) { + this.markers_[i].setMap(null); + } + } + + if (len >= this.minClusterSize_) { + marker.setMap(null); + } + + this.updateIcon(); + return true; }; @@ -886,7 +886,7 @@ Cluster.prototype.addMarker = function(marker) { * @return {MarkerClusterer} The associated marker clusterer. */ Cluster.prototype.getMarkerClusterer = function() { - return this.markerClusterer_; + return this.markerClusterer_; }; @@ -896,12 +896,12 @@ Cluster.prototype.getMarkerClusterer = function() { * @return {google.maps.LatLngBounds} the cluster bounds. */ Cluster.prototype.getBounds = function() { - var bounds = new google.maps.LatLngBounds(this.center_, this.center_); - var markers = this.getMarkers(); - for (var i = 0, marker; marker = markers[i]; i++) { - bounds.extend(marker.getPosition()); - } - return bounds; + var bounds = new google.maps.LatLngBounds(this.center_, this.center_); + var markers = this.getMarkers(); + for (var i = 0, marker; marker = markers[i]; i++) { + bounds.extend(marker.getPosition()); + } + return bounds; }; @@ -909,9 +909,9 @@ Cluster.prototype.getBounds = function() { * Removes the cluster */ Cluster.prototype.remove = function() { - this.clusterIcon_.remove(); - this.markers_.length = 0; - delete this.markers_; + this.clusterIcon_.remove(); + this.markers_.length = 0; + delete this.markers_; }; @@ -921,7 +921,7 @@ Cluster.prototype.remove = function() { * @return {number} The cluster center. */ Cluster.prototype.getSize = function() { - return this.markers_.length; + return this.markers_.length; }; @@ -931,7 +931,7 @@ Cluster.prototype.getSize = function() { * @return {Array.} The cluster center. */ Cluster.prototype.getMarkers = function() { - return this.markers_; + return this.markers_; }; @@ -941,7 +941,7 @@ Cluster.prototype.getMarkers = function() { * @return {google.maps.LatLng} The cluster center. */ Cluster.prototype.getCenter = function() { - return this.center_; + return this.center_; }; @@ -951,8 +951,8 @@ Cluster.prototype.getCenter = function() { * @private */ Cluster.prototype.calculateBounds_ = function() { - var bounds = new google.maps.LatLngBounds(this.center_, this.center_); - this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds); + var bounds = new google.maps.LatLngBounds(this.center_, this.center_); + this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds); }; @@ -963,7 +963,7 @@ Cluster.prototype.calculateBounds_ = function() { * @return {boolean} True if the marker lies in the bounds. */ Cluster.prototype.isMarkerInClusterBounds = function(marker) { - return this.bounds_.contains(marker.getPosition()); + return this.bounds_.contains(marker.getPosition()); }; @@ -973,7 +973,7 @@ Cluster.prototype.isMarkerInClusterBounds = function(marker) { * @return {google.maps.Map} The map. */ Cluster.prototype.getMap = function() { - return this.map_; + return this.map_; }; @@ -981,28 +981,28 @@ Cluster.prototype.getMap = function() { * Updates the cluster icon */ Cluster.prototype.updateIcon = function() { - var zoom = this.map_.getZoom(); - var mz = this.markerClusterer_.getMaxZoom(); - - if (mz && zoom > mz) { - // The zoom is greater than our max zoom so show all the markers in cluster. - for (var i = 0, marker; marker = this.markers_[i]; i++) { - marker.setMap(this.map_); - } - return; - } - - if (this.markers_.length < this.minClusterSize_) { - // Min cluster size not yet reached. - this.clusterIcon_.hide(); - return; - } - - var numStyles = this.markerClusterer_.getStyles().length; - var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles); - this.clusterIcon_.setCenter(this.center_); - this.clusterIcon_.setSums(sums); - this.clusterIcon_.show(); + var zoom = this.map_.getZoom(); + var mz = this.markerClusterer_.getMaxZoom(); + + if (mz && zoom > mz) { + // The zoom is greater than our max zoom so show all the markers in cluster. + for (var i = 0, marker; marker = this.markers_[i]; i++) { + marker.setMap(this.map_); + } + return; + } + + if (this.markers_.length < this.minClusterSize_) { + // Min cluster size not yet reached. + this.clusterIcon_.hide(); + return; + } + + var numStyles = this.markerClusterer_.getStyles().length; + var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles); + this.clusterIcon_.setCenter(this.center_); + this.clusterIcon_.setSums(sums); + this.clusterIcon_.show(); }; @@ -1024,18 +1024,18 @@ Cluster.prototype.updateIcon = function() { * @ignore */ function ClusterIcon(cluster, styles, opt_padding) { - cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView); - - this.styles_ = styles; - this.padding_ = opt_padding || 0; - this.cluster_ = cluster; - this.center_ = null; - this.map_ = cluster.getMap(); - this.div_ = null; - this.sums_ = null; - this.visible_ = false; - - this.setMap(this.map_); + cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView); + + this.styles_ = styles; + this.padding_ = opt_padding || 0; + this.cluster_ = cluster; + this.center_ = null; + this.map_ = cluster.getMap(); + this.div_ = null; + this.sums_ = null; + this.visible_ = false; + + this.setMap(this.map_); } @@ -1043,15 +1043,15 @@ function ClusterIcon(cluster, styles, opt_padding) { * Triggers the clusterclick event and zoom's if the option is set. */ ClusterIcon.prototype.triggerClusterClick = function() { - var markerClusterer = this.cluster_.getMarkerClusterer(); + var markerClusterer = this.cluster_.getMarkerClusterer(); - // Trigger the clusterclick event. - google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster_); + // Trigger the clusterclick event. + google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster_); - if (markerClusterer.isZoomOnClick()) { - // Zoom into the cluster. - this.map_.fitBounds(this.cluster_.getBounds()); - } + if (markerClusterer.isZoomOnClick()) { + // Zoom into the cluster. + this.map_.fitBounds(this.cluster_.getBounds()); + } }; @@ -1060,20 +1060,20 @@ ClusterIcon.prototype.triggerClusterClick = function() { * @ignore */ ClusterIcon.prototype.onAdd = function() { - this.div_ = document.createElement('DIV'); - if (this.visible_) { - var pos = this.getPosFromLatLng_(this.center_); - this.div_.style.cssText = this.createCss(pos); - this.div_.innerHTML = this.sums_.text; - } - - var panes = this.getPanes(); - panes.overlayMouseTarget.appendChild(this.div_); - - var that = this; - google.maps.event.addDomListener(this.div_, 'click', function() { - that.triggerClusterClick(); - }); + this.div_ = document.createElement('DIV'); + if (this.visible_) { + var pos = this.getPosFromLatLng_(this.center_); + this.div_.style.cssText = this.createCss(pos); + this.div_.innerHTML = this.sums_.text; + } + + var panes = this.getPanes(); + panes.overlayMouseTarget.appendChild(this.div_); + + var that = this; + google.maps.event.addDomListener(this.div_, 'click', function() { + that.triggerClusterClick(); + }); }; @@ -1085,10 +1085,10 @@ ClusterIcon.prototype.onAdd = function() { * @private */ ClusterIcon.prototype.getPosFromLatLng_ = function(latlng) { - var pos = this.getProjection().fromLatLngToDivPixel(latlng); - pos.x -= parseInt(this.width_ / 2, 10); - pos.y -= parseInt(this.height_ / 2, 10); - return pos; + var pos = this.getProjection().fromLatLngToDivPixel(latlng); + pos.x -= parseInt(this.width_ / 2, 10); + pos.y -= parseInt(this.height_ / 2, 10); + return pos; }; @@ -1097,11 +1097,11 @@ ClusterIcon.prototype.getPosFromLatLng_ = function(latlng) { * @ignore */ ClusterIcon.prototype.draw = function() { - if (this.visible_) { - var pos = this.getPosFromLatLng_(this.center_); - this.div_.style.top = pos.y + 'px'; - this.div_.style.left = pos.x + 'px'; - } + if (this.visible_) { + var pos = this.getPosFromLatLng_(this.center_); + this.div_.style.top = pos.y + 'px'; + this.div_.style.left = pos.x + 'px'; + } }; @@ -1109,10 +1109,10 @@ ClusterIcon.prototype.draw = function() { * Hide the icon. */ ClusterIcon.prototype.hide = function() { - if (this.div_) { - this.div_.style.display = 'none'; - } - this.visible_ = false; + if (this.div_) { + this.div_.style.display = 'none'; + } + this.visible_ = false; }; @@ -1120,12 +1120,12 @@ ClusterIcon.prototype.hide = function() { * Position and show the icon. */ ClusterIcon.prototype.show = function() { - if (this.div_) { - var pos = this.getPosFromLatLng_(this.center_); - this.div_.style.cssText = this.createCss(pos); - this.div_.style.display = ''; - } - this.visible_ = true; + if (this.div_) { + var pos = this.getPosFromLatLng_(this.center_); + this.div_.style.cssText = this.createCss(pos); + this.div_.style.display = ''; + } + this.visible_ = true; }; @@ -1133,7 +1133,7 @@ ClusterIcon.prototype.show = function() { * Remove the icon from the map */ ClusterIcon.prototype.remove = function() { - this.setMap(null); + this.setMap(null); }; @@ -1142,11 +1142,11 @@ ClusterIcon.prototype.remove = function() { * @ignore */ ClusterIcon.prototype.onRemove = function() { - if (this.div_ && this.div_.parentNode) { - this.hide(); - this.div_.parentNode.removeChild(this.div_); - this.div_ = null; - } + if (this.div_ && this.div_.parentNode) { + this.hide(); + this.div_.parentNode.removeChild(this.div_); + this.div_ = null; + } }; @@ -1158,14 +1158,14 @@ ClusterIcon.prototype.onRemove = function() { * 'index': (number) The style index of the icon. */ ClusterIcon.prototype.setSums = function(sums) { - this.sums_ = sums; - this.text_ = sums.text; - this.index_ = sums.index; - if (this.div_) { - this.div_.innerHTML = sums.text; - } - - this.useStyle(); + this.sums_ = sums; + this.text_ = sums.text; + this.index_ = sums.index; + if (this.div_) { + this.div_.innerHTML = sums.text; + } + + this.useStyle(); }; @@ -1173,16 +1173,16 @@ ClusterIcon.prototype.setSums = function(sums) { * Sets the icon to the the styles. */ ClusterIcon.prototype.useStyle = function() { - var index = Math.max(0, this.sums_.index - 1); - index = Math.min(this.styles_.length - 1, index); - var style = this.styles_[index]; - this.url_ = style['url']; - this.height_ = style['height']; - this.width_ = style['width']; - this.textColor_ = style['textColor']; - this.anchor_ = style['anchor']; - this.textSize_ = style['textSize']; - this.backgroundPosition_ = style['backgroundPosition']; + var index = Math.max(0, this.sums_.index - 1); + index = Math.min(this.styles_.length - 1, index); + var style = this.styles_[index]; + this.url_ = style['url']; + this.height_ = style['height']; + this.width_ = style['width']; + this.textColor_ = style['textColor']; + this.anchor_ = style['anchor']; + this.textSize_ = style['textSize']; + this.backgroundPosition_ = style['backgroundPosition']; }; @@ -1192,7 +1192,7 @@ ClusterIcon.prototype.useStyle = function() { * @param {google.maps.LatLng} center The latlng to set as the center. */ ClusterIcon.prototype.setCenter = function(center) { - this.center_ = center; + this.center_ = center; }; @@ -1203,39 +1203,39 @@ ClusterIcon.prototype.setCenter = function(center) { * @return {string} The css style text. */ ClusterIcon.prototype.createCss = function(pos) { - var style = []; - style.push('background-image:url(' + this.url_ + ');'); - var backgroundPosition = this.backgroundPosition_ ? this.backgroundPosition_ : '0 0'; - style.push('background-position:' + backgroundPosition + ';'); - - if (typeof this.anchor_ === 'object') { - if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 && - this.anchor_[0] < this.height_) { - style.push('height:' + (this.height_ - this.anchor_[0]) + - 'px; padding-top:' + this.anchor_[0] + 'px;'); - } else { - style.push('height:' + this.height_ + 'px; line-height:' + this.height_ + - 'px;'); - } - if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 && - this.anchor_[1] < this.width_) { - style.push('width:' + (this.width_ - this.anchor_[1]) + - 'px; padding-left:' + this.anchor_[1] + 'px;'); - } else { - style.push('width:' + this.width_ + 'px; text-align:center;'); - } - } else { - style.push('height:' + this.height_ + 'px; line-height:' + - this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;'); - } - - var txtColor = this.textColor_ ? this.textColor_ : 'black'; - var txtSize = this.textSize_ ? this.textSize_ : 11; - - style.push('cursor:pointer; top:' + pos.y + 'px; left:' + - pos.x + 'px; color:' + txtColor + '; position:absolute; font-size:' + - txtSize + 'px; font-family:Arial,sans-serif; font-weight:bold'); - return style.join(''); + var style = []; + style.push('background-image:url(' + this.url_ + ');'); + var backgroundPosition = this.backgroundPosition_ ? this.backgroundPosition_ : '0 0'; + style.push('background-position:' + backgroundPosition + ';'); + + if (typeof this.anchor_ === 'object') { + if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 && + this.anchor_[0] < this.height_) { + style.push('height:' + (this.height_ - this.anchor_[0]) + + 'px; padding-top:' + this.anchor_[0] + 'px;'); + } else { + style.push('height:' + this.height_ + 'px; line-height:' + this.height_ + + 'px;'); + } + if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 && + this.anchor_[1] < this.width_) { + style.push('width:' + (this.width_ - this.anchor_[1]) + + 'px; padding-left:' + this.anchor_[1] + 'px;'); + } else { + style.push('width:' + this.width_ + 'px; text-align:center;'); + } + } else { + style.push('height:' + this.height_ + 'px; line-height:' + + this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;'); + } + + var txtColor = this.textColor_ ? this.textColor_ : 'black'; + var txtSize = this.textSize_ ? this.textSize_ : 11; + + style.push('cursor:pointer; top:' + pos.y + 'px; left:' + + pos.x + 'px; color:' + txtColor + '; position:absolute; font-size:' + + txtSize + 'px; font-family:Arial,sans-serif; font-weight:bold'); + return style.join(''); }; @@ -1246,38 +1246,38 @@ window['MarkerClusterer'] = MarkerClusterer; MarkerClusterer.prototype['addMarker'] = MarkerClusterer.prototype.addMarker; MarkerClusterer.prototype['addMarkers'] = MarkerClusterer.prototype.addMarkers; MarkerClusterer.prototype['clearMarkers'] = - MarkerClusterer.prototype.clearMarkers; + MarkerClusterer.prototype.clearMarkers; MarkerClusterer.prototype['fitMapToMarkers'] = - MarkerClusterer.prototype.fitMapToMarkers; + MarkerClusterer.prototype.fitMapToMarkers; MarkerClusterer.prototype['getCalculator'] = - MarkerClusterer.prototype.getCalculator; + MarkerClusterer.prototype.getCalculator; MarkerClusterer.prototype['getGridSize'] = - MarkerClusterer.prototype.getGridSize; + MarkerClusterer.prototype.getGridSize; MarkerClusterer.prototype['getExtendedBounds'] = - MarkerClusterer.prototype.getExtendedBounds; + MarkerClusterer.prototype.getExtendedBounds; MarkerClusterer.prototype['getMap'] = MarkerClusterer.prototype.getMap; MarkerClusterer.prototype['getMarkers'] = MarkerClusterer.prototype.getMarkers; MarkerClusterer.prototype['getMaxZoom'] = MarkerClusterer.prototype.getMaxZoom; MarkerClusterer.prototype['getStyles'] = MarkerClusterer.prototype.getStyles; MarkerClusterer.prototype['getTotalClusters'] = - MarkerClusterer.prototype.getTotalClusters; + MarkerClusterer.prototype.getTotalClusters; MarkerClusterer.prototype['getTotalMarkers'] = - MarkerClusterer.prototype.getTotalMarkers; + MarkerClusterer.prototype.getTotalMarkers; MarkerClusterer.prototype['redraw'] = MarkerClusterer.prototype.redraw; MarkerClusterer.prototype['removeMarker'] = - MarkerClusterer.prototype.removeMarker; + MarkerClusterer.prototype.removeMarker; MarkerClusterer.prototype['removeMarkers'] = - MarkerClusterer.prototype.removeMarkers; + MarkerClusterer.prototype.removeMarkers; MarkerClusterer.prototype['resetViewport'] = - MarkerClusterer.prototype.resetViewport; + MarkerClusterer.prototype.resetViewport; MarkerClusterer.prototype['repaint'] = - MarkerClusterer.prototype.repaint; + MarkerClusterer.prototype.repaint; MarkerClusterer.prototype['setCalculator'] = - MarkerClusterer.prototype.setCalculator; + MarkerClusterer.prototype.setCalculator; MarkerClusterer.prototype['setGridSize'] = - MarkerClusterer.prototype.setGridSize; + MarkerClusterer.prototype.setGridSize; MarkerClusterer.prototype['setMaxZoom'] = - MarkerClusterer.prototype.setMaxZoom; + MarkerClusterer.prototype.setMaxZoom; MarkerClusterer.prototype['onAdd'] = MarkerClusterer.prototype.onAdd; MarkerClusterer.prototype['draw'] = MarkerClusterer.prototype.draw; From b94d4075f883235c051ac94de184e402d70e05a0 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 19 Mar 2015 22:04:07 +0700 Subject: [PATCH 192/354] WIP: Parking --- code/LatLongField.php | 27 ++++++++++++++++++----- code/MapExtension.php | 29 ++++++++++++++++++------- code/PointOfInterest.php | 25 +++++++++++++++++++++ code/PointsOfInterestLayer.php | 14 ++++++++++-- code/PointsOfInterestLayerExtension.php | 9 ++++---- javascript/google/maputil.js | 3 +++ javascript/mapField.js | 2 ++ 7 files changed, 89 insertions(+), 20 deletions(-) diff --git a/code/LatLongField.php b/code/LatLongField.php index c551fdc..16ff028 100755 --- a/code/LatLongField.php +++ b/code/LatLongField.php @@ -16,7 +16,16 @@ class LatLongField extends FieldGroup { protected $buttonText; + private static $ctr = 0; + public function __construct($children = array(), $addressFields = array(), $buttonText = null) { + $id = spl_object_hash($this); + + self::$ctr++; + if (self::$ctr == 2) { + //asdfsda; + } + if ((sizeof($children) < 2) || (!$children[0] instanceof FormField) || (!$children[1] instanceof FormField) @@ -52,6 +61,7 @@ public function hasData() { return true; } + public function FieldHolder($properties = array()) { Requirements::javascript(THIRDPARTY_DIR.'/jquery/jquery.js'); Requirements::javascript(THIRDPARTY_DIR.'/jquery-livequery/jquery.livequery.js'); @@ -79,7 +89,15 @@ public function FieldHolder($properties = array()) { Requirements::css('mappable/css/mapField.css'); $guidePointsJSON = ''; if (isset($this->guidePoints)) { - $guidePointsJSON = json_encode($this->guidePoints); + $latlongps = array(); + foreach ($this->guidePoints as $guidepoint) { + array_push($latlongps, array('latitude' => $guidepoint->Lat, 'longitude' => $guidepoint->Lon)); + + } + + $guidePointsJSON = json_encode($latlongps); + // convert the mappable guidepoints to lat lon + $attributes['data-GuidePoints'] = $guidePointsJSON; // we only wish to change the bounds to those of all the points iff @@ -93,8 +111,6 @@ public function FieldHolder($properties = array()) { $this->FieldList()->push(new LiteralField('locationEditor', $content)); - - $content2 = '
@@ -108,6 +124,7 @@ public function FieldHolder($properties = array()) { return parent::FieldHolder(); } + /* Perform place name search as a means of navigation when editing locations */ @@ -129,8 +146,8 @@ public function geocode(SS_HTTPRequest $r) { position of some other images so that subsequent photo edits do not start with a map centred on the horizon */ - public function setGuidePoints($guidePoints) { - $this->guidePoints = $guidePoints; + public function setGuidePoints($newGuidePoints) { + $this->guidePoints = $newGuidePoints; } } diff --git a/code/MapExtension.php b/code/MapExtension.php index 9437185..a8c5c83 100644 --- a/code/MapExtension.php +++ b/code/MapExtension.php @@ -1,4 +1,4 @@ -removeByName('MapPinEdited'); $fields->addFieldToTab("Root.Location", - new LatLongField(array( - new TextField('Lat', 'Latitude'), - new TextField('Lon', 'Longitude'), - new TextField('ZoomLevel', 'Zoom') - ), - array('Address') - ) + $this->getMapField() ); $fields->addFieldToTab('Root.Location', $uf = new UploadField('MapPinIcon', @@ -173,4 +167,23 @@ public function BasicMap() { return $map; } + + + /** + * Access the map editing field for the purpose of adding guide points + * @return [LatLongField] instance of location editing field + */ + public function getMapField() { + if (!isset($this->mapField)) { + $this->mapField = new LatLongField(array( + new TextField('Lat', 'Latitude'), + new TextField('Lon', 'Longitude'), + new TextField('ZoomLevel', 'Zoom') + ), + array('Address') + ); + } + return $this->mapField; + } + } diff --git a/code/PointOfInterest.php b/code/PointOfInterest.php index 9ccd365..7d30800 100644 --- a/code/PointOfInterest.php +++ b/code/PointOfInterest.php @@ -12,9 +12,34 @@ class PointOfInterest extends DataObject { private static $summary_fields = array('Name'); function getCMSFields() { + error_log('++++++++++++++++++++++'); + error_log(parent::Wibble()); $fields = parent::getCMSFields(); $fields->addFieldToTab('Root.Main', new TextField('Name', 'Name of the item on the map')); + + $layers = $this->PointsOfInterestLayer(); + $ids = array(); + foreach ($layers->getIterator() as $layer) { + array_push($ids, $layer->ID); + } + $csv = implode(',', $ids); + + if ($this->ShowGuideMarkers && strlen($csv) > 0) { + $sql = "ID IN (SELECT DISTINCT PointOfInterestID from "; + $sql .= "PointsOfInterestLayer_PointsOfInterest WHERE PointsOfInterestLayerID "; + $sql .= "IN ($csv))"; + + $pois = PointOfInterest::get()->where($sql); + $this->owner->getMapField()->setGuidePoints($pois); + } + return $fields; } + /* + FIXME - possible to populate layer id when adding a new record? + public function populateDefaults() { + parent::populateDefaults(); + } + */ } diff --git a/code/PointsOfInterestLayer.php b/code/PointsOfInterestLayer.php index 504ccfa..f22284d 100644 --- a/code/PointsOfInterestLayer.php +++ b/code/PointsOfInterestLayer.php @@ -1,6 +1,9 @@ 'Varchar'); + private static $db = array( + 'Name' => 'Varchar', + 'ShowGuideMarkers' => 'Boolean' + ); private static $many_many = array('PointsOfInterest' => 'PointOfInterest'); @@ -15,8 +18,15 @@ function getCMSFields() { $uf = new UploadField('DefaultIcon', _t('PointsOfInterest.ICON', 'Default Icon')) - ); + ); + $fields->addFieldToTab('Root.Main', new CheckboxField('ShowGuideMarkers', + 'Show grey guide markers of others points in this layer')); $uf->setFolderName('mapicons'); + return $fields; } + + function Wibble() { + return 'wibble'; + } } diff --git a/code/PointsOfInterestLayerExtension.php b/code/PointsOfInterestLayerExtension.php index c37d07b..b43b1ca 100644 --- a/code/PointsOfInterestLayerExtension.php +++ b/code/PointsOfInterestLayerExtension.php @@ -3,13 +3,13 @@ class PointsOfInterestLayerExtension extends DataExtension { static $many_many = array( - 'PointsOfInterestLayers' => 'PointsOfInterestLayer' + 'PointsOfInterestLayers' => 'PointsOfInterestLayer' ); static $belongs_many_many_extraFields = array( - 'PointsOfInterestLayers' => array( - 'SortOrder' => "Int" - ) + 'PointsOfInterestLayers' => array( + 'SortOrder' => "Int" + ) ); @@ -26,5 +26,4 @@ public function updateCMSFields(FieldList $fields) { ); $fields->addFieldToTab("Root.MapLayers", $gridField2); } - } diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index 78aa741..2ad69cb 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -60,6 +60,7 @@ function getCurrentLat() { return current_lat; } + /** * Get the current longitude * @return {float} The current longitude @@ -90,6 +91,7 @@ function addAllMarkers(map, markers, useClusterer, enableWindowZoom, defaultHide return allmarkers; } + /** * Add lines to a Google map * @param {googleMap} map Google map instance @@ -111,6 +113,7 @@ function addLines(map, lines) { } } + /** * Add one or more (max of 25) KML files to a Google map * @param {GoogleMap} map A Google Map instance diff --git a/javascript/mapField.js b/javascript/mapField.js index 40d8153..9fe91e5 100755 --- a/javascript/mapField.js +++ b/javascript/mapField.js @@ -28,6 +28,8 @@ function initMap() { var lonField = $('input[name=' + gm.attr('data-lonfieldname') + ']'); var zoomField = $('input[name=' + gm.attr('data-zoomfieldname') + ']'); var guidePointsAttr = gm.attr('data-GuidePoints'); + console.log('guidepoints'); + console.log(guidePointsAttr); // if we have emtpy initial values, set them appropriately, // otherwise googlemaps codegoes into an infinite tailspin From ef6224c54365c3b304f347cf6ca6bac5dd5b299d Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 18 Mar 2015 23:05:15 +0700 Subject: [PATCH 193/354] WIP: Learning Grunt WIP: Working now, paths need fixed WIP: Grunt file now looking good FIX: Removed concatenation of JS, not required WIP, time to switch branches FIX: Seems like Grunt fails to fail with erroneous filenames, fixed full screen js filename FIX: Remove debug ENHANCEMENT: First working cut of reduced size JS file for map rendering --- Gruntfile.js | 31 +++++++++++++++++++++++++ _config/maps.yml | 3 ++- code/MapAPI.php | 3 ++- code/MapExtension.php | 8 +++++++ css/mapField.min.css | 1 + javascript/google/mappablegoogle.min.js | 1 + javascript/google/maputil.js | 2 ++ javascript/mapField.min.js | 1 + package.json | 14 +++++++++++ templates/Includes/GoogleJavaScript.ss | 4 ++++ 10 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 Gruntfile.js create mode 100644 css/mapField.min.css create mode 100644 javascript/google/mappablegoogle.min.js create mode 100644 javascript/mapField.min.js create mode 100644 package.json diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..447169c --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,31 @@ +module.exports = function (grunt) { + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + cssmin: { + css: { + src: 'css/mapField.css', + dest: 'css/mapField.min.css' + } + }, + uglify: { + js: { + files : { + 'javascript/google/mappablegoogle.min.js' : [ + 'javascript/google/FullScreenControl.js', + 'javascript/google/markerclusterer.js', + 'javascript/google/maputil.js' + ], + + 'javascript/mapField.min.js' : [ + 'javascript/mapField.js' + ] + + } + }, + }, + }); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-cssmin'); + grunt.registerTask('default', ['cssmin:css', 'uglify:js']); +}; diff --git a/_config/maps.yml b/_config/maps.yml index e04d5e9..bf502b1 100644 --- a/_config/maps.yml +++ b/_config/maps.yml @@ -3,4 +3,5 @@ Name: mappable After: 'framework/*','cms/*' --- Mappable: - allow_full_screen: true \ No newline at end of file + allow_full_screen: true + use_compressed_assets: true diff --git a/code/MapAPI.php b/code/MapAPI.php index 49ed9cd..d2f8ce4 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -833,7 +833,8 @@ public function generate() { 'ClustererLibraryPath' => $this->clustererLibraryPath, 'Lines' => $linesJson, 'KmlFiles' => $kmlJson, - 'AllowFullScreen' => $this->allowFullScreen + 'AllowFullScreen' => $this->allowFullScreen, + 'UseCompressedAssets' => Config::inst()->get('Mappable', 'use_compressed_assets') ) ); diff --git a/code/MapExtension.php b/code/MapExtension.php index 9437185..f7e2f35 100644 --- a/code/MapExtension.php +++ b/code/MapExtension.php @@ -173,4 +173,12 @@ public function BasicMap() { return $map; } + + /** + * Template helper, used to decide whether or not to use compressed assets + */ + public function UseCompressedAssets() { + error_log('***** USE COMPRESSED ASSETS ******'); + return Config::inst()->get('Mappable', 'use_compressed_assets'); + } } diff --git a/css/mapField.min.css b/css/mapField.min.css new file mode 100644 index 0000000..1fe477d --- /dev/null +++ b/css/mapField.min.css @@ -0,0 +1 @@ +.geocodedSearchResults li{cursor:pointer;font-weight:700;font-size:120%;padding-top:5px;padding-bottom:5px}.editableMap{height:300px;float:none}.editableMapWrapper{display:inline-block;width:900px;float:none;margin-left:auto;margin-right:auto}.fieldholder-small{display:none} \ No newline at end of file diff --git a/javascript/google/mappablegoogle.min.js b/javascript/google/mappablegoogle.min.js new file mode 100644 index 0000000..beabad0 --- /dev/null +++ b/javascript/google/mappablegoogle.min.js @@ -0,0 +1 @@ +function FullScreenControl(a,b,c){void 0===b&&(b=null),void 0===c&&(c=null),null==b&&(b="Full screen"),null==c&&(c="Exit full screen");var d=document.createElement("div");d.className="fullScreen",d.index=1,d.style.padding="5px";var e=document.createElement("div");e.style.backgroundColor="white",e.style.borderStyle="solid",e.style.borderWidth="1px",e.style.borderColor="#717b87",e.style.cursor="pointer",e.style.textAlign="center",e.style.boxShadow="rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px",d.appendChild(e);var f=document.createElement("div");f.style.fontFamily="Roboto,Arial,sans-serif",f.style.fontSize="11px",f.style.fontWeight="400",f.style.paddingTop="1px",f.style.paddingBottom="1px",f.style.paddingLeft="6px",f.style.paddingRight="6px",f.innerHTML=""+b+"",e.appendChild(f);var g=document.getElementsByTagName("head")[0],h=document.createElement("style");h.setAttribute("type","text/css"),h.setAttribute("media","print");var i=".fullScreen { display: none;}",j=document.createTextNode(i);try{h.appendChild(j)}catch(k){h.styleSheet.cssText=i}g.appendChild(h);var l,m=!1,n=a.getDiv(),o=n.style;n.runtimeStyle&&(o=n.runtimeStyle);var p=o.position,q=o.width,r=o.height;""===q&&(q=n.style.width),""===r&&(r=n.style.height);var s=o.top,t=o.left,u=o.zIndex,v=document.body.style;document.body.runtimeStyle&&(v=document.body.runtimeStyle);var w=v.overflow;return d.goFullScreen=function(){var b=a.getCenter();n.style.position="fixed",n.style.width="100%",n.style.height="100%",n.style.top="0",n.style.left="0",n.style.zIndex="100",document.body.style.overflow="hidden",f.innerHTML=""+c+"",m=!0,google.maps.event.trigger(a,"resize"),a.setCenter(b),l=setInterval(function(){"fixed"!==n.style.position&&(n.style.position="fixed",google.maps.event.trigger(a,"resize"))},100)},d.exitFullScreen=function(){var c=a.getCenter();n.style.position=""===p?"relative":p,n.style.width=q,n.style.height=r,n.style.top=s,n.style.left=t,n.style.zIndex=u,document.body.style.overflow=w,f.innerHTML=""+b+"",m=!1,google.maps.event.trigger(a,"resize"),a.setCenter(c),clearInterval(l)},google.maps.event.addDomListener(e,"click",function(){m?d.exitFullScreen():d.goFullScreen()}),document.onkeydown=function(a){a=a||window.event,27==a.keyCode&&m&&d.exitFullScreen()},d}function MarkerClusterer(a,b,c){this.extend(MarkerClusterer,google.maps.OverlayView),this.map_=a,this.markers_=[],this.clusters_=[],this.sizes=[53,56,66,78,90],this.styles_=[],this.ready_=!1;var d=c||{};this.gridSize_=d.gridSize||60,this.minClusterSize_=d.minimumClusterSize||2,this.maxZoom_=d.maxZoom||null,this.styles_=d.styles||[],this.imagePath_=d.imagePath||this.MARKER_CLUSTER_IMAGE_PATH_,this.imageExtension_=d.imageExtension||this.MARKER_CLUSTER_IMAGE_EXTENSION_,this.zoomOnClick_=!0,void 0!=d.zoomOnClick&&(this.zoomOnClick_=d.zoomOnClick),this.averageCenter_=!1,void 0!=d.averageCenter&&(this.averageCenter_=d.averageCenter),this.setupStyles_(),this.setMap(a),this.prevZoom_=this.map_.getZoom();var e=this;google.maps.event.addListener(this.map_,"zoom_changed",function(){var a=e.map_.getZoom();e.prevZoom_!=a&&(e.prevZoom_=a,e.resetViewport())}),google.maps.event.addListener(this.map_,"idle",function(){e.redraw()}),b&&b.length&&this.addMarkers(b,!1)}function Cluster(a){this.markerClusterer_=a,this.map_=a.getMap(),this.gridSize_=a.getGridSize(),this.minClusterSize_=a.getMinClusterSize(),this.averageCenter_=a.isAverageCenter(),this.center_=null,this.markers_=[],this.bounds_=null,this.clusterIcon_=new ClusterIcon(this,a.getStyles(),a.getGridSize())}function ClusterIcon(a,b,c){a.getMarkerClusterer().extend(ClusterIcon,google.maps.OverlayView),this.styles_=b,this.padding_=c||0,this.cluster_=a,this.center_=null,this.map_=a.getMap(),this.div_=null,this.sums_=null,this.visible_=!1,this.setMap(this.map_)}function createMarker(a,b,c,d,e,f,g,h,i){mapId=a.getDiv().getAttribute("id");var j=new google.maps.Marker;if(j.setPosition(new google.maps.LatLng(b,c)),j.mycategory=e,f&&""!==f){var k=new google.maps.MarkerImage(f);j.setIcon(k)}return g||j.setMap(a),google.maps.event.addListener(j,"click",function(){h&&a.setCenter(new google.maps.LatLng(b,c),12);var e=infoWindows[mapId];e.setContent(d),e.open(a,this)}),gmarkers[mapId].push(j),i&&j.hide(),j}function getCurrentLat(){return current_lat}function getCurrentLng(){return current_lng}function addAllMarkers(a,b,c,d,e){for(var f=[],g=0;g=a;a++){var b=mappableMaps["google_map_"+a],c=new google.maps.Map(document.getElementById(b.googleMapID));if(geocoder=new google.maps.Geocoder,b.allowFullScreen&&c.controls[google.maps.ControlPosition.TOP_RIGHT].push(FullScreenControl(c,"Full Screen","Original Size")),b.enableAutomaticCenterZoom){e=b.centreCoordinates,c.setCenter(new google.maps.LatLng(e.lat,e.lng));var d=new google.maps.LatLngBounds(new google.maps.LatLng(b.minLat,b.minLng),new google.maps.LatLng(b.maxLat,b.maxLng));c.fitBounds(d),c.setZoom(b.zoom)}else{var e=b.centreCoordinates;c.setCenter(new google.maps.LatLng(e.lat,e.lng)),c.setZoom(b.zoom)}c.setMapTypeId(b.mapType?b.mapType:google.maps.MapTypeId.ROADMAP);var f=addAllMarkers(c,b.markers,b.useClusterer,b.enableAutomaticCenterZoom,b.defaultHideMarker);if(b.useClusterer){var g={gridSize:50,maxZoom:17};new MarkerClusterer(c,f,g)}addLines(c,b.lines),addKmlFiles(c,b.kmlFiles)}}MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_="http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/m",MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_="png",MarkerClusterer.prototype.extend=function(a,b){return function(a){for(var b in a.prototype)this.prototype[b]=a.prototype[b];return this}.apply(a,[b])},MarkerClusterer.prototype.onAdd=function(){this.setReady_(!0)},MarkerClusterer.prototype.draw=function(){},MarkerClusterer.prototype.setupStyles_=function(){if(!this.styles_.length)for(var a,b=0;a=this.sizes[b];b++)this.styles_.push({url:this.imagePath_+(b+1)+"."+this.imageExtension_,height:a,width:a})},MarkerClusterer.prototype.fitMapToMarkers=function(){for(var a,b=this.getMarkers(),c=new google.maps.LatLngBounds,d=0;a=b[d];d++)c.extend(a.getPosition());this.map_.fitBounds(c)},MarkerClusterer.prototype.setStyles=function(a){this.styles_=a},MarkerClusterer.prototype.getStyles=function(){return this.styles_},MarkerClusterer.prototype.isZoomOnClick=function(){return this.zoomOnClick_},MarkerClusterer.prototype.isAverageCenter=function(){return this.averageCenter_},MarkerClusterer.prototype.getMarkers=function(){return this.markers_},MarkerClusterer.prototype.getTotalMarkers=function(){return this.markers_.length},MarkerClusterer.prototype.setMaxZoom=function(a){this.maxZoom_=a},MarkerClusterer.prototype.getMaxZoom=function(){return this.maxZoom_},MarkerClusterer.prototype.calculator_=function(a,b){for(var c=0,d=a.length,e=d;0!==e;)e=parseInt(e/10,10),c++;return c=Math.min(c,b),{text:d,index:c}},MarkerClusterer.prototype.setCalculator=function(a){this.calculator_=a},MarkerClusterer.prototype.getCalculator=function(){return this.calculator_},MarkerClusterer.prototype.addMarkers=function(a,b){for(var c,d=0;c=a[d];d++)this.pushMarkerTo_(c);b||this.redraw()},MarkerClusterer.prototype.pushMarkerTo_=function(a){if(a.isAdded=!1,a.draggable){var b=this;google.maps.event.addListener(a,"dragend",function(){a.isAdded=!1,b.repaint()})}this.markers_.push(a)},MarkerClusterer.prototype.addMarker=function(a,b){this.pushMarkerTo_(a),b||this.redraw()},MarkerClusterer.prototype.removeMarker_=function(a){var b=-1;if(this.markers_.indexOf)b=this.markers_.indexOf(a);else for(var c,d=0;c=this.markers_[d];d++)if(c==a){b=d;break}return-1==b?!1:(a.setMap(null),this.markers_.splice(b,1),!0)},MarkerClusterer.prototype.removeMarker=function(a,b){var c=this.removeMarker_(a);return!b&&c?(this.resetViewport(),this.redraw(),!0):!1},MarkerClusterer.prototype.removeMarkers=function(a,b){for(var c,d=!1,e=0;c=a[e];e++){var f=this.removeMarker_(c);d=d||f}return!b&&d?(this.resetViewport(),this.redraw(),!0):void 0},MarkerClusterer.prototype.setReady_=function(a){this.ready_||(this.ready_=a,this.createClusters_())},MarkerClusterer.prototype.getTotalClusters=function(){return this.clusters_.length},MarkerClusterer.prototype.getMap=function(){return this.map_},MarkerClusterer.prototype.setMap=function(a){this.map_=a},MarkerClusterer.prototype.getGridSize=function(){return this.gridSize_},MarkerClusterer.prototype.setGridSize=function(a){this.gridSize_=a},MarkerClusterer.prototype.getMinClusterSize=function(){return this.minClusterSize_},MarkerClusterer.prototype.setMinClusterSize=function(a){this.minClusterSize_=a},MarkerClusterer.prototype.getExtendedBounds=function(a){var b=this.getProjection(),c=new google.maps.LatLng(a.getNorthEast().lat(),a.getNorthEast().lng()),d=new google.maps.LatLng(a.getSouthWest().lat(),a.getSouthWest().lng()),e=b.fromLatLngToDivPixel(c);e.x+=this.gridSize_,e.y-=this.gridSize_;var f=b.fromLatLngToDivPixel(d);f.x-=this.gridSize_,f.y+=this.gridSize_;var g=b.fromDivPixelToLatLng(e),h=b.fromDivPixelToLatLng(f);return a.extend(g),a.extend(h),a},MarkerClusterer.prototype.isMarkerInBounds_=function(a,b){return b.contains(a.getPosition())},MarkerClusterer.prototype.clearMarkers=function(){this.resetViewport(!0),this.markers_=[]},MarkerClusterer.prototype.resetViewport=function(a){for(var b,c=0;b=this.clusters_[c];c++)b.remove();for(var d,c=0;d=this.markers_[c];c++)d.isAdded=!1,a&&d.setMap(null);this.clusters_=[]},MarkerClusterer.prototype.repaint=function(){var a=this.clusters_.slice();this.clusters_.length=0,this.resetViewport(),this.redraw(),window.setTimeout(function(){for(var b,c=0;b=a[c];c++)b.remove()},0)},MarkerClusterer.prototype.redraw=function(){this.createClusters_()},MarkerClusterer.prototype.distanceBetweenPoints_=function(a,b){if(!a||!b)return 0;var c=6371,d=(b.lat()-a.lat())*Math.PI/180,e=(b.lng()-a.lng())*Math.PI/180,f=Math.sin(d/2)*Math.sin(d/2)+Math.cos(a.lat()*Math.PI/180)*Math.cos(b.lat()*Math.PI/180)*Math.sin(e/2)*Math.sin(e/2),g=2*Math.atan2(Math.sqrt(f),Math.sqrt(1-f)),h=c*g;return h},MarkerClusterer.prototype.addToClosestCluster_=function(a){for(var b,c=4e4,d=null,e=(a.getPosition(),0);b=this.clusters_[e];e++){var f=b.getCenter();if(f){var g=this.distanceBetweenPoints_(f,a.getPosition());c>g&&(c=g,d=b)}}if(d&&d.isMarkerInClusterBounds(a))d.addMarker(a);else{var b=new Cluster(this);b.addMarker(a),this.clusters_.push(b)}},MarkerClusterer.prototype.createClusters_=function(){if(this.ready_)for(var a,b=new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(),this.map_.getBounds().getNorthEast()),c=this.getExtendedBounds(b),d=0;a=this.markers_[d];d++)!a.isAdded&&this.isMarkerInBounds_(a,c)&&this.addToClosestCluster_(a)},Cluster.prototype.isMarkerAlreadyAdded=function(a){if(this.markers_.indexOf)return-1!=this.markers_.indexOf(a);for(var b,c=0;b=this.markers_[c];c++)if(b==a)return!0;return!1},Cluster.prototype.addMarker=function(a){if(this.isMarkerAlreadyAdded(a))return!1;if(this.center_){if(this.averageCenter_){var b=this.markers_.length+1,c=(this.center_.lat()*(b-1)+a.getPosition().lat())/b,d=(this.center_.lng()*(b-1)+a.getPosition().lng())/b;this.center_=new google.maps.LatLng(c,d),this.calculateBounds_()}}else this.center_=a.getPosition(),this.calculateBounds_();a.isAdded=!0,this.markers_.push(a);var e=this.markers_.length;if(ef;f++)this.markers_[f].setMap(null);return e>=this.minClusterSize_&&a.setMap(null),this.updateIcon(),!0},Cluster.prototype.getMarkerClusterer=function(){return this.markerClusterer_},Cluster.prototype.getBounds=function(){for(var a,b=new google.maps.LatLngBounds(this.center_,this.center_),c=this.getMarkers(),d=0;a=c[d];d++)b.extend(a.getPosition());return b},Cluster.prototype.remove=function(){this.clusterIcon_.remove(),this.markers_.length=0,delete this.markers_},Cluster.prototype.getSize=function(){return this.markers_.length},Cluster.prototype.getMarkers=function(){return this.markers_},Cluster.prototype.getCenter=function(){return this.center_},Cluster.prototype.calculateBounds_=function(){var a=new google.maps.LatLngBounds(this.center_,this.center_);this.bounds_=this.markerClusterer_.getExtendedBounds(a)},Cluster.prototype.isMarkerInClusterBounds=function(a){return this.bounds_.contains(a.getPosition())},Cluster.prototype.getMap=function(){return this.map_},Cluster.prototype.updateIcon=function(){var a=this.map_.getZoom(),b=this.markerClusterer_.getMaxZoom();if(b&&a>b)for(var c,d=0;c=this.markers_[d];d++)c.setMap(this.map_);else{if(this.markers_.length0&&this.anchor_[0]0&&this.anchor_[1]=0;k--){var l=h[k].latitude,m=h[k].longitude;addGuideMarker(l,m);var n=new google.maps.LatLng(l,m);i+=parseFloat(l),j+=parseFloat(m),bounds.extend(n)}if(0===d.val()&&0===e.val()){var o=h.length;new google.maps.LatLng(i/o,j/o)}map.fitBounds(bounds)}d.val()&&e.val()&&(marker=null,setMarker(a.center,!0)),google.maps.event.addListener(map,"rightclick",function(a){var b=a.latLng.lat(),c=a.latLng.lng();d.val(b),e.val(c),setMarker(a.latLng,!1),statusMessage("Location changed to "+b+","+c)}),google.maps.event.addListener(map,"zoom_changed",function(){f.length&&f.val(map.getZoom())}),google.maps.event.trigger(map,"resize"),map.setZoom(map.getZoom()),b(".ui-tabs-anchor").click(function(){google.maps.event.trigger(map,"resize");var a=b("#GoogleMap"),c=a.attr("data-usemapbounds");c?map.fitBounds(bounds):map.setCenter(marker.getPosition())})}(jQuery)}function addGuideMarker(a,b){var c=new google.maps.LatLng(a,b),d="CCCCCC",e=new google.maps.MarkerImage("//chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|"+d,new google.maps.Size(21,34),new google.maps.Point(0,0),new google.maps.Point(10,34)),f=new google.maps.MarkerImage("//chart.apis.google.com/chart?chst=d_map_pin_shadow",new google.maps.Size(40,37),new google.maps.Point(0,0),new google.maps.Point(12,35)),g=new google.maps.Marker({position:c,title:"Marker",icon:e,shadow:f});g.setMap(map)}function setMarker(a,b){null!==marker?marker.setPosition(a):(marker=new google.maps.Marker({position:a,title:"Position",draggable:!0}),marker.setMap(map),google.maps.event.addListener(marker,"dragend",setCoordByMarker)),b&&map.setCenter(a)}function setCoordByMarker(a){!function(b){var c=b("#GoogleMap"),d=b("input[name="+c.attr("data-latfieldname")+"]"),e=b("input[name="+c.attr("data-lonfieldname")+"]"),f=b("input[name="+c.attr("data-zoomfieldname")+"]"),g=a.latLng.lat(),h=a.latLng.lng();d.val(g),e.val(h),setMarker(a.latLng,!0),statusMessage("Location changed to "+g+","+h),f.length&&f.val(map.getZoom()),map.setCenter(a.latLng)}(jQuery)}function searchForAddress(a){!function(b){{var c=new google.maps.Geocoder;new google.maps.ElevationService}c&&(statusMessage("Searching for:"+a),c.geocode({address:a},function(a,c){if(c==google.maps.GeocoderStatus.OK){var d=a.length;d>0?statusMessage("Places found"):0===d&&errorMessage("No places found");var e='
    ';b.each(a,function(a,c){var d=[];b.each(c.address_components,function(a,b){d.push(b.long_name)}),e=e+'
  • '+d+"
  • "}),e+="
",b("#mapSearchResults").html(e)}else errorMessage("Unable to find any geocoded results")}))}(jQuery)}function initLivequery(){!function(a){a("input[name=action_GetCoords]").livequery("click",function(){var b=a("#Form_EditForm_Location").val();return setCoordByAddress(b),!1}),a("#searchLocationButton").livequery("click",function(){var b=a("#location_search").val();return searchForAddress(b),!1}),a(".geocodedSearchResults li").livequery("click",function(){var b=a(this),c=b.attr("lat"),d=b.attr("lon"),e=b.html(),f=new google.maps.LatLng(c,d);statusMessage("Setting map to "+e),a(".geocodedSearchResults").html(""),a("#Form_EditForm_Latitude").val(c),a("#Form_EditForm_Longitude").val(d);{var g=a("#GoogleMap"),h=a("input[name="+g.attr("data-latfieldname")+"]"),i=a("input[name="+g.attr("data-lonfieldname")+"]");a("input[name="+g.attr("data-zoomfieldname")+"]")}return h.val(c),i.val(d),map.setZoom(12),setMarker(f,!0),!1}),a("#GoogleMap").livequery(function(){initMap()})}(jQuery)}var marker,bounds;!function(a){function b(){var a=document.createElement("script");a.type="text/javascript",a.src="//maps.googleapis.com/maps/api/js?sensor=false&callback=gmloaded",document.body.appendChild(a)}a(document).ready(function(){b()})}(jQuery); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..b0775cc --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "mappable", + "version": "0.1.0", + "devDependencies": { + "grunt": "~0.4.5", + "grunt-contrib-jshint": "~0.11.0", + "grunt-contrib-nodeunit": "~0.4.1", + "grunt-contrib-uglify": "~0.8.0", + "grunt-contrib-concat": "~0.5.1", + "grunt-contrib-cssmin": "~0.12.2", + "grunt-contrib-watch": "~0.6.1", + "grunt-contrib-clean": "~0.6.0" + } +} diff --git a/templates/Includes/GoogleJavaScript.ss b/templates/Includes/GoogleJavaScript.ss index 497e1f0..ecd4375 100644 --- a/templates/Includes/GoogleJavaScript.ss +++ b/templates/Includes/GoogleJavaScript.ss @@ -2,9 +2,13 @@ <% if DelayLoadMapFunction %> <% else %> +<% if $UseCompressedAssets %> + +<% else %> +<% end_if %> <% end_if %> From 3f4533c13d40ae7bea414f2f37cedf0618666d81 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 21 Mar 2015 23:44:25 +0700 Subject: [PATCH 194/354] FIX: Remove rogue 1 character --- code/MapExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/MapExtension.php b/code/MapExtension.php index 5f8601f..7ec487c 100644 --- a/code/MapExtension.php +++ b/code/MapExtension.php @@ -1,4 +1,4 @@ -1 Date: Sat, 21 Mar 2015 14:55:09 +0700 Subject: [PATCH 195/354] FIX: Avoid committing node modules if installing as current user --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules From 3bd0cd7c6c8f891b15630c1b2c05ff5fe5760339 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 21 Mar 2015 21:14:53 +0700 Subject: [PATCH 196/354] FIX: Use requirements to load the JavaScript files --- templates/Includes/GoogleJavaScript.ss | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/templates/Includes/GoogleJavaScript.ss b/templates/Includes/GoogleJavaScript.ss index ecd4375..81c6522 100644 --- a/templates/Includes/GoogleJavaScript.ss +++ b/templates/Includes/GoogleJavaScript.ss @@ -1,13 +1,14 @@ <% if DownloadJS %> <% if DelayLoadMapFunction %> <% else %> - +<% require javascript("//maps.google.com/maps/api/js?sensor=false&hl=$Lang") %> + <% if $UseCompressedAssets %> - +<% require javascript("mappable/javascript/google/mappablegoogle.min.js") %> <% else %> - - - +<% require javascript("mappable/javascript/google/FullScreenControl.js") %> +<% require javascript("mappable/javascript/google/markerclusterer.js") %> +<% require javascript("mappable/javascript/google/maputil.js") %> <% end_if %> From 7c20be0aa70114dbb7c0e070e27bf8d7c26dd63d Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 21 Mar 2015 21:16:43 +0700 Subject: [PATCH 197/354] FIX: Separate JS and HTML for Google map rendering, in order to ensure that the JavaScript can be deferred --- javascript/google/{MapGoogle.ss => MapGoogleJS.ss} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename javascript/google/{MapGoogle.ss => MapGoogleJS.ss} (100%) diff --git a/javascript/google/MapGoogle.ss b/javascript/google/MapGoogleJS.ss similarity index 100% rename from javascript/google/MapGoogle.ss rename to javascript/google/MapGoogleJS.ss From ae968df1976569d1a9262b4d6f0c7391d5593b70 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 21 Mar 2015 23:44:07 +0700 Subject: [PATCH 198/354] FIX: Ensure JavaScript is loaded using SS Requirements, and appears at end of the page --- code/MapAPI.php | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index d2f8ce4..6867d54 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -838,16 +838,41 @@ public function generate() { ) ); - $this->content = $this->processTemplate('Map', $vars); + // HTML component of the map + $this->content = $this->processTemplateHTML('Map', $vars); + + $javascript = $this->processTemplateJS('Map', $vars); + + Requirements::customScript(<<renderWith($templateName.$this->mappingService.'JS'); + return $result; + } - function processTemplate($templateName, $templateVariables = null ) { + function processTemplateHTML($templateName, $templateVariables = null ) { if (!$templateVariables) { $templateVariables = new ArrayList(); } - $result = $templateVariables->renderWith($templateName.$this->mappingService); + $result = $templateVariables->renderWith($templateName.$this->mappingService.'HTML'); return $result; } } From f427cefbf2c017a744f6a8edb7d9eff69f0c2c4f Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 21 Mar 2015 23:44:40 +0700 Subject: [PATCH 199/354] MINOR: Whitespace --- code/MappableData.php | 1 + 1 file changed, 1 insertion(+) diff --git a/code/MappableData.php b/code/MappableData.php index d99463e..41d35d0 100644 --- a/code/MappableData.php +++ b/code/MappableData.php @@ -28,6 +28,7 @@ public function getRenderableMap($width = null, $height = null, $zoom = 9) { return $gmap; } + public function StaticMap($width = null, $height = null) { $w = $width ? $width : MapUtil::$map_width; $h = $height ? $height : MapUtil::$map_height; From dd20ee6464fc1b3c90797c76bd1bb10fba679ddd Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 21 Mar 2015 23:44:56 +0700 Subject: [PATCH 200/354] MINOR: Remove error log statements --- code/PointOfInterest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/PointOfInterest.php b/code/PointOfInterest.php index 7d30800..7f368f9 100644 --- a/code/PointOfInterest.php +++ b/code/PointOfInterest.php @@ -12,8 +12,6 @@ class PointOfInterest extends DataObject { private static $summary_fields = array('Name'); function getCMSFields() { - error_log('++++++++++++++++++++++'); - error_log(parent::Wibble()); $fields = parent::getCMSFields(); $fields->addFieldToTab('Root.Main', new TextField('Name', 'Name of the item on the map')); From 3041d5a8b35a360de33e030cb4ab164cd8ab27ae Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 21 Mar 2015 23:45:15 +0700 Subject: [PATCH 201/354] FIX: Keep this file as only JS --- javascript/google/MapGoogleJS.ss | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/javascript/google/MapGoogleJS.ss b/javascript/google/MapGoogleJS.ss index 94fddb4..6855605 100644 --- a/javascript/google/MapGoogleJS.ss +++ b/javascript/google/MapGoogleJS.ss @@ -1,6 +1,4 @@ -<% include GoogleJavaScript %> - -
style="width:{$Width}; height: {$Height};" -<% end_if %><% if AdditionalCssClasses %> class="$AdditionalCssClasses"<% end_if %>> -
From a7801760563d24fb775e88966cb65cb09ec87b03 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 21 Mar 2015 23:45:30 +0700 Subject: [PATCH 202/354] FIX: HTML part of Google map only --- javascript/google/MapGoogleHTML.ss | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 javascript/google/MapGoogleHTML.ss diff --git a/javascript/google/MapGoogleHTML.ss b/javascript/google/MapGoogleHTML.ss new file mode 100644 index 0000000..78a869f --- /dev/null +++ b/javascript/google/MapGoogleHTML.ss @@ -0,0 +1,4 @@ +<% include GoogleJavaScript %> +
style="width:{$Width}; height: {$Height};" +<% end_if %><% if AdditionalCssClasses %> class="$AdditionalCssClasses"<% end_if %>> +
From ebe566d8d8bf6f98902eccc3f73bf33c4c2c4c4a Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 21 Mar 2015 23:45:47 +0700 Subject: [PATCH 203/354] FIX: Remove debug --- javascript/google/maputil.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index 67c5626..2ad69cb 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -223,5 +223,3 @@ function loadedGoogleMapsAPI() { addKmlFiles(map, map_info.kmlFiles); } } - -alert('maputil'); From 9145bf0965f809199bf2c66378f293eac2ce6e5f Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 21 Mar 2015 23:45:59 +0700 Subject: [PATCH 204/354] FIX: Remove debug --- javascript/mapField.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/javascript/mapField.js b/javascript/mapField.js index 9fe91e5..40d8153 100755 --- a/javascript/mapField.js +++ b/javascript/mapField.js @@ -28,8 +28,6 @@ function initMap() { var lonField = $('input[name=' + gm.attr('data-lonfieldname') + ']'); var zoomField = $('input[name=' + gm.attr('data-zoomfieldname') + ']'); var guidePointsAttr = gm.attr('data-GuidePoints'); - console.log('guidepoints'); - console.log(guidePointsAttr); // if we have emtpy initial values, set them appropriately, // otherwise googlemaps codegoes into an infinite tailspin From 67391d240e5896d1ffee929487e61ad8bf1c3469 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 21 Mar 2015 23:46:13 +0700 Subject: [PATCH 205/354] MINOR: Whitespace --- templates/Includes/GoogleJavaScript.ss | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/templates/Includes/GoogleJavaScript.ss b/templates/Includes/GoogleJavaScript.ss index 81c6522..c303ec9 100644 --- a/templates/Includes/GoogleJavaScript.ss +++ b/templates/Includes/GoogleJavaScript.ss @@ -10,11 +10,9 @@ <% require javascript("mappable/javascript/google/markerclusterer.js") %> <% require javascript("mappable/javascript/google/maputil.js") %> <% end_if %> - - <% end_if %> + From 94248f8691187a1a8a707fa73dd81a64e820d965 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 21 Mar 2015 23:46:27 +0700 Subject: [PATCH 206/354] MINOR: Whitespace --- templates/Includes/GoogleStreetView.ss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/templates/Includes/GoogleStreetView.ss b/templates/Includes/GoogleStreetView.ss index 84c9e23..5cf750a 100644 --- a/templates/Includes/GoogleStreetView.ss +++ b/templates/Includes/GoogleStreetView.ss @@ -15,11 +15,10 @@ function initialize_{$DomID}() { zoom: $Zoom }; var domNode = document.getElementById('$DomID'); - console.log(domNode); var myPano = new google.maps.StreetViewPanorama( domNode, panoramaOptions); myPano.setVisible(true); } google.maps.event.addDomListener(window, 'load', initialize_{$DomID}); - \ No newline at end of file + From 1b8ccfd6e699b52cfce208dec1684fbf8762924c Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 21 Mar 2015 23:46:40 +0700 Subject: [PATCH 207/354] FIX: Remove debug --- templates/Layout/POIMapPage.ss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/Layout/POIMapPage.ss b/templates/Layout/POIMapPage.ss index 7b0e977..33bf0b1 100644 --- a/templates/Layout/POIMapPage.ss +++ b/templates/Layout/POIMapPage.ss @@ -2,9 +2,9 @@

$Title

- $BasicMap << MAP + $BasicMap
$Content
$Form $PageComments -
\ No newline at end of file +
From 594160a09c70e766be095936452ca202cf68f344 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 24 Mar 2015 11:52:58 +0700 Subject: [PATCH 208/354] MINOR: Revert default for use of compressed assets to false --- _config/maps.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config/maps.yml b/_config/maps.yml index bf502b1..4e8b588 100644 --- a/_config/maps.yml +++ b/_config/maps.yml @@ -4,4 +4,4 @@ After: 'framework/*','cms/*' --- Mappable: allow_full_screen: true - use_compressed_assets: true + use_compressed_assets: false From 260c5468f5f9ebcda7201672674a3c4aede54b3c Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 24 Mar 2015 11:54:58 +0700 Subject: [PATCH 209/354] MINOR: Documentation in the code --- code/PointsOfInterestLayerExtension.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code/PointsOfInterestLayerExtension.php b/code/PointsOfInterestLayerExtension.php index b43b1ca..29e0580 100644 --- a/code/PointsOfInterestLayerExtension.php +++ b/code/PointsOfInterestLayerExtension.php @@ -12,7 +12,10 @@ class PointsOfInterestLayerExtension extends DataExtension { ) ); - + /** + * Update cms fields - add list of POIs + * @param FieldList $fields list of existing fields on the object + */ public function updateCMSFields(FieldList $fields) { $gridConfig2 = GridFieldConfig_RelationEditor::create(); $gridConfig2->getComponentByType( From 4062f96cc66182b01c3acbd4584cdab5d2a03b9d Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 24 Mar 2015 23:15:11 +0700 Subject: [PATCH 210/354] ENHANCEMENT: Move min/max coor calculation to JavaScript for reasons of caching Conflicts: code/MapAPI.php --- code/MapAPI.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index 6867d54..d92d92a 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -537,13 +537,6 @@ public function geocoding($address) { public function addMarkerByCoords($lat, $lng, $html='', $category='', $icon='') { - // Save the lat/lon to enable the automatic center/zoom - $this->maxLng = (float) max((float)$lng, $this->maxLng); - $this->minLng = (float) min((float)$lng, $this->minLng); - $this->maxLat = (float) max((float)$lat, $this->maxLat); - $this->minLat = (float) min((float)$lat, $this->minLat); - $this->centerLng = (float) ($this->minLng + $this->maxLng) / 2; - $this->centerLat = (float) ($this->minLat + $this->maxLat) / 2; $iconURL = null; if ($icon) { $iconURL = $icon->getURL(); @@ -821,10 +814,6 @@ public function generate() { 'Zoom' => $this->zoom, 'MaxZoom' => $this->maxZoom, 'GridSize' => $this->gridSize, - 'MinLng' => $this->minLng, - 'MinLat' => $this->minLat, - 'MaxLng' => $this->maxLng, - 'MaxLat' => $this->maxLat, 'MapType' => $this->mapType, 'GoogleMapID' => $this->googleMapId, 'Lang'=>$this->lang, From 0c8f4332f0c37eb53dc8be096605ce728bf73509 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 24 Mar 2015 23:15:43 +0700 Subject: [PATCH 211/354] FIX: Condition for map location not being edited was incorrect --- code/MapExtension.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/code/MapExtension.php b/code/MapExtension.php index 7ec487c..edfdba0 100644 --- a/code/MapExtension.php +++ b/code/MapExtension.php @@ -55,6 +55,7 @@ public function getMappableLongitude() { return $this->owner->Lon; } + /** * Renders the map info window for the DataObject. * @@ -76,16 +77,23 @@ public function getMappableMapContent() { return MapUtil::sanitize($this->owner->renderWith($template)); } + /* If the marker pin is not at position 0,0 mark the pin as edited. This provides the option of filtering out (0,0) point which is often irrelevant for plots */ public function onBeforeWrite() { - if (($this->owner->Lat !== 0) || ($this->owner->Lon !== 0)) { + $latzero = ($this->owner->Lat == 0); + $lonzero = ($this->owner->Lon == 0); + $latlonzero = $latzero && $lonzero; + + // if both latitude and longitude still default, do not set the map location as edited + if (!$latlonzero) { $this->owner->MapPinEdited = true; } } + /* If a user has uploaded a map pin icon display that, otherwise */ @@ -103,6 +111,7 @@ public function getMappableMapPin() { return $result; } + /* Check for non zero coordinates, on the assumption that (0,0) will never be the desired coordinates */ From dbdbc793765c9219aef41e74305ba06a591505fc Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 24 Mar 2015 23:16:03 +0700 Subject: [PATCH 212/354] ENHANCEMENT: Calculate min/max bounds of markers using JavaScript instead of PHP --- javascript/google/maputil.js | 60 ++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index 2ad69cb..bb32282 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -80,14 +80,50 @@ function getCurrentLng() { * * @return array of Google map markers converted from the JSON data */ function addAllMarkers(map, markers, useClusterer, enableWindowZoom, defaultHideMarker) { + + // these will be altered by adding markers + map.minLat = 1000000; + map.minLng = 1000000; + map.maxLat = -1000000; + map.maxLng = -1000000; + + var allmarkers = []; for (var i = 0; i < markers.length; i++) { var markerinfo = markers[i]; var marker = createMarker(map, markerinfo.latitude, markerinfo.longitude, markerinfo.html, markerinfo.category, markerinfo.icon, useClusterer, enableWindowZoom, defaultHideMarker); + + var latitude = parseFloat(markerinfo.latitude); + var longitude = parseFloat(markerinfo.longitude); + + // update lat,lon min to lat,lon max + if (latitude < map.minLat) { + map.minLat = latitude; + + } + if (latitude > map.maxLat) { + map.maxLat = latitude; + } + + if (longitude > map.maxLng) { + map.maxLng = longitude; + + } + + if (longitude < map.minLng) { + map.minLng = longitude; + } + allmarkers.push(marker); } + + + var centreCoordinates = []; + centreCoordinates.lat = (parseFloat(map.minLat)+parseFloat(map.maxLat))/2;; + centreCoordinates.lng = (parseFloat(map.minLng)+parseFloat(map.maxLng))/2; + map.centreCoordinates = centreCoordinates; return allmarkers; } @@ -131,17 +167,14 @@ function addKmlFiles(map, kmlFiles) { } -function registerMap(googleMapID, centreCoordinates, zoom, minLat, minLng, maxLat, maxLng, mapType, +function registerMap(googleMapID, centreCoordinates, zoom, mapType, markers, lines, kmlFiles, jsonMapStyles, enableAutomaticCenterZoom, useClusterer, allowFullScreen) { var newMap = []; newMap.googleMapID = googleMapID; newMap.zoom = zoom; newMap.centreCoordinates = centreCoordinates; - newMap.minLat = minLat; - newMap.minLng = minLng; - newMap.maxLng = maxLng; - newMap.maxLat = maxLat; + newMap.markers = markers; newMap.googleMapID = googleMapID; newMap.mapType = mapType; @@ -191,14 +224,24 @@ function loadedGoogleMapsAPI() { FullScreenControl(map, "Full Screen", "Original Size") ); } + + + var markers = addAllMarkers(map, map_info.markers, map_info.useClusterer, + map_info.enableAutomaticCenterZoom, map_info.defaultHideMarker); + + if (map_info.enableAutomaticCenterZoom) { - centre = map_info.centreCoordinates; + centre = map.centreCoordinates; map.setCenter(new google.maps.LatLng(centre.lat, centre.lng)); + map_info.minLat = map.minLat; + map_info.maxLat = map.maxLat; + map_info.minLng = map.minLng; + map_info.maxLng = map.maxLng; + var bds = new google.maps.LatLngBounds(new google.maps.LatLng(map_info.minLat, map_info.minLng), new google.maps.LatLng(map_info.maxLat, map_info.maxLng)); map.fitBounds(bds); - map.setZoom(map_info.zoom); } else { var centre = map_info.centreCoordinates; map.setCenter(new google.maps.LatLng(centre.lat, centre.lng)); @@ -211,9 +254,6 @@ function loadedGoogleMapsAPI() { map.setMapTypeId(google.maps.MapTypeId.ROADMAP); } - var markers = addAllMarkers(map, map_info.markers, map_info.useClusterer, - map_info.enableAutomaticCenterZoom, map_info.defaultHideMarker); - if (map_info.useClusterer) { var mcOptions = {gridSize: 50, maxZoom: 17}; var markerCluster = new MarkerClusterer(map,markers,mcOptions); From 2ca45e805b89c9cc1c16854ba5382db8dcdcf0dc Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 24 Mar 2015 23:16:30 +0700 Subject: [PATCH 213/354] ENHANCEMENT: Remove min/max bounds from register map call --- javascript/google/MapGoogleJS.ss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/google/MapGoogleJS.ss b/javascript/google/MapGoogleJS.ss index 6855605..0974f37 100644 --- a/javascript/google/MapGoogleJS.ss +++ b/javascript/google/MapGoogleJS.ss @@ -5,6 +5,6 @@ var gmarkers = []; var mapLayers = []; var mapLines = []; <% end_if %> -registerMap('$GoogleMapID', $LatLngCentre, $Zoom, $MinLat,$MinLng,$MaxLat,$MaxLng, $MapType, +registerMap('$GoogleMapID', $LatLngCentre, $Zoom, $MapType, $MapMarkers,$Lines,$KmlFiles, $JsonMapStyles, $EnableAutomaticCenterZoom, $UseClusterer, $AllowFullScreen); From 92cfeadac6c54691f5b0c47af50d2751175bbda1 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 25 Mar 2015 12:00:09 +0700 Subject: [PATCH 214/354] FIX: whitespace --- code/shortcodes/GoogleMapShortCodeHandler.php | 159 +++++++++--------- 1 file changed, 79 insertions(+), 80 deletions(-) diff --git a/code/shortcodes/GoogleMapShortCodeHandler.php b/code/shortcodes/GoogleMapShortCodeHandler.php index 4bcb062..f6e1c86 100644 --- a/code/shortcodes/GoogleMapShortCodeHandler.php +++ b/code/shortcodes/GoogleMapShortCodeHandler.php @@ -2,84 +2,83 @@ class GoogleMapShortCodeHandler { - /* Counter used to ensure unique div ids to allow for multiple maps on on page */ - private static $gsv_ctr = 1; - - public static function parse_googlemap($arguments, $caption = null, $parser = null) { - // each of latitude and longitude are required at a bare minimum - if(!isset($arguments['latitude'])){ - return ''; - } - - if(!isset($arguments['longitude'])){ - return ''; - } - - - // defaults - can be overriden by using zoom and FIXME in the shortcode - $defaults = array( - 'Zoom' => 5, - 'MapType' => 'ROAD' - ); - - // ensure JavaScript for the map service is only downloaded once - $arguments['DownloadJS'] = !MapUtil::get_map_already_rendered(); - MapUtil::set_map_already_rendered(true); - - // convert parameters to CamelCase as per standard template conventions - $arguments['Latitude'] = $arguments['latitude']; - $arguments['Longitude'] = $arguments['longitude']; - - // optional parameter caption - if (isset($arguments['caption'])) { - $arguments['Caption'] = $arguments['caption']; - } - - // optional parameter pitch - if (isset($arguments['maptype'])) { - $arguments['MapType'] = $arguments['maptype']; - - switch ($arguments['MapType']) { - case 'road': - $arguments['MapType'] = 'ROAD'; - break; - case 'aerial': - $arguments['MapType'] = 'SATELLITE'; - break; - case 'hybrid': - $arguments['MapType'] = 'HYBRID'; - break; - case 'terrain': - $arguments['MapType'] = 'TERRAIN'; - break; - - default: - $arguments['MapType'] = 'ROAD'; - break; - } - } - - // optional parameter zoom - if (isset($arguments['zoom'])) { - $arguments['Zoom'] = $arguments['zoom']; - } - - // the id of the dom element to be used to render the street view - $arguments['DomID'] = 'google_sc_map_'.self::$gsv_ctr; - - // fullscreen - $arguments['AllowFullScreen'] = Config::inst()->get('Mappable', 'allow_full_screen'); - - // incrememt the counter to ensure a unique id for each map canvas - self::$gsv_ctr++; - - // merge defaults and arguments - $customised = array_merge($defaults, $arguments); - - //get streetview template template - $template = new SSViewer('GoogleMapShortCode'); - - //return the template customised with the parmameters - return $template->process(new ArrayData($customised)); - } + /* Counter used to ensure unique div ids to allow for multiple maps on on page */ + private static $gsv_ctr = 1; + + public static function parse_googlemap($arguments, $caption = null, $parser = null) { + // each of latitude and longitude are required at a bare minimum + if(!isset($arguments['latitude'])){ + return ''; + } + + if(!isset($arguments['longitude'])){ + return ''; + } + + // defaults - can be overriden by using zoom and FIXME in the shortcode + $defaults = array( + 'Zoom' => 5, + 'MapType' => 'ROAD' + ); + + // ensure JavaScript for the map service is only downloaded once + $arguments['DownloadJS'] = !MapUtil::get_map_already_rendered(); + MapUtil::set_map_already_rendered(true); + + // convert parameters to CamelCase as per standard template conventions + $arguments['Latitude'] = $arguments['latitude']; + $arguments['Longitude'] = $arguments['longitude']; + + // optional parameter caption + if (isset($arguments['caption'])) { + $arguments['Caption'] = $arguments['caption']; + } + + // optional parameter pitch + if (isset($arguments['maptype'])) { + $arguments['MapType'] = $arguments['maptype']; + + switch ($arguments['MapType']) { + case 'road': + $arguments['MapType'] = 'ROAD'; + break; + case 'aerial': + $arguments['MapType'] = 'SATELLITE'; + break; + case 'hybrid': + $arguments['MapType'] = 'HYBRID'; + break; + case 'terrain': + $arguments['MapType'] = 'TERRAIN'; + break; + + default: + $arguments['MapType'] = 'ROAD'; + break; + } + } + + // optional parameter zoom + if (isset($arguments['zoom'])) { + $arguments['Zoom'] = $arguments['zoom']; + } + + // the id of the dom element to be used to render the street view + $arguments['DomID'] = 'google_sc_map_'.self::$gsv_ctr; + + // fullscreen + $arguments['AllowFullScreen'] = Config::inst()->get('Mappable', 'allow_full_screen'); + + // incrememt the counter to ensure a unique id for each map canvas + self::$gsv_ctr++; + + // merge defaults and arguments + $customised = array_merge($defaults, $arguments); + + //get streetview template template + $template = new SSViewer('GoogleMapShortCode'); + + //return the template customised with the parmameters + return $template->process(new ArrayData($customised)); + } } From adc57af5d7df162375bf9cf18ed9235ef7e15b3d Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 25 Mar 2015 23:23:44 +0700 Subject: [PATCH 215/354] MINOR: Moved embedded google maps javascript to a JavaScript template --- javascript/google/map.google.template.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 javascript/google/map.google.template.js diff --git a/javascript/google/map.google.template.js b/javascript/google/map.google.template.js new file mode 100644 index 0000000..d9ec1ce --- /dev/null +++ b/javascript/google/map.google.template.js @@ -0,0 +1,15 @@ +function initialize() { + var mapOptions = { + center: { lat: $Latitude, lng: $Longitude}, + zoom: $Zoom, + mapTypeId: google.maps.MapTypeId.$MapType + }; + var map = new google.maps.Map(document.getElementById('$DomID'), + mapOptions); + if ($AllowFullScreen == 1) { + map.controls[google.maps.ControlPosition.TOP_RIGHT].push( + FullScreenControl(map, "Full Screen", "Original Size") + ); + } +} +google.maps.event.addDomListener(window, 'load', initialize); From 20c983a105228c6d8086d2b016af438b7c4d436b Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 25 Mar 2015 23:24:14 +0700 Subject: [PATCH 216/354] MINOR: Moved embedded google maps javascript to a JavaScript template --- code/shortcodes/GoogleMapShortCodeHandler.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code/shortcodes/GoogleMapShortCodeHandler.php b/code/shortcodes/GoogleMapShortCodeHandler.php index f6e1c86..1d00f5e 100644 --- a/code/shortcodes/GoogleMapShortCodeHandler.php +++ b/code/shortcodes/GoogleMapShortCodeHandler.php @@ -75,7 +75,10 @@ public static function parse_googlemap($arguments, $caption = null, $parser = nu // merge defaults and arguments $customised = array_merge($defaults, $arguments); - //get streetview template template + // include JavaScript to be appended at the end of the page + Requirements::javascriptTemplate("mappable/javascript/google/map.google.template.js", $customised); + + //get map view template template $template = new SSViewer('GoogleMapShortCode'); //return the template customised with the parmameters From eca5e6bf4d9a49a0b67ce6724202d5b72f66ee1f Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 25 Mar 2015 23:25:33 +0700 Subject: [PATCH 217/354] MINOR: Reordering of inline JavaScript before JS files --- templates/Includes/GoogleJavaScript.ss | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/templates/Includes/GoogleJavaScript.ss b/templates/Includes/GoogleJavaScript.ss index c303ec9..84931bd 100644 --- a/templates/Includes/GoogleJavaScript.ss +++ b/templates/Includes/GoogleJavaScript.ss @@ -2,18 +2,16 @@ <% if DelayLoadMapFunction %> <% else %> <% require javascript("//maps.google.com/maps/api/js?sensor=false&hl=$Lang") %> - <% if $UseCompressedAssets %> <% require javascript("mappable/javascript/google/mappablegoogle.min.js") %> <% else %> + <% require javascript("mappable/javascript/google/FullScreenControl.js") %> <% require javascript("mappable/javascript/google/markerclusterer.js") %> <% require javascript("mappable/javascript/google/maputil.js") %> <% end_if %> <% end_if %> - - <% end_if %> From 1976ce580e3d326c8ca457e91b015d06c0f522ec Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 01:20:26 +0700 Subject: [PATCH 218/354] WIP: Using callbacks to render streetview and embedded maps after deferring JS as late as possible --- javascript/google/maputil.js | 170 +++++++++++++++++++++++++++++++---- 1 file changed, 151 insertions(+), 19 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index bb32282..a0cd986 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -167,23 +167,63 @@ function addKmlFiles(map, kmlFiles) { } -function registerMap(googleMapID, centreCoordinates, zoom, mapType, - markers, lines, kmlFiles, jsonMapStyles, enableAutomaticCenterZoom, useClusterer, - allowFullScreen) { +/** + * Register a short code generated map, namely storing parameters for later rendering + * once google maps API loaded + * @param array options lat,lon,zoom,type + */ +function registerShortcodeMap(options) { + newMap = []; + newMap.latitude = options.latitude; + newMap.longitude = options.longitude; + newMap.zoom = options.zoom; + newMap.maptype = options.maptype; + newMap.allowfullscreen = options.allowfullscreen; + newMap.caption = options.caption; + newMap.domid = options.domid; + shortcodeMaps.push(newMap); +} + +/** + * Register a short code generated streetview, namely storing parameters for later rendering + * once google maps API loaded + * @param array options lat,lon,zoom,pitch,heading,caption,domid + */ +function registerStreetView(options) { + newView = []; + newView.latitude = options.latitude; + newView.longitude = options.longitude; + newView.zoom = options.zoom; + newView.pitch = options.pitch; + newView.heading = options.heading; + newView.caption = options.caption; + newView.domid = options.domid; + shortcodeStreetview.push(newView); +} + + +//function registerMap(googleMapID, centreCoordinates, zoom, mapType, +// markers, lines, kmlFiles, jsonMapStyles, enableAutomaticCenterZoom, useClusterer, +// allowFullScreen) { + +function registerMap(options) { + console.log("REGISTERING MAP"); + console.log(options); + var newMap = []; - newMap.googleMapID = googleMapID; - newMap.zoom = zoom; - newMap.centreCoordinates = centreCoordinates; - - newMap.markers = markers; - newMap.googleMapID = googleMapID; - newMap.mapType = mapType; - newMap.lines = lines; - newMap.kmlFiles = kmlFiles; - newMap.jsonMapStyles = jsonMapStyles; - newMap.enableAutomaticCenterZoom = enableAutomaticCenterZoom; - newMap.useClusterer = useClusterer; - newMap.allowFullScreen = allowFullScreen; + newMap.googleMapID = options.domid; + newMap.zoom = options.zoom; + newMap.centreCoordinates = options.centre; + + newMap.markers = options.mapmarkers; + newMap.mapType = options.maptype; + newMap.lines = options.lines; + newMap.kmlFiles = options.kmlfiles; + newMap.jsonMapStyles = options.mapstyles; + newMap.enableAutomaticCenterZoom = options.enableautocentrezoom; + newMap.useClusterer = options.useclusterer; + newMap.allowFullScreen = options.allowfullscreen; + var googleMapID = options.domid; mappableMaps[googleMapID] = newMap; // increment map counter @@ -191,14 +231,98 @@ function registerMap(googleMapID, centreCoordinates, zoom, mapType, // initialise gmarkers array for this map gmarkers[googleMapID] = []; - var infoWindow = new google.maps.InfoWindow({ + /* + + var infoWindow = new google.maps.InfoWindow({ content: 'test', maxWidth: 400 }); infoWindows[googleMapID] = infoWindow; + */ + + mapLayers[googleMapID] = options.kmlfiles; + mapLines[googleMapID] = options.lines; +} + + +/** + * After the Google Maps API has been loaded init the relevant Google maps + */ +function loadShortCodeStreetView() { + for (var i = 0; i < shortcodeStreetview.length; i++) { + console.log("STREETVIEW"); + + view = shortcodeStreetview[i]; + console.log(view); + + var location = new google.maps.LatLng(view.latitude, view.longitude); + console.log(location); + + var mapOptions = { + center: location, + zoom: view.zoom + }; + + console.log(mapOptions); + + var map = new google.maps.Map( + document.getElementById(view.domid), mapOptions); + + var panoramaOptions = { + position: location, + pov: { + heading: view.heading, + pitch: view.pitch + }, + zoom: view.zoom + }; + var domNode = document.getElementById(view.domid); + console.log(domNode); + var pano = new google.maps.StreetViewPanorama( + domNode, + panoramaOptions); + pano.setVisible(true); + } + +} + + +/** + * After the Google Maps API has been loaded init the relevant Google maps + */ +function loadShortCodeMaps() { + for (var i = 0; i < shortcodeMaps.length; i++) { + map = shortcodeMaps[i]; + + // deal with map type + var maptype = google.maps.MapTypeId.ROAD; + switch (map.maptype) { + case 'aerial': + maptype = google.maps.MapTypeId.SATELLITE; + break; + case 'hybrid': + maptype = google.maps.MapTypeId.HYBRID; + break; + case 'terrain': + maptype = google.maps.MapTypeId.TERRAIN; + break; + } + + // Google Maps API has already been loaded, so init Google Map + var mapOptions = { + center: { lat: map.latitude, lng: map.longitude}, + zoom: map.zoom, + mapTypeId: maptype + }; + var gmap = new google.maps.Map(document.getElementById(map.domid), + mapOptions); + if (map.allowfullscreen == 1) { + gmap.controls[google.maps.ControlPosition.TOP_RIGHT].push( + FullScreenControl(gmap, "Full Screen", "Original Size") + ); + } + } - mapLayers[googleMapID] = kmlFiles; - mapLines[googleMapID] = lines; } @@ -207,8 +331,13 @@ function registerMap(googleMapID, centreCoordinates, zoom, mapType, * associated points of interest and layers */ function loadedGoogleMapsAPI() { + loadShortCodeMaps(); + loadShortCodeStreetView(); + for (var i = 1; i <= mappableMapCtr; i++) { var map_info = mappableMaps['google_map_' + i]; + console.log("MAP INFO"); + console.log(map_info); var map = new google.maps.Map(document.getElementById(map_info.googleMapID)); // initialise geocoder @@ -254,6 +383,9 @@ function loadedGoogleMapsAPI() { map.setMapTypeId(google.maps.MapTypeId.ROADMAP); } + map.setMapTypeId(google.maps.MapTypeId.ROADMAP); + + if (map_info.useClusterer) { var mcOptions = {gridSize: 50, maxZoom: 17}; var markerCluster = new MarkerClusterer(map,markers,mcOptions); From 5a0fa4d8743c3ef35ba17a7f8ddeda055b399caf Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 01:22:27 +0700 Subject: [PATCH 219/354] MINOR: Whitespace fixing --- _config/_config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config/_config.yml b/_config/_config.yml index 4583419..9038b26 100644 --- a/_config/_config.yml +++ b/_config/_config.yml @@ -1,3 +1,3 @@ # allow geographical format files to be uploaded File: - $allowed_extensions: ['gpx', 'kml'] \ No newline at end of file + $allowed_extensions: ['gpx', 'kml'] From d3f2b8a7ae528a039d30d3e744efb8b6d69bbff4 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 01:22:36 +0700 Subject: [PATCH 220/354] MINOR: Whitespace fixing --- _config/extensions.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config/extensions.yml b/_config/extensions.yml index b3cd5b6..34246e9 100644 --- a/_config/extensions.yml +++ b/_config/extensions.yml @@ -20,4 +20,4 @@ ArrayList: POIMapPage: extensions: - ['PointsOfInterestLayerExtension','MapExtension'] \ No newline at end of file + ['PointsOfInterestLayerExtension','MapExtension'] From 59f6b2d3a26fa71a79733322454f6a19589eb38d Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 01:25:10 +0700 Subject: [PATCH 221/354] MINOR: Removal of whitespace and now obsolete google maps loading call --- code/MapAPI.php | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index d92d92a..13644f7 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -702,26 +702,17 @@ public function addLine($from = array(), $to = array(), $color = "#FF3300") { ); array_push($this->lines, $line); - - } - /** - * Initialize the javascript code - * - * @return void - */ - - -/* -For php 5.3 -*/ -function jsonRemoveUnicodeSequences($struct) { - return preg_replace("/\\\\u([a-f0-9]{4})/e", - "iconv('UCS-4LE','UTF-8',pack('V', hexdec('U$1')))", - json_encode($struct)); -} + /* + For php 5.3 + */ + function jsonRemoveUnicodeSequences($struct) { + return preg_replace("/\\\\u([a-f0-9]{4})/e", + "iconv('UCS-4LE','UTF-8',pack('V', hexdec('U$1')))", + json_encode($struct)); + } /** @@ -835,18 +826,10 @@ public function generate() { Requirements::customScript(<< Date: Fri, 27 Mar 2015 01:25:31 +0700 Subject: [PATCH 222/354] MINOR: Debug removal --- code/PointsOfInterestLayer.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/code/PointsOfInterestLayer.php b/code/PointsOfInterestLayer.php index f22284d..ec3f169 100644 --- a/code/PointsOfInterestLayer.php +++ b/code/PointsOfInterestLayer.php @@ -25,8 +25,4 @@ function getCMSFields() { return $fields; } - - function Wibble() { - return 'wibble'; - } } From 3fa608b57dde6cd2d59bbf0969dae47165d3253e Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 01:26:05 +0700 Subject: [PATCH 223/354] WIP: Deferring JS to end of page --- code/shortcodes/GoogleMapShortCodeHandler.php | 26 +++---------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/code/shortcodes/GoogleMapShortCodeHandler.php b/code/shortcodes/GoogleMapShortCodeHandler.php index 1d00f5e..f66518f 100644 --- a/code/shortcodes/GoogleMapShortCodeHandler.php +++ b/code/shortcodes/GoogleMapShortCodeHandler.php @@ -18,7 +18,7 @@ public static function parse_googlemap($arguments, $caption = null, $parser = nu // defaults - can be overriden by using zoom and FIXME in the shortcode $defaults = array( 'Zoom' => 5, - 'MapType' => 'ROAD' + 'MapType' => 'road' ); // ensure JavaScript for the map service is only downloaded once @@ -34,28 +34,8 @@ public static function parse_googlemap($arguments, $caption = null, $parser = nu $arguments['Caption'] = $arguments['caption']; } - // optional parameter pitch if (isset($arguments['maptype'])) { $arguments['MapType'] = $arguments['maptype']; - - switch ($arguments['MapType']) { - case 'road': - $arguments['MapType'] = 'ROAD'; - break; - case 'aerial': - $arguments['MapType'] = 'SATELLITE'; - break; - case 'hybrid': - $arguments['MapType'] = 'HYBRID'; - break; - case 'terrain': - $arguments['MapType'] = 'TERRAIN'; - break; - - default: - $arguments['MapType'] = 'ROAD'; - break; - } } // optional parameter zoom @@ -75,10 +55,10 @@ public static function parse_googlemap($arguments, $caption = null, $parser = nu // merge defaults and arguments $customised = array_merge($defaults, $arguments); - // include JavaScript to be appended at the end of the page + // include JavaScript to be appended at the end of the page, namely params for map rendering Requirements::javascriptTemplate("mappable/javascript/google/map.google.template.js", $customised); - //get map view template template + //get map view template and render the HTML $template = new SSViewer('GoogleMapShortCode'); //return the template customised with the parmameters From 0e20b3372f72f8148113dc123e5a38a670b33a84 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 01:26:33 +0700 Subject: [PATCH 224/354] WIP: Use JS template to add variables to prime streetview --- code/shortcodes/GoogleStreetViewShortCodeHandler.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/shortcodes/GoogleStreetViewShortCodeHandler.php b/code/shortcodes/GoogleStreetViewShortCodeHandler.php index 5b2d045..48f2bbe 100644 --- a/code/shortcodes/GoogleStreetViewShortCodeHandler.php +++ b/code/shortcodes/GoogleStreetViewShortCodeHandler.php @@ -59,6 +59,10 @@ public static function parse_googlestreetview($arguments, $caption = null, $pars // merge defaults and arguments $customised = array_merge($defaults, $arguments); + // Include google maps JS at the end of the page + Requirements::javascriptTemplate("mappable/javascript/google/streetview.google.template.js", $customised); + + //get streetview template template $template = new SSViewer('GoogleStreetView'); From 10c492ffb67d1eb65bf790e1f424c0c23157da14 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 01:26:46 +0700 Subject: [PATCH 225/354] MINOR: Whitespace compliance --- css/lat_long_field.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/css/lat_long_field.css b/css/lat_long_field.css index 7480efe..13147b2 100755 --- a/css/lat_long_field.css +++ b/css/lat_long_field.css @@ -4,4 +4,4 @@ #LatLonZoomLevel .fieldholder-small-label { margin: 0px; -} \ No newline at end of file +} From 1da8f1adcce840354dbf62ca319d81d65a341054 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 01:27:24 +0700 Subject: [PATCH 226/354] FIX: Use options instead of lots of parameters to render a map --- javascript/google/MapGoogleJS.ss | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/javascript/google/MapGoogleJS.ss b/javascript/google/MapGoogleJS.ss index 0974f37..9e466ed 100644 --- a/javascript/google/MapGoogleJS.ss +++ b/javascript/google/MapGoogleJS.ss @@ -5,6 +5,24 @@ var gmarkers = []; var mapLayers = []; var mapLines = []; <% end_if %> +var options = { + centre: $LatLngCentre, + zoom: $Zoom, + maptype: '$MapType', + domid: '$GoogleMapID', + allowfullscreen: $AllowFullScreen, + mapmarkers: $MapMarkers, + lines: $Lines, + kmlfiles: $KmlFiles, + mapstyles: $JsonMapStyles, + useclusterer: $UseClusterer, + enableautocentrezoom: $EnableAutomaticCenterZoom +} + +registerMap(options); + +/* registerMap('$GoogleMapID', $LatLngCentre, $Zoom, $MapType, $MapMarkers,$Lines,$KmlFiles, $JsonMapStyles, $EnableAutomaticCenterZoom, $UseClusterer, $AllowFullScreen); +*/ From 2801ecb1dc97681e75afe398eb3302a4f1e27868 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 01:28:04 +0700 Subject: [PATCH 227/354] FIX: Instead of calling function to render a map, call one that primes values to be rendered by a callback --- javascript/google/map.google.template.js | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/javascript/google/map.google.template.js b/javascript/google/map.google.template.js index d9ec1ce..9b31e0c 100644 --- a/javascript/google/map.google.template.js +++ b/javascript/google/map.google.template.js @@ -1,15 +1,9 @@ -function initialize() { - var mapOptions = { - center: { lat: $Latitude, lng: $Longitude}, - zoom: $Zoom, - mapTypeId: google.maps.MapTypeId.$MapType - }; - var map = new google.maps.Map(document.getElementById('$DomID'), - mapOptions); - if ($AllowFullScreen == 1) { - map.controls[google.maps.ControlPosition.TOP_RIGHT].push( - FullScreenControl(map, "Full Screen", "Original Size") - ); - } +var options = { + latitude: $Latitude, + longitude: $Longitude, + zoom: $Zoom, + maptype: '$MapType', + domid: '$DomID', + allowfullscreen: $AllowFullScreen } -google.maps.event.addDomListener(window, 'load', initialize); +registerShortcodeMap(options); From 6e422da0160f89db5a1937bfafb32953d9149d94 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 01:29:01 +0700 Subject: [PATCH 228/354] FIX: Load google maps asynchronously and use callback to prime maps with provided data. This gets around the issue of timing and google map library not being found --- templates/Includes/GoogleJavaScript.ss | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/templates/Includes/GoogleJavaScript.ss b/templates/Includes/GoogleJavaScript.ss index 84931bd..9db04cf 100644 --- a/templates/Includes/GoogleJavaScript.ss +++ b/templates/Includes/GoogleJavaScript.ss @@ -1,12 +1,23 @@ <% if DownloadJS %> <% if DelayLoadMapFunction %> <% else %> -<% require javascript("//maps.google.com/maps/api/js?sensor=false&hl=$Lang") %> + <% if $UseCompressedAssets %> <% require javascript("mappable/javascript/google/mappablegoogle.min.js") %> <% else %> <% require javascript("mappable/javascript/google/FullScreenControl.js") %> <% require javascript("mappable/javascript/google/markerclusterer.js") %> From e7c1eea3d041462eeb209a3cb71cb7f2df49b533 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 01:29:28 +0700 Subject: [PATCH 229/354] FIX: Move JavaScript to a template that can be rendered at the end of the page --- templates/Includes/GoogleMapShortCode.ss | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/templates/Includes/GoogleMapShortCode.ss b/templates/Includes/GoogleMapShortCode.ss index e3fc2be..959002a 100644 --- a/templates/Includes/GoogleMapShortCode.ss +++ b/templates/Includes/GoogleMapShortCode.ss @@ -1,22 +1,5 @@ <% include GoogleJavaScript %>
-
+
<% if $Caption %>

$Caption

<% end_if %>
- From c4fc45ed259291415ce51c37b59d0c08945b45b7 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 01:29:39 +0700 Subject: [PATCH 230/354] FIX: Move JavaScript to a template that can be rendered at the end of the page --- templates/Includes/GoogleStreetView.ss | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/templates/Includes/GoogleStreetView.ss b/templates/Includes/GoogleStreetView.ss index 5cf750a..71c3545 100644 --- a/templates/Includes/GoogleStreetView.ss +++ b/templates/Includes/GoogleStreetView.ss @@ -1,24 +1,5 @@ <% include GoogleJavaScript %> -
+
<% if $Caption %>

$Caption

<% end_if %>
- From 8ac01a88fa800e600d2b544920b3ac0ee284c1b9 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 01:30:05 +0700 Subject: [PATCH 231/354] FIX: Move JS for google streetview into a separate template from teh HTML --- javascript/google/streetview.google.template.js | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 javascript/google/streetview.google.template.js diff --git a/javascript/google/streetview.google.template.js b/javascript/google/streetview.google.template.js new file mode 100644 index 0000000..72209fb --- /dev/null +++ b/javascript/google/streetview.google.template.js @@ -0,0 +1,9 @@ +var options = { + latitude: $Latitude, + longitude: $Longitude, + zoom: $Zoom, + pitch: $Pitch, + heading: $Heading, + domid: '$DomID' +} +registerStreetView(options); From 9dd040b7d042a815fd4948cdf92c9a9c1ea81581 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 10:41:23 +0700 Subject: [PATCH 232/354] FIX: Updated map types to those used with version 3 of the Google Maps API --- code/MapAPI.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index 13644f7..6f7b190 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -136,13 +136,13 @@ class MapAPI extends ViewableData /** * Type of the gmap, can be: - * 'google.maps.MapTypeId.ROADMAP' (roadmap), - * 'G_SATELLITE_MAP' (sattelite) - * 'G_HYBRID_MAP' (hybrid) - * 'G_PHYSICAL_MAP' (terrain) + * 'google.maps.MapTypeId.ROAD' (roadmap), + * 'google.maps.MapTypeId.SATELLITE' (sattelite) + * 'google.maps.MapTypeId.HYBRID' (hybrid) + * 'google.maps.MapTypeId.TERRAIN' (terrain) */ - protected $mapType = 'google.maps.MapTypeId.ROADMAP'; + protected $mapType = 'google.maps.MapTypeId.ROAD'; /** Content of the HTML generated **/ protected $content = ''; From 9e56b0b85a633c80c98d48c158898eb896476ec3 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 11:50:03 +0700 Subject: [PATCH 233/354] FIX: Changed map type to a generic one so that other map services are not dependent on google names --- code/MapAPI.php | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index 6f7b190..e2d176b 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -136,13 +136,15 @@ class MapAPI extends ViewableData /** * Type of the gmap, can be: - * 'google.maps.MapTypeId.ROAD' (roadmap), - * 'google.maps.MapTypeId.SATELLITE' (sattelite) - * 'google.maps.MapTypeId.HYBRID' (hybrid) - * 'google.maps.MapTypeId.TERRAIN' (terrain) + * 'road' (roadmap), + * 'satellite' (sattelite/aerial photographs) + * 'hybrid' (hybrid of road and satellite) + * 'terrain' (terrain) + * The JavaScript for the mapping service will convert this into a suitable mapping type */ - protected $mapType = 'google.maps.MapTypeId.ROAD'; + protected $mapType = 'road'; + /** Content of the HTML generated **/ protected $content = ''; @@ -407,16 +409,31 @@ public function setCenter($center) { } /** - * Set the type of the gmap + * Set the type of the gmap. Also takes into account legacy settings * - * @param string $mapType (can be 'google.maps.MapTypeId.ROADMAP', - * 'G_SATELLITE_MAP', 'G_HYBRID_MAP', 'G_PHYSICAL_MAP') + * @param string $mapType Can be one of road,satellite,hybrid or terrain. Defaults to road * * @return void */ public function setMapType($mapType) { $this->mapType = $mapType; + + // deal with legacy values for backwards compatbility + switch ($mapType) { + case 'google.maps.MapTypeId.SATELLITE': + $this->mapType = "satellite"; + break; + case 'google.maps.MapTypeId.SATELLITE': + $this->mapType = "satellite"; + break; + case 'google.maps.MapTypeId.SATELLITE': + $this->mapType = "satellite"; + break; + default: + $this->MapType = "road"; + break; + } } /* From e40708e63fc8eb038e1c0359971c0cc9ab50ab27 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 11:50:27 +0700 Subject: [PATCH 234/354] ENHANCEMENT: Added method to create google map type from generic map type name --- javascript/google/maputil.js | 64 +++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index a0cd986..c5936b2 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -167,6 +167,28 @@ function addKmlFiles(map, kmlFiles) { } +/** + * Convert a map type name (road,satellite,hybrid,terrain) to Google map types + * @param String mapTypeName generic name of the map type + * @return google.maps.MapTypeId map type in Google format + */ +function convertMapType(mapTypeName) { + var result = google.maps.MapTypeId.ROADMAP; + switch (mapTypeName) { + case 'aerial': + result = google.maps.MapTypeId.SATELLITE; + break; + case 'hybrid': + result = google.maps.MapTypeId.HYBRID; + break; + case 'terrain': + result = google.maps.MapTypeId.TERRAIN; + break; + } + return result; +} + + /** * Register a short code generated map, namely storing parameters for later rendering * once google maps API loaded @@ -207,9 +229,6 @@ function registerStreetView(options) { // allowFullScreen) { function registerMap(options) { - console.log("REGISTERING MAP"); - console.log(options); - var newMap = []; newMap.googleMapID = options.domid; newMap.zoom = options.zoom; @@ -231,14 +250,7 @@ function registerMap(options) { // initialise gmarkers array for this map gmarkers[googleMapID] = []; - /* - var infoWindow = new google.maps.InfoWindow({ - content: 'test', - maxWidth: 400 - }); - infoWindows[googleMapID] = infoWindow; - */ mapLayers[googleMapID] = options.kmlfiles; mapLines[googleMapID] = options.lines; @@ -295,18 +307,7 @@ function loadShortCodeMaps() { map = shortcodeMaps[i]; // deal with map type - var maptype = google.maps.MapTypeId.ROAD; - switch (map.maptype) { - case 'aerial': - maptype = google.maps.MapTypeId.SATELLITE; - break; - case 'hybrid': - maptype = google.maps.MapTypeId.HYBRID; - break; - case 'terrain': - maptype = google.maps.MapTypeId.TERRAIN; - break; - } + var maptype = convertMapType(map.maptype); // Google Maps API has already been loaded, so init Google Map var mapOptions = { @@ -335,7 +336,8 @@ function loadedGoogleMapsAPI() { loadShortCodeStreetView(); for (var i = 1; i <= mappableMapCtr; i++) { - var map_info = mappableMaps['google_map_' + i]; + var mapdomid = 'google_map_' + i; + var map_info = mappableMaps[mapdomid]; console.log("MAP INFO"); console.log(map_info); var map = new google.maps.Map(document.getElementById(map_info.googleMapID)); @@ -377,14 +379,8 @@ function loadedGoogleMapsAPI() { map.setZoom(map_info.zoom); } - if (map_info.mapType) { - map.setMapTypeId(map_info.mapType); - } else { - map.setMapTypeId(google.maps.MapTypeId.ROADMAP); - } - - map.setMapTypeId(google.maps.MapTypeId.ROADMAP); - + var googlemaptype = convertMapType(map_info.mapType); + map.setMapTypeId(googlemaptype); if (map_info.useClusterer) { var mcOptions = {gridSize: 50, maxZoom: 17}; @@ -393,5 +389,11 @@ function loadedGoogleMapsAPI() { addLines(map, map_info.lines); addKmlFiles(map, map_info.kmlFiles); + + var infoWindow = new google.maps.InfoWindow({ + content: 'test', + maxWidth: 400 + }); + infoWindows[mapdomid] = infoWindow; } } From 1d9ed3824da2b9d832f02c5c0382256c17265a88 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 12:37:44 +0700 Subject: [PATCH 235/354] MINOR: Example css compatible with the simple theme --- css/mapsimple.css | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 css/mapsimple.css diff --git a/css/mapsimple.css b/css/mapsimple.css new file mode 100644 index 0000000..e95477a --- /dev/null +++ b/css/mapsimple.css @@ -0,0 +1,15 @@ +.streetview, .map { + width: 100%; + height: 500px; + background: none; +} + +// see http://stackoverflow.com/questions/11340468/street-view-not-working-in-firefox +.googlestreetview img, .map img { + border: none !important; + max-width: none !important; +} + +.streetviewcontainer p.caption, .googlemapcontainer p.caption { + text-align: center; +} From a874d6c651fb856eef034f883011038ed9974a9b Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 12:38:38 +0700 Subject: [PATCH 236/354] MINOR: Remove hardwired style and move to css --- templates/Includes/GoogleMapShortCode.ss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/Includes/GoogleMapShortCode.ss b/templates/Includes/GoogleMapShortCode.ss index 959002a..f8d718f 100644 --- a/templates/Includes/GoogleMapShortCode.ss +++ b/templates/Includes/GoogleMapShortCode.ss @@ -1,5 +1,5 @@ <% include GoogleJavaScript %>
-
+
<% if $Caption %>

$Caption

<% end_if %>
From 7800f79e36a50353399afeca9e9a95e21e7c5e33 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 27 Mar 2015 12:38:53 +0700 Subject: [PATCH 237/354] MINOR: Remove hardwired style and move to css --- templates/Includes/GoogleStreetView.ss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/Includes/GoogleStreetView.ss b/templates/Includes/GoogleStreetView.ss index 71c3545..e4523be 100644 --- a/templates/Includes/GoogleStreetView.ss +++ b/templates/Includes/GoogleStreetView.ss @@ -1,5 +1,5 @@ <% include GoogleJavaScript %> -
+
<% if $Caption %>

$Caption

<% end_if %>
From 270c07a60ee838c78b80b2a7a84eb1e5a9936381 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 1 Apr 2015 21:59:37 +0700 Subject: [PATCH 238/354] MINOR: Remove logging statements --- javascript/google/maputil.js | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index c5936b2..e4436c5 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -250,7 +250,7 @@ function registerMap(options) { // initialise gmarkers array for this map gmarkers[googleMapID] = []; - + mapLayers[googleMapID] = options.kmlfiles; mapLines[googleMapID] = options.lines; @@ -262,21 +262,14 @@ function registerMap(options) { */ function loadShortCodeStreetView() { for (var i = 0; i < shortcodeStreetview.length; i++) { - console.log("STREETVIEW"); - view = shortcodeStreetview[i]; - console.log(view); - var location = new google.maps.LatLng(view.latitude, view.longitude); - console.log(location); var mapOptions = { center: location, zoom: view.zoom }; - console.log(mapOptions); - var map = new google.maps.Map( document.getElementById(view.domid), mapOptions); @@ -289,7 +282,6 @@ function loadShortCodeStreetView() { zoom: view.zoom }; var domNode = document.getElementById(view.domid); - console.log(domNode); var pano = new google.maps.StreetViewPanorama( domNode, panoramaOptions); @@ -338,8 +330,6 @@ function loadedGoogleMapsAPI() { for (var i = 1; i <= mappableMapCtr; i++) { var mapdomid = 'google_map_' + i; var map_info = mappableMaps[mapdomid]; - console.log("MAP INFO"); - console.log(map_info); var map = new google.maps.Map(document.getElementById(map_info.googleMapID)); // initialise geocoder @@ -360,7 +350,6 @@ function loadedGoogleMapsAPI() { var markers = addAllMarkers(map, map_info.markers, map_info.useClusterer, map_info.enableAutomaticCenterZoom, map_info.defaultHideMarker); - if (map_info.enableAutomaticCenterZoom) { centre = map.centreCoordinates; map.setCenter(new google.maps.LatLng(centre.lat, centre.lng)); From 6670cf7d5226f2292547689c386e6cba476f9edf Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 1 Apr 2015 22:08:59 +0700 Subject: [PATCH 239/354] CONFIG: Moved mapping service name to a config value --- code/MapAPI.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index e2d176b..77741fd 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -82,7 +82,6 @@ class MapAPI extends ViewableData protected $latLongCenter = null; - protected $mappingService = 'Google'; @@ -484,6 +483,7 @@ public function getGoogleMap() { return $this->content; } + /** * Get URL content using cURL. * @@ -619,7 +619,7 @@ public function addMarkerAsObject(ViewableData $obj) { foreach ($extensions as $extension) { $class = new ReflectionClass($extension); if ($class->implementsInterface('Mappable')) { - $extensionsImplementMappable = true; + $extensionsImplementMappable = true; } } @@ -851,8 +851,8 @@ function processTemplateJS($templateName, $templateVariables = null) { if (!$templateVariables) { $templateVariables = new ArrayList(); } - - $result = $templateVariables->renderWith($templateName.$this->mappingService.'JS'); + $mappingService = Config::inst()->get('Mappable', 'mapping_service'); + $result = $templateVariables->renderWith($templateName.$mappingService.'JS'); return $result; } @@ -860,8 +860,8 @@ function processTemplateHTML($templateName, $templateVariables = null ) { if (!$templateVariables) { $templateVariables = new ArrayList(); } - - $result = $templateVariables->renderWith($templateName.$this->mappingService.'HTML'); + $mappingService = Config::inst()->get('Mappable', 'mapping_service'); + $result = $templateVariables->renderWith($templateName.$mappingService.'HTML'); return $result; } } From 72e848fec48191e6c2e2d2cb7c98fc85a5f79fe0 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 2 Apr 2015 00:00:25 +0700 Subject: [PATCH 240/354] MINOR: Remove extra semicolon that jshint was complaining about --- javascript/google/maputil.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index e4436c5..9579fdd 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -121,7 +121,7 @@ function addAllMarkers(map, markers, useClusterer, enableWindowZoom, defaultHide var centreCoordinates = []; - centreCoordinates.lat = (parseFloat(map.minLat)+parseFloat(map.maxLat))/2;; + centreCoordinates.lat = (parseFloat(map.minLat)+parseFloat(map.maxLat))/2; centreCoordinates.lng = (parseFloat(map.minLng)+parseFloat(map.maxLng))/2; map.centreCoordinates = centreCoordinates; return allmarkers; From 30bca3aaaa9cdcb22d334c6bbc398f008de066a4 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 2 Apr 2015 12:08:15 +0700 Subject: [PATCH 241/354] FIX: Remove unused map info width --- code/MapAPI.php | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index 77741fd..c0723be 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -47,13 +47,6 @@ class MapAPI extends ViewableData /* kml file to be rendered */ protected $kmlFiles = array(); - /** - * - * - * @var int Infowindow width of the gmarker - * */ - protected $infoWindowWidth = 500; - /** Default zoom of the gmap **/ protected $zoom = 9; @@ -309,17 +302,6 @@ public function setSize($width, $height) { $this->height = $height; } - /** - * Set the with of the gmap infowindow (on marker clik) - * - * @param int $infoWindowWidth GoogleMap info window width - * - * @return void - */ - - public function setInfoWindowWidth($infoWindowWidth) { - $this->infoWindowWidth = $infoWindowWidth; - } /** * Set the size of the icon markers @@ -810,7 +792,6 @@ public function generate() { 'AdditionalCssClasses' => $this->additional_css_classes, 'Width' => $this->width, 'Height' => $this->height, - 'InfoWindowWidth' => $this->infoWindowWidth, 'ShowInlineMapDivStyle' => $this->show_inline_map_div_style, 'InfoWindowZoom' => $this->infoWindowZoom, 'EnableWindowZoom' => $this->enableWindowZoom, From 7c5c323c63391f10dd9e1194fdbd9363c546a06c Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 2 Apr 2015 12:08:37 +0700 Subject: [PATCH 242/354] MINOR: Remove call to now obsolete set info width --- code/MapUtil.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/MapUtil.php b/code/MapUtil.php index d7f2422..5064d72 100644 --- a/code/MapUtil.php +++ b/code/MapUtil.php @@ -185,7 +185,6 @@ public static function instance() } - $gmap = new MapAPI($key); $gmap->setDivId(self::$div_id."_".self::$instances); $gmap->setEnableAutomaticCenterZoom(self::$automatic_center); @@ -193,7 +192,6 @@ public static function instance() $gmap->setSize(self::$map_width, self::$map_height); $gmap->setDefaultHideMarker(self::$hide_marker); $gmap->setMapType(self::$map_type); - $gmap->setInfoWindowWidth(self::$info_window_width); $gmap->setCenter(self::$center); $gmap->setIconSize(self::$iconWidth, self::$iconHeight); $gmap->setIncludeDownloadJavascript(self::$map_already_rendered); From 1b148a957deb8ec86857c4f9aee079bd78a193e5 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 2 Apr 2015 12:09:28 +0700 Subject: [PATCH 243/354] MINOR: Remove info width --- javascript/google/maputil.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index 9579fdd..8d977bf 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -380,8 +380,7 @@ function loadedGoogleMapsAPI() { addKmlFiles(map, map_info.kmlFiles); var infoWindow = new google.maps.InfoWindow({ - content: 'test', - maxWidth: 400 + content: '' }); infoWindows[mapdomid] = infoWindow; } From 53e9498757faf1537c39365403f929b27b3de56f Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 1 Apr 2015 21:53:50 +0700 Subject: [PATCH 244/354] CONFIG: Added mapping service, and FIX: suffix for map info window was missing a default --- _config/maps.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/_config/maps.yml b/_config/maps.yml index 4e8b588..f0552ca 100644 --- a/_config/maps.yml +++ b/_config/maps.yml @@ -5,3 +5,7 @@ After: 'framework/*','cms/*' Mappable: allow_full_screen: true use_compressed_assets: false + mapping_service: 'Google' + +MapExtension: + map_info_window_suffix: 'MapInfoWindow' From 12aa0bea15d465496318450b46eaf5d59d7ee66c Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 3 Apr 2015 21:01:46 +0700 Subject: [PATCH 245/354] FIX: Remove commented out code that was doubling the JSON output for the markers --- javascript/google/MapGoogleJS.ss | 6 ------ 1 file changed, 6 deletions(-) diff --git a/javascript/google/MapGoogleJS.ss b/javascript/google/MapGoogleJS.ss index 9e466ed..732d051 100644 --- a/javascript/google/MapGoogleJS.ss +++ b/javascript/google/MapGoogleJS.ss @@ -20,9 +20,3 @@ var options = { } registerMap(options); - -/* -registerMap('$GoogleMapID', $LatLngCentre, $Zoom, $MapType, -$MapMarkers,$Lines,$KmlFiles, $JsonMapStyles, $EnableAutomaticCenterZoom, $UseClusterer, -$AllowFullScreen); -*/ From 5a88c79da67dd63db43dc5ef4febc4a062fbd8e7 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 3 Apr 2015 22:08:49 +0700 Subject: [PATCH 246/354] FIX: Ensure show guide markers flag is respected --- code/PointOfInterest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/PointOfInterest.php b/code/PointOfInterest.php index 7f368f9..7c9a9b3 100644 --- a/code/PointOfInterest.php +++ b/code/PointOfInterest.php @@ -19,6 +19,9 @@ function getCMSFields() { $ids = array(); foreach ($layers->getIterator() as $layer) { array_push($ids, $layer->ID); + if ($layer->ShowGuideMarkers) { + $this->ShowGuideMarkers = true; + } } $csv = implode(',', $ids); From c54b42c9c8be27a6cfe30cf9f9baaf1837c81abe Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 3 Apr 2015 23:19:19 +0700 Subject: [PATCH 247/354] ENHANCEMENT: Make map code chainable --- code/MapAPI.php | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index c0723be..b69f593 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -217,30 +217,36 @@ public function __construct($googleMapKey='') { public function setKey($googleMapKey) { $this->googleMapKey = $googleMapKey; + return $this; } public function setIncludeDownloadJavascript($inclusion) { self::$include_download_javascript = $inclusion; + return $this; } public function setShowInlineMapDivStyle($new_show_inline_map_div_style) { $this->show_inline_map_div_style = $new_show_inline_map_div_style; + return $this; } public function setAdditionalCSSClasses($new_additional_css_classes) { $this->additional_css_classes = $new_additional_css_classes; + return $this; } public function setMapStyles($newStyles) { $this->jsonMapStyles = $newStyles; + return $this; } public function setDelayLoadMapFunction($newDelay) { $this->delayLoadMapFunction = $newDelay; + return $this; } /** @@ -262,6 +268,7 @@ public function setClusterer($useClusterer, $gridSize=100, $maxZoom=9, $this->gridSize = $gridSize; $this->maxZoom = $maxZoom; $this->clustererLibraryPath = $clustererLibraryPath; + return $this; } /** @@ -274,6 +281,7 @@ public function setClusterer($useClusterer, $gridSize=100, $maxZoom=9, public function setDivId($googleMapId) { $this->googleMapId = $googleMapId; + return $this; } /** @@ -286,10 +294,12 @@ public function setDivId($googleMapId) { public function setDirectionDivId($googleMapDirectionId) { $this->googleMapDirectionId = $googleMapDirectionId; + return $this; } /** - * Set the size of the gmap + * Set the size of the gmap. If these values are not provided + * then CSS is used instead * * @param int $width GoogleMap width * @param int $height GoogleMap height @@ -300,6 +310,7 @@ public function setDirectionDivId($googleMapDirectionId) { public function setSize($width, $height) { $this->width = $width; $this->height = $height; + return $this; } @@ -315,6 +326,7 @@ public function setSize($width, $height) { public function setIconSize($iconWidth, $iconHeight) { $this->iconWidth = $iconWidth; $this->iconHeight = $iconHeight; + return $this; } /** @@ -327,6 +339,7 @@ public function setIconSize($iconWidth, $iconHeight) { public function setLang($lang) { $this->lang = $lang; + return $this; } /** @@ -339,6 +352,7 @@ public function setLang($lang) { public function setZoom($zoom) { $this->zoom = $zoom; + return $this; } /** @@ -351,6 +365,7 @@ public function setZoom($zoom) { public function setInfoWindowZoom($infoWindowZoom) { $this->infoWindowZoom = $infoWindowZoom; + return $this; } /** @@ -363,6 +378,7 @@ public function setInfoWindowZoom($infoWindowZoom) { public function setEnableWindowZoom($enableWindowZoom) { $this->enableWindowZoom = $enableWindowZoom; + return $this; } /** @@ -375,6 +391,7 @@ public function setEnableWindowZoom($enableWindowZoom) { public function setEnableAutomaticCenterZoom($enableAutomaticCenterZoom) { $this->enableAutomaticCenterZoom = $enableAutomaticCenterZoom; + return $this; } /** @@ -387,6 +404,7 @@ public function setEnableAutomaticCenterZoom($enableAutomaticCenterZoom) { public function setCenter($center) { $this->center = $center; + return $this; } /** @@ -415,6 +433,8 @@ public function setMapType($mapType) { $this->MapType = "road"; break; } + + return $this; } /* @@ -422,6 +442,7 @@ public function setMapType($mapType) { */ public function setAllowFullScreen($allowed) { $this->allowFullScreen = $allowed; + return $this; } /** @@ -429,6 +450,7 @@ public function setAllowFullScreen($allowed) { **/ public function setLatLongCenter($center) { $this->latLongCenter = $center; + return $this; } /** @@ -441,6 +463,7 @@ public function setLatLongCenter($center) { public function setDisplayDirectionFields($displayDirectionFields) { $this->displayDirectionFields = $displayDirectionFields; + return $this; } /** @@ -453,6 +476,7 @@ public function setDisplayDirectionFields($displayDirectionFields) { public function setDefaultHideMarker($defaultHideMarker) { $this->defaultHideMarker = $defaultHideMarker; + return $this; } /** @@ -535,7 +559,6 @@ public function geocoding($address) { */ public function addMarkerByCoords($lat, $lng, $html='', $category='', $icon='') { - $iconURL = null; if ($icon) { $iconURL = $icon->getURL(); @@ -549,8 +572,10 @@ public function addMarkerByCoords($lat, $lng, $html='', $category='', $icon='') 'icon' => $iconURL ); array_push($this->markers, $m); + return $this; } + /** * Add marker by his address * @@ -569,6 +594,7 @@ public function addMarkerByAddress($address, $content='', $category='', $icon='' } else { // throw new Exception('Adress not found : '.$address); } + return $this; } /** @@ -585,6 +611,7 @@ public function addArrayMarkerByCoords($coordtab, $category='', $icon='') { foreach ($coordtab as $coord) { $this->addMarkerByCoords($coord[0], $coord[1], $coord[2], $category, $icon); } + return $this; } @@ -619,6 +646,8 @@ public function addMarkerAsObject(ViewableData $obj) { $obj->getMappableMapPin() ); } + + return $this; } @@ -658,6 +687,7 @@ public function addArrayMarkerByAddress($coordtab, $category='', $icon='') { foreach ($coordtab as $coord) { $this->addMarkerByAddress($coord[0], $coord[1], $category, $icon); } + return $this; } /** @@ -684,6 +714,7 @@ public function addDirection($from, $to, $idpanel='') { public function addKML($url) { array_push($this->kmlFiles, $url); + return $this; } @@ -701,6 +732,7 @@ public function addLine($from = array(), $to = array(), $color = "#FF3300") { ); array_push($this->lines, $line); + return $this; } From 90921d9bcc0155ee3f82030af61f5e762fc9aee8 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 3 Apr 2015 23:19:37 +0700 Subject: [PATCH 248/354] MINOR: Whitespace --- code/MappableDataObjectSet.php | 1 - 1 file changed, 1 deletion(-) diff --git a/code/MappableDataObjectSet.php b/code/MappableDataObjectSet.php index db38c93..97667e7 100755 --- a/code/MappableDataObjectSet.php +++ b/code/MappableDataObjectSet.php @@ -16,5 +16,4 @@ public function getRenderableMap($width = null, $height = null) { return $gmap; } - } From 350b80754950b16e961358f3923b19362908a5f1 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 4 Apr 2015 09:54:41 +0700 Subject: [PATCH 249/354] MINOR: Changed initial set up of map to use chaining instead of separate calls --- code/MapExtension.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/MapExtension.php b/code/MapExtension.php index edfdba0..a0ace5a 100644 --- a/code/MapExtension.php +++ b/code/MapExtension.php @@ -136,10 +136,10 @@ public function HasGeo() { Render a map at the provided lat,lon, zoom from the editing functions, */ public function BasicMap() { - $map = $this->owner->getRenderableMap(); - $map->setZoom($this->owner->ZoomLevel); - $map->setAdditionalCSSClasses('fullWidthMap'); - $map->setShowInlineMapDivStyle(true); + $map = $this->owner->getRenderableMap()-> + setZoom($this->owner->ZoomLevel)-> + setAdditionalCSSClasses('fullWidthMap')-> + setShowInlineMapDivStyle(true); // add any KML map layers if (Object::has_extension($this->owner->ClassName, 'MapLayerExtension')) { From 047b9c9ff14c2bea30d91b58e54c60db47aee9e9 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 4 Apr 2015 13:12:58 +0700 Subject: [PATCH 250/354] FIX: Change defaults for new clusterer and ensure those configurable values are passed to the JavaScript generation stage --- code/MapAPI.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index b69f593..458fdf3 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -157,9 +157,9 @@ class MapAPI extends ViewableData /** Use clusterer to display a lot of markers on the gmap **/ protected $useClusterer = false; - protected $gridSize = 100; - protected $maxZoom = 9; - protected $clustererLibraryPath = '/mappable/javascript/Fluster2.packed.js'; + protected $gridSize = 50; + protected $maxZoom = 17; + protected $clustererLibraryPath = "/mappable/javascript/google/markerclusterer.js"; /** Enable automatic center/zoom **/ protected $enableAutomaticCenterZoom = false; @@ -262,8 +262,8 @@ public function setDelayLoadMapFunction($newDelay) { * @return void */ - public function setClusterer($useClusterer, $gridSize=100, $maxZoom=9, - $clustererLibraryPath='mappable/javascript/Fluster2.packed.js') { + public function setClusterer($useClusterer, $gridSize=50, $maxZoom=17, + $clustererLibraryPath='/mappable/javascript/google/markerclusterer.js') { $this->useClusterer = $useClusterer; $this->gridSize = $gridSize; $this->maxZoom = $maxZoom; @@ -841,6 +841,8 @@ public function generate() { 'UseClusterer'=>$this->useClusterer, 'DownloadJS' => !(self::$include_download_javascript), 'ClustererLibraryPath' => $this->clustererLibraryPath, + 'ClustererMaxZoom' => $this->maxZoom, + 'ClustererGridSize' => $this->gridSize, 'Lines' => $linesJson, 'KmlFiles' => $kmlJson, 'AllowFullScreen' => $this->allowFullScreen, From d64a2c8209d797ed2c51fb0234b03cb6d46f240e Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 4 Apr 2015 13:13:31 +0700 Subject: [PATCH 251/354] FIX: Ensure cluster grid size and max zoom are passed to the actual point where clustering is defined --- javascript/google/MapGoogleJS.ss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/javascript/google/MapGoogleJS.ss b/javascript/google/MapGoogleJS.ss index 732d051..df54c5f 100644 --- a/javascript/google/MapGoogleJS.ss +++ b/javascript/google/MapGoogleJS.ss @@ -16,6 +16,8 @@ var options = { kmlfiles: $KmlFiles, mapstyles: $JsonMapStyles, useclusterer: $UseClusterer, + clusterergridsize: $ClustererGridSize, + clusterermaxzoom: $ClustererMaxZoom, enableautocentrezoom: $EnableAutomaticCenterZoom } From 5059f654b4aa3157b154aa981e75750c8ef29189 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 4 Apr 2015 13:14:00 +0700 Subject: [PATCH 252/354] FIX: Use previously missing map clusterer values --- javascript/google/maputil.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index 8d977bf..dfe4eb2 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -372,7 +372,7 @@ function loadedGoogleMapsAPI() { map.setMapTypeId(googlemaptype); if (map_info.useClusterer) { - var mcOptions = {gridSize: 50, maxZoom: 17}; + var mcOptions = {gridSize: options.clusterergridsize, maxZoom: options.clusterermaxzoom}; var markerCluster = new MarkerClusterer(map,markers,mcOptions); } From b88375c04a9f2be3e7e6016dc0bcbc18aacc2524 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 7 Apr 2015 14:28:17 +0700 Subject: [PATCH 253/354] ENHANCEMENT: Addition of map icon for reports menu --- code/PointsOfInterestAdmin.php | 1 + 1 file changed, 1 insertion(+) diff --git a/code/PointsOfInterestAdmin.php b/code/PointsOfInterestAdmin.php index b12c50e..fbc6447 100644 --- a/code/PointsOfInterestAdmin.php +++ b/code/PointsOfInterestAdmin.php @@ -4,6 +4,7 @@ class PointsOfInterestAdmin extends ModelAdmin { private static $managed_models = array('PointsOfInterestLayer','PointOfInterest'); private static $url_segment = 'poi'; private static $menu_title = 'Points of Interest'; + private static $menu_icon = '/mappable/icons/menuicon.png'; static $has_one = array( 'DefaultIcon' => 'Image' From f29216649c54d99ca7fb2fbf8f6b5a4d7c2b2233 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 7 Apr 2015 14:28:38 +0700 Subject: [PATCH 254/354] MINOR: Attribution of icon note --- docs/Credits.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 docs/Credits.md diff --git a/docs/Credits.md b/docs/Credits.md new file mode 100644 index 0000000..1bd359e --- /dev/null +++ b/docs/Credits.md @@ -0,0 +1,6 @@ +# Credits + +## Map Menu Icons +Sourced from http://www.flaticon.com/free-icon/map-location_32364 under the Creative Commons License. + +
Icon made by Freepik from www.flaticon.com is licensed under CC BY 3.0
From 94a6bc3ff28817b428c0ca4bf51fc0c6f9d088a0 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 7 Apr 2015 14:29:06 +0700 Subject: [PATCH 255/354] MINOR: Icon for menu of points of interest --- icons/menuicon.png | Bin 0 -> 420 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 icons/menuicon.png diff --git a/icons/menuicon.png b/icons/menuicon.png new file mode 100644 index 0000000000000000000000000000000000000000..2c4d4aeaa844bfcb43998ab26107ae389c003693 GIT binary patch literal 420 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6n2Mb|LpV4>-?)JUISV`@i-B}0 z2s1uD#GVRdNS3%plmzFem6RtIr81P4m+NKbWfvzW7NqLs7p2dBXCuYHz^Lr$;uswTMQElJh^VO4XyzHF6?aYm-DxNBZ=kqVi{Yq8yoaFIn_st{D(Vml9nmgs6 z30>aS^Yh8v*=@ph)$ezuZ#z5l>uv7voEr~U@p}6i8$4;_tXKWtbEHT}+0Nj3K~mhN z2@!rm%Pgks-7ybG0F4z9O zWsRKXb4zYFtl#r%8H9HeP4&!n&xe Date: Thu, 9 Apr 2015 15:46:48 +0700 Subject: [PATCH 256/354] ENHANCEMENT: Addition of parameter to allow top level values to be passed down to the infomration windows rendered when map icons are clicked --- code/MapAPI.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index 458fdf3..a977cab 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -618,10 +618,11 @@ public function addArrayMarkerByCoords($coordtab, $category='', $icon='') { /** * Adds a {@link ViewableData} object that implements {@link Mappable} * to the map. + * @param $infowindowtemplateparams Optional array of extra parameters to pass to the map info window * * @param ViewableData $obj */ - public function addMarkerAsObject(ViewableData $obj) { + public function addMarkerAsObject(ViewableData $obj, $infowindowtemplateparams = null) { $extensionsImplementMappable = false; $extensions = Object::get_extensions(get_class($obj)); @@ -638,6 +639,11 @@ public function addMarkerAsObject(ViewableData $obj) { ) { //if(($obj->getMappableLatitude() > 0) || ($obj->getMappableLongitude() > 0)) { $cat = $obj->hasMethod('getMappableMapCategory') ? $obj->getMappableMapCategory() : "default"; + if ($infowindowtemplateparams !== null) { + foreach ($infowindowtemplateparams as $key => $value) { + $obj->{$key} = $value; + } + } $this->addMarkerByCoords( $obj->getMappableLatitude(), $obj->getMappableLongitude(), From cad836b445c2659ce01467031192ac628c364803 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 9 Apr 2015 15:47:07 +0700 Subject: [PATCH 257/354] ENHANCEMENT: Addition of parameter to allow top level values to be passed down to the infomration windows rendered when map icons are clicked --- code/MapUtil.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/MapUtil.php b/code/MapUtil.php index 5064d72..dd80d33 100644 --- a/code/MapUtil.php +++ b/code/MapUtil.php @@ -219,13 +219,13 @@ public static function sanitize($content) { * @param SS_List $set * @return MapAPI */ - public static function get_map(SS_List $list) { + public static function get_map(SS_List $list, $optionalinfowindowtemplatevalues) { $gmap = self::instance(); if($list) { $arr = $list->toArray(); foreach ($arr as $mappable) { if ($mappable->MapPinEdited) { - $gmap->addMarkerAsObject($mappable); + $gmap->addMarkerAsObject($mappable, $optionalinfowindowtemplatevalues); } } } From c0040ab4cc860e1307d82687e784efd7812961ed Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 9 Apr 2015 15:49:17 +0700 Subject: [PATCH 258/354] ENHANCEMENT: One can now pass parameters to a Mappable or MappableDataObjectSet to be rendered in the map information windows --- code/MappableData.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/code/MappableData.php b/code/MappableData.php index 41d35d0..a0556e3 100644 --- a/code/MappableData.php +++ b/code/MappableData.php @@ -8,8 +8,24 @@ */ class MappableData extends Extension { + /** + * Optional template values for the map info windows + */ + private $MarkerTemplateValues = null; + + /** + * Pass through values to the markers so that when rendering the map info windows, these + * parameters are available to the template. This is of course optional + * + * @param array $values hash array of template key to template value + */ + public function setMarkerTemplateValues($values) { + $this->MarkerTemplateValues = $values; + } + + public function getRenderableMap($width = null, $height = null, $zoom = 9) { - $gmap = MapUtil::get_map(new ArrayList(array($this->owner))); + $gmap = MapUtil::get_map(new ArrayList(array($this->owner)), $this->MarkerTemplateValues); $w = $width ? $width : MapUtil::$map_width; $h = $height ? $height : MapUtil::$map_height; $gmap->setSize($w,$h); From 4b598ac5cbf786b1d17eaba5727e831b1a385c1f Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 9 Apr 2015 15:49:33 +0700 Subject: [PATCH 259/354] ENHANCEMENT: One can now pass parameters to a Mappable or MappableDataObjectSet to be rendered in the map information windows --- code/MappableDataObjectSet.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/code/MappableDataObjectSet.php b/code/MappableDataObjectSet.php index 97667e7..3a537e1 100755 --- a/code/MappableDataObjectSet.php +++ b/code/MappableDataObjectSet.php @@ -8,8 +8,23 @@ */ class MappableDataObjectSet extends Extension { + /** + * Optional template values for the map info windows + */ + private $MarkerTemplateValues = null; + + /** + * Pass through values to the markers so that when rendering the map info windows, these + * parameters are available to the template. This is of course optional + * + * @param array $values hash array of template key to template value + */ + public function setMarkerTemplateValues($values) { + $this->MarkerTemplateValues = $values; + } + public function getRenderableMap($width = null, $height = null) { - $gmap = MapUtil::get_map($this->owner); + $gmap = MapUtil::get_map($this->owner, $this->MarkerTemplateValues); $w = $width ? $width : MapUtil::$map_width; $h = $height ? $height : MapUtil::$map_height; $gmap->setSize($w,$h); From 2e0993a8c0ef5cba17a24cde89165e01ef1c44e3 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 16 Apr 2015 16:37:36 +0700 Subject: [PATCH 260/354] ENHANCEMENT: Added default sort order of name, and extra summary fields of latitude and longitude --- code/PointOfInterest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/PointOfInterest.php b/code/PointOfInterest.php index 7c9a9b3..873027b 100644 --- a/code/PointOfInterest.php +++ b/code/PointOfInterest.php @@ -9,7 +9,9 @@ class PointOfInterest extends DataObject { 'Name' => 'Varchar' ); - private static $summary_fields = array('Name'); + private static $summary_fields = array('Name','Lat','Lon'); + + private static $default_sort = 'Name'; function getCMSFields() { $fields = parent::getCMSFields(); From 1dedc75882e0363e435008c180533283cb79e8cc Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 16 Apr 2015 17:02:22 +0700 Subject: [PATCH 261/354] FIX: Make sure that icons are passed around as strings in the case of using a common icon from the layer --- code/MapAPI.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index a977cab..ad085ec 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -559,17 +559,12 @@ public function geocoding($address) { */ public function addMarkerByCoords($lat, $lng, $html='', $category='', $icon='') { - $iconURL = null; - if ($icon) { - $iconURL = $icon->getURL(); - } - $m = array( 'latitude' => $lat, 'longitude' => $lng, 'html' => $html, 'category' => $category, - 'icon' => $iconURL + 'icon' => $icon ); array_push($this->markers, $m); return $this; From 59c7076eac6fbed40ced482efa47703881b69adc Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 16 Apr 2015 17:03:08 +0700 Subject: [PATCH 262/354] FIX: Ensure that strings are used for the icon --- code/MapExtension.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/MapExtension.php b/code/MapExtension.php index a0ace5a..f0fbfed 100644 --- a/code/MapExtension.php +++ b/code/MapExtension.php @@ -103,9 +103,9 @@ public function getMappableMapPin() { $mappin = $this->owner->MapPinIcon(); $result = $mappin->getAbsoluteURL(); } else { - // check for a cached map pin already having been provided - if ($this->owner->CachedMapPin) { - $result = $this->owner->CachedMapPin; + // check for a cached map pin already having been provided for the layer + if ($this->owner->CachedMapPinURL) { + $result = $this->owner->CachedMapPinURL; } } return $result; @@ -159,7 +159,7 @@ public function BasicMap() { foreach ($layer->PointsOfInterest() as $poi) { if ($poi->MapPinEdited) { if ($poi->MapPinIconID == 0) { - $poi->CachedMapPin = $layericon; + $poi->CachedMapPinURL = $layericon->getAbsoluteURL(); } $map->addMarkerAsObject($poi); } From f595f8593a187846b48174e4cf2009a1af177c4a Mon Sep 17 00:00:00 2001 From: Werner Krauss Date: Thu, 16 Apr 2015 15:07:29 +0200 Subject: [PATCH 263/354] do not use window.onload - can be overwritten later also fixed a small typo... --- templates/Includes/GoogleJavaScript.ss | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/templates/Includes/GoogleJavaScript.ss b/templates/Includes/GoogleJavaScript.ss index 9db04cf..184c263 100644 --- a/templates/Includes/GoogleJavaScript.ss +++ b/templates/Includes/GoogleJavaScript.ss @@ -6,10 +6,12 @@ function loadScript() { var script = document.createElement('script'); script.type = 'text/javascript'; script.src = 'https://maps.googleapis.com/maps/api/js?' + - '&ensor=false&callback=loadedGoogleMapsAPI&hl=en'; + '&sensor=false&callback=loadedGoogleMapsAPI&hl=en'; document.body.appendChild(script); } -window.onload = loadScript; +window.addEventListener ? + window.addEventListener("load",loadScript,false) : +window.attachEvent && window.attachEvent("onload",loadScript); <% if $UseCompressedAssets %> <% require javascript("mappable/javascript/google/mappablegoogle.min.js") %> From 426fc94416acdf7e570b10a746f3a9b8f479c222 Mon Sep 17 00:00:00 2001 From: Werner Krauss Date: Fri, 17 Apr 2015 10:20:16 +0200 Subject: [PATCH 264/354] some refinement to filter dataobjects that have mappable interface implemented or MapExtension and MapPinEdited --- code/MapUtil.php | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/code/MapUtil.php b/code/MapUtil.php index dd80d33..c96de72 100644 --- a/code/MapUtil.php +++ b/code/MapUtil.php @@ -222,13 +222,34 @@ public static function sanitize($content) { public static function get_map(SS_List $list, $optionalinfowindowtemplatevalues) { $gmap = self::instance(); if($list) { - $arr = $list->toArray(); - foreach ($arr as $mappable) { - if ($mappable->MapPinEdited) { + foreach ($list as $mappable) { + if (self::ChooseToAddDataobject($mappable)) { $gmap->addMarkerAsObject($mappable, $optionalinfowindowtemplatevalues); } } } return $gmap; } + + /** + * Determines if the current DataObject should be included to the map + * Checks if it has Mappable interface implemented + * If it has MapExtension included, the value of MapPinEdited is also checked + * + * @param DataObject $do + * @return bool + */ + private static function ChooseToAddDataobject(DataObject $do) { + $isMappable = $do->is_a('Mappable'); + + foreach($do->getExtensionInstances() as $extension) { + $isMappable = $isMappable || $extension instanceof Mappable; + } + + $filterMapPinEdited = $do->hasExtension('MapExtension') + ? $do->MapPinEdited + : true; + + return $isMappable && $filterMapPinEdited ; + } } From 2133486bbba7db8b95319a7d8f57f5764aefd9a6 Mon Sep 17 00:00:00 2001 From: Werner Krauss Date: Mon, 20 Apr 2015 15:42:20 +0200 Subject: [PATCH 265/354] StaticMap update - make default zoom and static maps url configurable - use getMappableMarkerPin() for custom pin --- code/MappableData.php | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/code/MappableData.php b/code/MappableData.php index a0556e3..d965a6d 100644 --- a/code/MappableData.php +++ b/code/MappableData.php @@ -13,6 +13,18 @@ class MappableData extends Extension { */ private $MarkerTemplateValues = null; + /** + * URL of static maps api + * @var string + */ + private static $staticmap_api_url = '//maps.googleapis.com/maps/api/staticmap'; + + /** + * Default zoom for static map + * @var int + */ + private static $staticmap_default_zoom = 13; + /** * Pass through values to the markers so that when rendering the map info windows, these * parameters are available to the template. This is of course optional @@ -45,14 +57,39 @@ public function getRenderableMap($width = null, $height = null, $zoom = 9) { } + /** + * returns an with a src set to a static map picture + * + * You can use MappableData.staticmap_api_url config var to set the domain of the static map. + * You can use MappableData.staticmap_default_zoom config var to set the default zoom for the static map. + * + * @uses Mappable::getMappableMapPin() to draw a special marker, be sure this image is public available + * + * @param null $width + * @param null $height + * @return string + */ public function StaticMap($width = null, $height = null) { $w = $width ? $width : MapUtil::$map_width; $h = $height ? $height : MapUtil::$map_height; $lat = $this->owner->getMappableLatitude(); $lng = $this->owner->getMappableLongitude(); - $url = "//maps.google.com/maps/api/staticmap?center=$lat,$lng&markers=$lat,". - "$lng&zoom=13&size=${w}x$h&sensor=false"; - $src = htmlentities($url); + $pin = $this->owner->getMappableMapPin(); + + $apiurl = Config::inst()->get('MappableData', 'staticmap_api_url'); + + $urlparts = array( + 'center' => "$lat,$lng", + 'markers' => "$lat,$lng", + 'zoom' => Config::inst()->get('MappableData', 'staticmap_default_zoom'), + 'size' => "${w}x$h", + 'sensor' => 'false' //@todo: make sensor param configurable + ); + if ($pin) { + $urlparts['markers'] = "icon:$pin|$lat,$lng"; + } + + $src = htmlentities($apiurl . '?' . http_build_query($urlparts)); return ''.$this->owner->Title.''; } From 1aa7c9f330cf1b389eaabe2eb14fbe0b029db738 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 20 Apr 2015 21:45:03 +0700 Subject: [PATCH 266/354] FIX: Deal gracefully with both the marker and the layer having no icon --- code/MapExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/MapExtension.php b/code/MapExtension.php index f0fbfed..1b64777 100644 --- a/code/MapExtension.php +++ b/code/MapExtension.php @@ -158,7 +158,7 @@ public function BasicMap() { } foreach ($layer->PointsOfInterest() as $poi) { if ($poi->MapPinEdited) { - if ($poi->MapPinIconID == 0) { + if ($poi->MapPinIconID == 0 && $layericon) { $poi->CachedMapPinURL = $layericon->getAbsoluteURL(); } $map->addMarkerAsObject($poi); From e675f0bb3b320adb5f46d5950438455a38dc3284 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 20 Apr 2015 21:46:41 +0700 Subject: [PATCH 267/354] FIX: Guidepoints were assuming objects, no array --- code/LatLongField.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/LatLongField.php b/code/LatLongField.php index 16ff028..aff90f6 100755 --- a/code/LatLongField.php +++ b/code/LatLongField.php @@ -90,10 +90,10 @@ public function FieldHolder($properties = array()) { $guidePointsJSON = ''; if (isset($this->guidePoints)) { $latlongps = array(); - foreach ($this->guidePoints as $guidepoint) { - array_push($latlongps, array('latitude' => $guidepoint->Lat, 'longitude' => $guidepoint->Lon)); - } + foreach ($this->guidePoints as $guidepoint) { + array_push($latlongps, $guidepoint); + } $guidePointsJSON = json_encode($latlongps); // convert the mappable guidepoints to lat lon From 8491d0319aa4b5c695d4186e93e9005296f24b83 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 23 Apr 2015 08:10:20 +0700 Subject: [PATCH 268/354] FIX: Ensure that JS markers can be cached --- code/MapAPI.php | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index ad085ec..8f7fcaf 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -820,6 +820,8 @@ public function generate() { $this->allowFullScreen = 'false'; } + + $vars = new ArrayData(array( 'JsonMapStyles' => $this->jsonMapStyles, 'AdditionalCssClasses' => $this->additional_css_classes, @@ -851,17 +853,13 @@ public function generate() { ) ); - // HTML component of the map - $this->content = $this->processTemplateHTML('Map', $vars); - + // JavaScript required to prime the map $javascript = $this->processTemplateJS('Map', $vars); + $vars->setField('JavaScript', $javascript); - Requirements::customScript(<<content = $this->processTemplateHTML('Map', $vars); + } function processTemplateJS($templateName, $templateVariables = null) { if (!$templateVariables) { From b1ebbf7c7c1646a6c374e5f4a4cb44bc4690774c Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 23 Apr 2015 08:10:58 +0700 Subject: [PATCH 269/354] FIX: Do not defer priming of map as it does not work when using partial caching --- code/shortcodes/GoogleMapShortCodeHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/shortcodes/GoogleMapShortCodeHandler.php b/code/shortcodes/GoogleMapShortCodeHandler.php index f66518f..489b496 100644 --- a/code/shortcodes/GoogleMapShortCodeHandler.php +++ b/code/shortcodes/GoogleMapShortCodeHandler.php @@ -56,7 +56,7 @@ public static function parse_googlemap($arguments, $caption = null, $parser = nu $customised = array_merge($defaults, $arguments); // include JavaScript to be appended at the end of the page, namely params for map rendering - Requirements::javascriptTemplate("mappable/javascript/google/map.google.template.js", $customised); + //Requirements::javascriptTemplate("mappable/javascript/google/map.google.template.js", $customised); //get map view template and render the HTML $template = new SSViewer('GoogleMapShortCode'); From 87136ef7122032c27130bd09b07a992793d63df7 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 23 Apr 2015 08:11:15 +0700 Subject: [PATCH 270/354] FIX: Do not defer priming of streetview as it does not work when using partial caching --- code/shortcodes/GoogleStreetViewShortCodeHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/shortcodes/GoogleStreetViewShortCodeHandler.php b/code/shortcodes/GoogleStreetViewShortCodeHandler.php index 48f2bbe..f5041d0 100644 --- a/code/shortcodes/GoogleStreetViewShortCodeHandler.php +++ b/code/shortcodes/GoogleStreetViewShortCodeHandler.php @@ -60,7 +60,7 @@ public static function parse_googlestreetview($arguments, $caption = null, $pars $customised = array_merge($defaults, $arguments); // Include google maps JS at the end of the page - Requirements::javascriptTemplate("mappable/javascript/google/streetview.google.template.js", $customised); + //Requirements::javascriptTemplate("mappable/javascript/google/streetview.google.template.js", $customised); //get streetview template template From 03e8b23de606adbf100106928d2a31067a736a80 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 23 Apr 2015 08:11:44 +0700 Subject: [PATCH 271/354] FIX: Move JavaScript for priming map where it is not broken by using partial caching --- javascript/google/MapGoogleHTML.ss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/javascript/google/MapGoogleHTML.ss b/javascript/google/MapGoogleHTML.ss index 78a869f..7dbea7a 100644 --- a/javascript/google/MapGoogleHTML.ss +++ b/javascript/google/MapGoogleHTML.ss @@ -2,3 +2,6 @@
style="width:{$Width}; height: {$Height};" <% end_if %><% if AdditionalCssClasses %> class="$AdditionalCssClasses"<% end_if %>>
+ From 93fd17982bc41984896f90a1cb030577d2de9bda Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 23 Apr 2015 08:12:53 +0700 Subject: [PATCH 272/354] MINOR: Use unique name for each options variable --- javascript/google/MapGoogleJS.ss | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/javascript/google/MapGoogleJS.ss b/javascript/google/MapGoogleJS.ss index df54c5f..a3800da 100644 --- a/javascript/google/MapGoogleJS.ss +++ b/javascript/google/MapGoogleJS.ss @@ -5,7 +5,7 @@ var gmarkers = []; var mapLayers = []; var mapLines = []; <% end_if %> -var options = { +var options_$GoogleMapID = { centre: $LatLngCentre, zoom: $Zoom, maptype: '$MapType', @@ -20,5 +20,4 @@ var options = { clusterermaxzoom: $ClustererMaxZoom, enableautocentrezoom: $EnableAutomaticCenterZoom } - -registerMap(options); +registerMap(options_$GoogleMapID); From 44b04b0f0a849f652d2e6d88d94d38f29ef77948 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 23 Apr 2015 08:13:15 +0700 Subject: [PATCH 273/354] FIX: Move register methods into inline JavaScript --- javascript/google/maputil.js | 76 +----------------------------------- 1 file changed, 1 insertion(+), 75 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index dfe4eb2..70c7a66 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -1,9 +1,3 @@ -/** - * Counter used for uniquely identifying each Google map - * @type {Number} - */ -var mappableMapCtr = 0; - /** * Create a google map pin from data provided * @param {GoogleMap} map Instance of a google map @@ -189,74 +183,6 @@ function convertMapType(mapTypeName) { } -/** - * Register a short code generated map, namely storing parameters for later rendering - * once google maps API loaded - * @param array options lat,lon,zoom,type - */ -function registerShortcodeMap(options) { - newMap = []; - newMap.latitude = options.latitude; - newMap.longitude = options.longitude; - newMap.zoom = options.zoom; - newMap.maptype = options.maptype; - newMap.allowfullscreen = options.allowfullscreen; - newMap.caption = options.caption; - newMap.domid = options.domid; - shortcodeMaps.push(newMap); -} - -/** - * Register a short code generated streetview, namely storing parameters for later rendering - * once google maps API loaded - * @param array options lat,lon,zoom,pitch,heading,caption,domid - */ -function registerStreetView(options) { - newView = []; - newView.latitude = options.latitude; - newView.longitude = options.longitude; - newView.zoom = options.zoom; - newView.pitch = options.pitch; - newView.heading = options.heading; - newView.caption = options.caption; - newView.domid = options.domid; - shortcodeStreetview.push(newView); -} - - -//function registerMap(googleMapID, centreCoordinates, zoom, mapType, -// markers, lines, kmlFiles, jsonMapStyles, enableAutomaticCenterZoom, useClusterer, -// allowFullScreen) { - -function registerMap(options) { - var newMap = []; - newMap.googleMapID = options.domid; - newMap.zoom = options.zoom; - newMap.centreCoordinates = options.centre; - - newMap.markers = options.mapmarkers; - newMap.mapType = options.maptype; - newMap.lines = options.lines; - newMap.kmlFiles = options.kmlfiles; - newMap.jsonMapStyles = options.mapstyles; - newMap.enableAutomaticCenterZoom = options.enableautocentrezoom; - newMap.useClusterer = options.useclusterer; - newMap.allowFullScreen = options.allowfullscreen; - var googleMapID = options.domid; - mappableMaps[googleMapID] = newMap; - - // increment map counter - mappableMapCtr++; - - // initialise gmarkers array for this map - gmarkers[googleMapID] = []; - - - mapLayers[googleMapID] = options.kmlfiles; - mapLines[googleMapID] = options.lines; -} - - /** * After the Google Maps API has been loaded init the relevant Google maps */ @@ -372,7 +298,7 @@ function loadedGoogleMapsAPI() { map.setMapTypeId(googlemaptype); if (map_info.useClusterer) { - var mcOptions = {gridSize: options.clusterergridsize, maxZoom: options.clusterermaxzoom}; + var mcOptions = {gridSize: map_info.clustererGridSize, maxZoom: map_info.clustererMaxZoom}; var markerCluster = new MarkerClusterer(map,markers,mcOptions); } From 0c3b40c00031a1e39b6d50d94ce9e7cc9f9a1066 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 23 Apr 2015 08:13:30 +0700 Subject: [PATCH 274/354] FIX: Moved register methods into inline JavaScript --- templates/Includes/GoogleJavaScript.ss | 57 ++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/templates/Includes/GoogleJavaScript.ss b/templates/Includes/GoogleJavaScript.ss index 184c263..89f75cf 100644 --- a/templates/Includes/GoogleJavaScript.ss +++ b/templates/Includes/GoogleJavaScript.ss @@ -2,7 +2,63 @@ <% if DelayLoadMapFunction %> <% else %> From b45c0b5bb30bf1d1efe6098c28162e19618cec41 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 23 Apr 2015 08:14:28 +0700 Subject: [PATCH 276/354] FIX: Register streetview map in a manner that is not broken by partial caching --- templates/Includes/GoogleStreetView.ss | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/templates/Includes/GoogleStreetView.ss b/templates/Includes/GoogleStreetView.ss index e4523be..10ea1f2 100644 --- a/templates/Includes/GoogleStreetView.ss +++ b/templates/Includes/GoogleStreetView.ss @@ -3,3 +3,14 @@
<% if $Caption %>

$Caption

<% end_if %>
+ From 22e83445954ccaf1a5b68f04d38bc30bbc2d5f28 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 23 Apr 2015 09:42:44 +0700 Subject: [PATCH 277/354] MINOR: Remove debug --- templates/Includes/GoogleJavaScript.ss | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/Includes/GoogleJavaScript.ss b/templates/Includes/GoogleJavaScript.ss index 89f75cf..08a3a75 100644 --- a/templates/Includes/GoogleJavaScript.ss +++ b/templates/Includes/GoogleJavaScript.ss @@ -58,7 +58,6 @@ function registerMap(options) { } function loadScript() { - console.log("Calling load script"); var script = document.createElement('script'); script.type = 'text/javascript'; script.src = 'https://maps.googleapis.com/maps/api/js?' + From 6e77f3a32a04c5d69a0aa06810185a68a7b65e21 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 23 Apr 2015 10:56:56 +0700 Subject: [PATCH 278/354] MINOR: Changed name of set map styles to set map style as it made more semantic sense --- code/MapAPI.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index ad085ec..9eec2f4 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -237,7 +237,7 @@ public function setAdditionalCSSClasses($new_additional_css_classes) { } - public function setMapStyles($newStyles) { + public function setMapStyle($newStyles) { $this->jsonMapStyles = $newStyles; return $this; } From f98988d939750749903771a6ad759a84b688eef5 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 23 Apr 2015 10:57:14 +0700 Subject: [PATCH 279/354] FIX: Resurrect map styles --- javascript/google/maputil.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index dfe4eb2..d3467a3 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -335,10 +335,10 @@ function loadedGoogleMapsAPI() { // initialise geocoder geocoder = new google.maps.Geocoder(); - // TODO - // if (map_info.jsonMapStyles) { - // map.setOptions({styles: map_info.jsonMapStyles}); - // }; + // default of [] renders google maps as per normal + if (map_info.jsonMapStyles) { + map.setOptions({styles: map_info.jsonMapStyles}); + }; if (map_info.allowFullScreen) { map.controls[google.maps.ControlPosition.TOP_RIGHT].push( From 51ca211e092ed418128a501d28a72dd96eba71e2 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 23 Apr 2015 10:58:20 +0700 Subject: [PATCH 280/354] ENHANCEMENT: Documentation re map styles --- docs/en/MayStyles.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 docs/en/MayStyles.md diff --git a/docs/en/MayStyles.md b/docs/en/MayStyles.md new file mode 100644 index 0000000..6b22662 --- /dev/null +++ b/docs/en/MayStyles.md @@ -0,0 +1,38 @@ +#Map Styles +The color of a rendered Google map can be changed by setting style parameters in JSON format. For +background on this see https://developers.google.com/maps/documentation/javascript/styling + +``` + $map->setMapStyle( $jsonStyle ); +``` + +A good source of styles to get started with is https://snazzymaps.com/explore, a worked example +appears below. The only difference with normal map rendering is the addition of the setStyle call. + +```php +public function Map() { + $stations = OpenWeatherMapStation::get(); + $vars = array( + 'Link' => $this->Link() + ); + $stations->setMarkerTemplateValues($vars); + $map = $stations->getRenderableMap()-> + setZoom($this->owner->ZoomLevel)-> + setAdditionalCSSClasses('fullWidthMap'); + $map->setEnableAutomaticCenterZoom(true); + $map->setZoom(10); + $map->setShowInlineMapDivStyle(true); + $map->setClusterer(true); + $map->CurrentURL = $this->Link(); + + $style = '[{"featureType":"landscape","stylers":[{"saturation":-100},{"lightness":65},{"visibility":"on"}]},{"featureType":"poi","stylers":[{"saturation":-100},{"lightness":51},{"visibility":"simplified"}]},{"featureType":"road.highway","stylers":[{"saturation":-100},{"visibility":"simplified"}]},{"featureType":"road.arterial","stylers":[{"saturation":-100},{"lightness":30},{"visibility":"on"}]},{"featureType":"road.local","stylers":[{"saturation":-100},{"lightness":40},{"visibility":"on"}]},{"featureType":"transit","stylers":[{"saturation":-100},{"visibility":"simplified"}]},{"featureType":"administrative.province","stylers":[{"visibility":"off"}]},{"featureType":"water","elementType":"labels","stylers":[{"visibility":"on"},{"lightness":-25},{"saturation":-100}]},{"featureType":"water","elementType":"geometry","stylers":[{"hue":"#ffff00"},{"lightness":-25},{"saturation":-97}]}]'; + + $map->setMapStyle($style); + + return $map; + } +``` + +![Styled Map] +(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/styledmap.png?raw=true +Styled Map") From e0c7d7916926f988e8f42bceffd2177d1245e160 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 23 Apr 2015 11:23:59 +0700 Subject: [PATCH 281/354] MINOR: Re minify of JavaScript files --- javascript/google/mappablegoogle.min.js | 2 +- javascript/mapField.min.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/google/mappablegoogle.min.js b/javascript/google/mappablegoogle.min.js index beabad0..32e67e1 100644 --- a/javascript/google/mappablegoogle.min.js +++ b/javascript/google/mappablegoogle.min.js @@ -1 +1 @@ -function FullScreenControl(a,b,c){void 0===b&&(b=null),void 0===c&&(c=null),null==b&&(b="Full screen"),null==c&&(c="Exit full screen");var d=document.createElement("div");d.className="fullScreen",d.index=1,d.style.padding="5px";var e=document.createElement("div");e.style.backgroundColor="white",e.style.borderStyle="solid",e.style.borderWidth="1px",e.style.borderColor="#717b87",e.style.cursor="pointer",e.style.textAlign="center",e.style.boxShadow="rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px",d.appendChild(e);var f=document.createElement("div");f.style.fontFamily="Roboto,Arial,sans-serif",f.style.fontSize="11px",f.style.fontWeight="400",f.style.paddingTop="1px",f.style.paddingBottom="1px",f.style.paddingLeft="6px",f.style.paddingRight="6px",f.innerHTML=""+b+"",e.appendChild(f);var g=document.getElementsByTagName("head")[0],h=document.createElement("style");h.setAttribute("type","text/css"),h.setAttribute("media","print");var i=".fullScreen { display: none;}",j=document.createTextNode(i);try{h.appendChild(j)}catch(k){h.styleSheet.cssText=i}g.appendChild(h);var l,m=!1,n=a.getDiv(),o=n.style;n.runtimeStyle&&(o=n.runtimeStyle);var p=o.position,q=o.width,r=o.height;""===q&&(q=n.style.width),""===r&&(r=n.style.height);var s=o.top,t=o.left,u=o.zIndex,v=document.body.style;document.body.runtimeStyle&&(v=document.body.runtimeStyle);var w=v.overflow;return d.goFullScreen=function(){var b=a.getCenter();n.style.position="fixed",n.style.width="100%",n.style.height="100%",n.style.top="0",n.style.left="0",n.style.zIndex="100",document.body.style.overflow="hidden",f.innerHTML=""+c+"",m=!0,google.maps.event.trigger(a,"resize"),a.setCenter(b),l=setInterval(function(){"fixed"!==n.style.position&&(n.style.position="fixed",google.maps.event.trigger(a,"resize"))},100)},d.exitFullScreen=function(){var c=a.getCenter();n.style.position=""===p?"relative":p,n.style.width=q,n.style.height=r,n.style.top=s,n.style.left=t,n.style.zIndex=u,document.body.style.overflow=w,f.innerHTML=""+b+"",m=!1,google.maps.event.trigger(a,"resize"),a.setCenter(c),clearInterval(l)},google.maps.event.addDomListener(e,"click",function(){m?d.exitFullScreen():d.goFullScreen()}),document.onkeydown=function(a){a=a||window.event,27==a.keyCode&&m&&d.exitFullScreen()},d}function MarkerClusterer(a,b,c){this.extend(MarkerClusterer,google.maps.OverlayView),this.map_=a,this.markers_=[],this.clusters_=[],this.sizes=[53,56,66,78,90],this.styles_=[],this.ready_=!1;var d=c||{};this.gridSize_=d.gridSize||60,this.minClusterSize_=d.minimumClusterSize||2,this.maxZoom_=d.maxZoom||null,this.styles_=d.styles||[],this.imagePath_=d.imagePath||this.MARKER_CLUSTER_IMAGE_PATH_,this.imageExtension_=d.imageExtension||this.MARKER_CLUSTER_IMAGE_EXTENSION_,this.zoomOnClick_=!0,void 0!=d.zoomOnClick&&(this.zoomOnClick_=d.zoomOnClick),this.averageCenter_=!1,void 0!=d.averageCenter&&(this.averageCenter_=d.averageCenter),this.setupStyles_(),this.setMap(a),this.prevZoom_=this.map_.getZoom();var e=this;google.maps.event.addListener(this.map_,"zoom_changed",function(){var a=e.map_.getZoom();e.prevZoom_!=a&&(e.prevZoom_=a,e.resetViewport())}),google.maps.event.addListener(this.map_,"idle",function(){e.redraw()}),b&&b.length&&this.addMarkers(b,!1)}function Cluster(a){this.markerClusterer_=a,this.map_=a.getMap(),this.gridSize_=a.getGridSize(),this.minClusterSize_=a.getMinClusterSize(),this.averageCenter_=a.isAverageCenter(),this.center_=null,this.markers_=[],this.bounds_=null,this.clusterIcon_=new ClusterIcon(this,a.getStyles(),a.getGridSize())}function ClusterIcon(a,b,c){a.getMarkerClusterer().extend(ClusterIcon,google.maps.OverlayView),this.styles_=b,this.padding_=c||0,this.cluster_=a,this.center_=null,this.map_=a.getMap(),this.div_=null,this.sums_=null,this.visible_=!1,this.setMap(this.map_)}function createMarker(a,b,c,d,e,f,g,h,i){mapId=a.getDiv().getAttribute("id");var j=new google.maps.Marker;if(j.setPosition(new google.maps.LatLng(b,c)),j.mycategory=e,f&&""!==f){var k=new google.maps.MarkerImage(f);j.setIcon(k)}return g||j.setMap(a),google.maps.event.addListener(j,"click",function(){h&&a.setCenter(new google.maps.LatLng(b,c),12);var e=infoWindows[mapId];e.setContent(d),e.open(a,this)}),gmarkers[mapId].push(j),i&&j.hide(),j}function getCurrentLat(){return current_lat}function getCurrentLng(){return current_lng}function addAllMarkers(a,b,c,d,e){for(var f=[],g=0;g=a;a++){var b=mappableMaps["google_map_"+a],c=new google.maps.Map(document.getElementById(b.googleMapID));if(geocoder=new google.maps.Geocoder,b.allowFullScreen&&c.controls[google.maps.ControlPosition.TOP_RIGHT].push(FullScreenControl(c,"Full Screen","Original Size")),b.enableAutomaticCenterZoom){e=b.centreCoordinates,c.setCenter(new google.maps.LatLng(e.lat,e.lng));var d=new google.maps.LatLngBounds(new google.maps.LatLng(b.minLat,b.minLng),new google.maps.LatLng(b.maxLat,b.maxLng));c.fitBounds(d),c.setZoom(b.zoom)}else{var e=b.centreCoordinates;c.setCenter(new google.maps.LatLng(e.lat,e.lng)),c.setZoom(b.zoom)}c.setMapTypeId(b.mapType?b.mapType:google.maps.MapTypeId.ROADMAP);var f=addAllMarkers(c,b.markers,b.useClusterer,b.enableAutomaticCenterZoom,b.defaultHideMarker);if(b.useClusterer){var g={gridSize:50,maxZoom:17};new MarkerClusterer(c,f,g)}addLines(c,b.lines),addKmlFiles(c,b.kmlFiles)}}MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_="http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/m",MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_="png",MarkerClusterer.prototype.extend=function(a,b){return function(a){for(var b in a.prototype)this.prototype[b]=a.prototype[b];return this}.apply(a,[b])},MarkerClusterer.prototype.onAdd=function(){this.setReady_(!0)},MarkerClusterer.prototype.draw=function(){},MarkerClusterer.prototype.setupStyles_=function(){if(!this.styles_.length)for(var a,b=0;a=this.sizes[b];b++)this.styles_.push({url:this.imagePath_+(b+1)+"."+this.imageExtension_,height:a,width:a})},MarkerClusterer.prototype.fitMapToMarkers=function(){for(var a,b=this.getMarkers(),c=new google.maps.LatLngBounds,d=0;a=b[d];d++)c.extend(a.getPosition());this.map_.fitBounds(c)},MarkerClusterer.prototype.setStyles=function(a){this.styles_=a},MarkerClusterer.prototype.getStyles=function(){return this.styles_},MarkerClusterer.prototype.isZoomOnClick=function(){return this.zoomOnClick_},MarkerClusterer.prototype.isAverageCenter=function(){return this.averageCenter_},MarkerClusterer.prototype.getMarkers=function(){return this.markers_},MarkerClusterer.prototype.getTotalMarkers=function(){return this.markers_.length},MarkerClusterer.prototype.setMaxZoom=function(a){this.maxZoom_=a},MarkerClusterer.prototype.getMaxZoom=function(){return this.maxZoom_},MarkerClusterer.prototype.calculator_=function(a,b){for(var c=0,d=a.length,e=d;0!==e;)e=parseInt(e/10,10),c++;return c=Math.min(c,b),{text:d,index:c}},MarkerClusterer.prototype.setCalculator=function(a){this.calculator_=a},MarkerClusterer.prototype.getCalculator=function(){return this.calculator_},MarkerClusterer.prototype.addMarkers=function(a,b){for(var c,d=0;c=a[d];d++)this.pushMarkerTo_(c);b||this.redraw()},MarkerClusterer.prototype.pushMarkerTo_=function(a){if(a.isAdded=!1,a.draggable){var b=this;google.maps.event.addListener(a,"dragend",function(){a.isAdded=!1,b.repaint()})}this.markers_.push(a)},MarkerClusterer.prototype.addMarker=function(a,b){this.pushMarkerTo_(a),b||this.redraw()},MarkerClusterer.prototype.removeMarker_=function(a){var b=-1;if(this.markers_.indexOf)b=this.markers_.indexOf(a);else for(var c,d=0;c=this.markers_[d];d++)if(c==a){b=d;break}return-1==b?!1:(a.setMap(null),this.markers_.splice(b,1),!0)},MarkerClusterer.prototype.removeMarker=function(a,b){var c=this.removeMarker_(a);return!b&&c?(this.resetViewport(),this.redraw(),!0):!1},MarkerClusterer.prototype.removeMarkers=function(a,b){for(var c,d=!1,e=0;c=a[e];e++){var f=this.removeMarker_(c);d=d||f}return!b&&d?(this.resetViewport(),this.redraw(),!0):void 0},MarkerClusterer.prototype.setReady_=function(a){this.ready_||(this.ready_=a,this.createClusters_())},MarkerClusterer.prototype.getTotalClusters=function(){return this.clusters_.length},MarkerClusterer.prototype.getMap=function(){return this.map_},MarkerClusterer.prototype.setMap=function(a){this.map_=a},MarkerClusterer.prototype.getGridSize=function(){return this.gridSize_},MarkerClusterer.prototype.setGridSize=function(a){this.gridSize_=a},MarkerClusterer.prototype.getMinClusterSize=function(){return this.minClusterSize_},MarkerClusterer.prototype.setMinClusterSize=function(a){this.minClusterSize_=a},MarkerClusterer.prototype.getExtendedBounds=function(a){var b=this.getProjection(),c=new google.maps.LatLng(a.getNorthEast().lat(),a.getNorthEast().lng()),d=new google.maps.LatLng(a.getSouthWest().lat(),a.getSouthWest().lng()),e=b.fromLatLngToDivPixel(c);e.x+=this.gridSize_,e.y-=this.gridSize_;var f=b.fromLatLngToDivPixel(d);f.x-=this.gridSize_,f.y+=this.gridSize_;var g=b.fromDivPixelToLatLng(e),h=b.fromDivPixelToLatLng(f);return a.extend(g),a.extend(h),a},MarkerClusterer.prototype.isMarkerInBounds_=function(a,b){return b.contains(a.getPosition())},MarkerClusterer.prototype.clearMarkers=function(){this.resetViewport(!0),this.markers_=[]},MarkerClusterer.prototype.resetViewport=function(a){for(var b,c=0;b=this.clusters_[c];c++)b.remove();for(var d,c=0;d=this.markers_[c];c++)d.isAdded=!1,a&&d.setMap(null);this.clusters_=[]},MarkerClusterer.prototype.repaint=function(){var a=this.clusters_.slice();this.clusters_.length=0,this.resetViewport(),this.redraw(),window.setTimeout(function(){for(var b,c=0;b=a[c];c++)b.remove()},0)},MarkerClusterer.prototype.redraw=function(){this.createClusters_()},MarkerClusterer.prototype.distanceBetweenPoints_=function(a,b){if(!a||!b)return 0;var c=6371,d=(b.lat()-a.lat())*Math.PI/180,e=(b.lng()-a.lng())*Math.PI/180,f=Math.sin(d/2)*Math.sin(d/2)+Math.cos(a.lat()*Math.PI/180)*Math.cos(b.lat()*Math.PI/180)*Math.sin(e/2)*Math.sin(e/2),g=2*Math.atan2(Math.sqrt(f),Math.sqrt(1-f)),h=c*g;return h},MarkerClusterer.prototype.addToClosestCluster_=function(a){for(var b,c=4e4,d=null,e=(a.getPosition(),0);b=this.clusters_[e];e++){var f=b.getCenter();if(f){var g=this.distanceBetweenPoints_(f,a.getPosition());c>g&&(c=g,d=b)}}if(d&&d.isMarkerInClusterBounds(a))d.addMarker(a);else{var b=new Cluster(this);b.addMarker(a),this.clusters_.push(b)}},MarkerClusterer.prototype.createClusters_=function(){if(this.ready_)for(var a,b=new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(),this.map_.getBounds().getNorthEast()),c=this.getExtendedBounds(b),d=0;a=this.markers_[d];d++)!a.isAdded&&this.isMarkerInBounds_(a,c)&&this.addToClosestCluster_(a)},Cluster.prototype.isMarkerAlreadyAdded=function(a){if(this.markers_.indexOf)return-1!=this.markers_.indexOf(a);for(var b,c=0;b=this.markers_[c];c++)if(b==a)return!0;return!1},Cluster.prototype.addMarker=function(a){if(this.isMarkerAlreadyAdded(a))return!1;if(this.center_){if(this.averageCenter_){var b=this.markers_.length+1,c=(this.center_.lat()*(b-1)+a.getPosition().lat())/b,d=(this.center_.lng()*(b-1)+a.getPosition().lng())/b;this.center_=new google.maps.LatLng(c,d),this.calculateBounds_()}}else this.center_=a.getPosition(),this.calculateBounds_();a.isAdded=!0,this.markers_.push(a);var e=this.markers_.length;if(ef;f++)this.markers_[f].setMap(null);return e>=this.minClusterSize_&&a.setMap(null),this.updateIcon(),!0},Cluster.prototype.getMarkerClusterer=function(){return this.markerClusterer_},Cluster.prototype.getBounds=function(){for(var a,b=new google.maps.LatLngBounds(this.center_,this.center_),c=this.getMarkers(),d=0;a=c[d];d++)b.extend(a.getPosition());return b},Cluster.prototype.remove=function(){this.clusterIcon_.remove(),this.markers_.length=0,delete this.markers_},Cluster.prototype.getSize=function(){return this.markers_.length},Cluster.prototype.getMarkers=function(){return this.markers_},Cluster.prototype.getCenter=function(){return this.center_},Cluster.prototype.calculateBounds_=function(){var a=new google.maps.LatLngBounds(this.center_,this.center_);this.bounds_=this.markerClusterer_.getExtendedBounds(a)},Cluster.prototype.isMarkerInClusterBounds=function(a){return this.bounds_.contains(a.getPosition())},Cluster.prototype.getMap=function(){return this.map_},Cluster.prototype.updateIcon=function(){var a=this.map_.getZoom(),b=this.markerClusterer_.getMaxZoom();if(b&&a>b)for(var c,d=0;c=this.markers_[d];d++)c.setMap(this.map_);else{if(this.markers_.length0&&this.anchor_[0]0&&this.anchor_[1]"+b+"",e.appendChild(f);var g=document.getElementsByTagName("head")[0],h=document.createElement("style");h.setAttribute("type","text/css"),h.setAttribute("media","print");var i=".fullScreen { display: none;}",j=document.createTextNode(i);try{h.appendChild(j)}catch(k){h.styleSheet.cssText=i}g.appendChild(h);var l,m=!1,n=a.getDiv(),o=n.style;n.runtimeStyle&&(o=n.runtimeStyle);var p=o.position,q=o.width,r=o.height;""===q&&(q=n.style.width),""===r&&(r=n.style.height);var s=o.top,t=o.left,u=o.zIndex,v=document.body.style;document.body.runtimeStyle&&(v=document.body.runtimeStyle);var w=v.overflow;return d.goFullScreen=function(){var b=a.getCenter();n.style.position="fixed",n.style.width="100%",n.style.height="100%",n.style.top="0",n.style.left="0",n.style.zIndex="100",document.body.style.overflow="hidden",f.innerHTML=""+c+"",m=!0,google.maps.event.trigger(a,"resize"),a.setCenter(b),l=setInterval(function(){"fixed"!==n.style.position&&(n.style.position="fixed",google.maps.event.trigger(a,"resize"))},100)},d.exitFullScreen=function(){var c=a.getCenter();""===p?n.style.position="relative":n.style.position=p,n.style.width=q,n.style.height=r,n.style.top=s,n.style.left=t,n.style.zIndex=u,document.body.style.overflow=w,f.innerHTML=""+b+"",m=!1,google.maps.event.trigger(a,"resize"),a.setCenter(c),clearInterval(l)},google.maps.event.addDomListener(e,"click",function(){m?d.exitFullScreen():d.goFullScreen()}),document.onkeydown=function(a){a=a||window.event,27==a.keyCode&&m&&d.exitFullScreen()},d}function MarkerClusterer(a,b,c){this.extend(MarkerClusterer,google.maps.OverlayView),this.map_=a,this.markers_=[],this.clusters_=[],this.sizes=[53,56,66,78,90],this.styles_=[],this.ready_=!1;var d=c||{};this.gridSize_=d.gridSize||60,this.minClusterSize_=d.minimumClusterSize||2,this.maxZoom_=d.maxZoom||null,this.styles_=d.styles||[],this.imagePath_=d.imagePath||this.MARKER_CLUSTER_IMAGE_PATH_,this.imageExtension_=d.imageExtension||this.MARKER_CLUSTER_IMAGE_EXTENSION_,this.zoomOnClick_=!0,void 0!=d.zoomOnClick&&(this.zoomOnClick_=d.zoomOnClick),this.averageCenter_=!1,void 0!=d.averageCenter&&(this.averageCenter_=d.averageCenter),this.setupStyles_(),this.setMap(a),this.prevZoom_=this.map_.getZoom();var e=this;google.maps.event.addListener(this.map_,"zoom_changed",function(){var a=e.map_.getZoom();e.prevZoom_!=a&&(e.prevZoom_=a,e.resetViewport())}),google.maps.event.addListener(this.map_,"idle",function(){e.redraw()}),b&&b.length&&this.addMarkers(b,!1)}function Cluster(a){this.markerClusterer_=a,this.map_=a.getMap(),this.gridSize_=a.getGridSize(),this.minClusterSize_=a.getMinClusterSize(),this.averageCenter_=a.isAverageCenter(),this.center_=null,this.markers_=[],this.bounds_=null,this.clusterIcon_=new ClusterIcon(this,a.getStyles(),a.getGridSize())}function ClusterIcon(a,b,c){a.getMarkerClusterer().extend(ClusterIcon,google.maps.OverlayView),this.styles_=b,this.padding_=c||0,this.cluster_=a,this.center_=null,this.map_=a.getMap(),this.div_=null,this.sums_=null,this.visible_=!1,this.setMap(this.map_)}function createMarker(a,b,c,d,e,f,g,h,i){mapId=a.getDiv().getAttribute("id");var j=new google.maps.Marker;if(j.setPosition(new google.maps.LatLng(b,c)),j.mycategory=e,f&&""!==f){var k=new google.maps.MarkerImage(f);j.setIcon(k)}return g||j.setMap(a),google.maps.event.addListener(j,"click",function(){h&&a.setCenter(new google.maps.LatLng(b,c),12);var e=infoWindows[mapId];e.setContent(d),e.open(a,this)}),gmarkers[mapId].push(j),i&&j.hide(),j}function getCurrentLat(){return current_lat}function getCurrentLng(){return current_lng}function addAllMarkers(a,b,c,d,e){a.minLat=1e6,a.minLng=1e6,a.maxLat=-1e6,a.maxLng=-1e6;for(var f=[],g=0;ga.maxLat&&(a.maxLat=j),k>a.maxLng&&(a.maxLng=k),kg&&(c=g,d=b)}}if(d&&d.isMarkerInClusterBounds(a))d.addMarker(a);else{var b=new Cluster(this);b.addMarker(a),this.clusters_.push(b)}},MarkerClusterer.prototype.createClusters_=function(){if(this.ready_)for(var a,b=new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(),this.map_.getBounds().getNorthEast()),c=this.getExtendedBounds(b),d=0;a=this.markers_[d];d++)!a.isAdded&&this.isMarkerInBounds_(a,c)&&this.addToClosestCluster_(a)},Cluster.prototype.isMarkerAlreadyAdded=function(a){if(this.markers_.indexOf)return-1!=this.markers_.indexOf(a);for(var b,c=0;b=this.markers_[c];c++)if(b==a)return!0;return!1},Cluster.prototype.addMarker=function(a){if(this.isMarkerAlreadyAdded(a))return!1;if(this.center_){if(this.averageCenter_){var b=this.markers_.length+1,c=(this.center_.lat()*(b-1)+a.getPosition().lat())/b,d=(this.center_.lng()*(b-1)+a.getPosition().lng())/b;this.center_=new google.maps.LatLng(c,d),this.calculateBounds_()}}else this.center_=a.getPosition(),this.calculateBounds_();a.isAdded=!0,this.markers_.push(a);var e=this.markers_.length;if(ef;f++)this.markers_[f].setMap(null);return e>=this.minClusterSize_&&a.setMap(null),this.updateIcon(),!0},Cluster.prototype.getMarkerClusterer=function(){return this.markerClusterer_},Cluster.prototype.getBounds=function(){for(var a,b=new google.maps.LatLngBounds(this.center_,this.center_),c=this.getMarkers(),d=0;a=c[d];d++)b.extend(a.getPosition());return b},Cluster.prototype.remove=function(){this.clusterIcon_.remove(),this.markers_.length=0,delete this.markers_},Cluster.prototype.getSize=function(){return this.markers_.length},Cluster.prototype.getMarkers=function(){return this.markers_},Cluster.prototype.getCenter=function(){return this.center_},Cluster.prototype.calculateBounds_=function(){var a=new google.maps.LatLngBounds(this.center_,this.center_);this.bounds_=this.markerClusterer_.getExtendedBounds(a)},Cluster.prototype.isMarkerInClusterBounds=function(a){return this.bounds_.contains(a.getPosition())},Cluster.prototype.getMap=function(){return this.map_},Cluster.prototype.updateIcon=function(){var a=this.map_.getZoom(),b=this.markerClusterer_.getMaxZoom();if(b&&a>b)for(var c,d=0;c=this.markers_[d];d++)c.setMap(this.map_);else{if(this.markers_.length0&&this.anchor_[0]0&&this.anchor_[1]=0;k--){var l=h[k].latitude,m=h[k].longitude;addGuideMarker(l,m);var n=new google.maps.LatLng(l,m);i+=parseFloat(l),j+=parseFloat(m),bounds.extend(n)}if(0===d.val()&&0===e.val()){var o=h.length;new google.maps.LatLng(i/o,j/o)}map.fitBounds(bounds)}d.val()&&e.val()&&(marker=null,setMarker(a.center,!0)),google.maps.event.addListener(map,"rightclick",function(a){var b=a.latLng.lat(),c=a.latLng.lng();d.val(b),e.val(c),setMarker(a.latLng,!1),statusMessage("Location changed to "+b+","+c)}),google.maps.event.addListener(map,"zoom_changed",function(){f.length&&f.val(map.getZoom())}),google.maps.event.trigger(map,"resize"),map.setZoom(map.getZoom()),b(".ui-tabs-anchor").click(function(){google.maps.event.trigger(map,"resize");var a=b("#GoogleMap"),c=a.attr("data-usemapbounds");c?map.fitBounds(bounds):map.setCenter(marker.getPosition())})}(jQuery)}function addGuideMarker(a,b){var c=new google.maps.LatLng(a,b),d="CCCCCC",e=new google.maps.MarkerImage("//chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|"+d,new google.maps.Size(21,34),new google.maps.Point(0,0),new google.maps.Point(10,34)),f=new google.maps.MarkerImage("//chart.apis.google.com/chart?chst=d_map_pin_shadow",new google.maps.Size(40,37),new google.maps.Point(0,0),new google.maps.Point(12,35)),g=new google.maps.Marker({position:c,title:"Marker",icon:e,shadow:f});g.setMap(map)}function setMarker(a,b){null!==marker?marker.setPosition(a):(marker=new google.maps.Marker({position:a,title:"Position",draggable:!0}),marker.setMap(map),google.maps.event.addListener(marker,"dragend",setCoordByMarker)),b&&map.setCenter(a)}function setCoordByMarker(a){!function(b){var c=b("#GoogleMap"),d=b("input[name="+c.attr("data-latfieldname")+"]"),e=b("input[name="+c.attr("data-lonfieldname")+"]"),f=b("input[name="+c.attr("data-zoomfieldname")+"]"),g=a.latLng.lat(),h=a.latLng.lng();d.val(g),e.val(h),setMarker(a.latLng,!0),statusMessage("Location changed to "+g+","+h),f.length&&f.val(map.getZoom()),map.setCenter(a.latLng)}(jQuery)}function searchForAddress(a){!function(b){{var c=new google.maps.Geocoder;new google.maps.ElevationService}c&&(statusMessage("Searching for:"+a),c.geocode({address:a},function(a,c){if(c==google.maps.GeocoderStatus.OK){var d=a.length;d>0?statusMessage("Places found"):0===d&&errorMessage("No places found");var e='
    ';b.each(a,function(a,c){var d=[];b.each(c.address_components,function(a,b){d.push(b.long_name)}),e=e+'
  • '+d+"
  • "}),e+="
",b("#mapSearchResults").html(e)}else errorMessage("Unable to find any geocoded results")}))}(jQuery)}function initLivequery(){!function(a){a("input[name=action_GetCoords]").livequery("click",function(){var b=a("#Form_EditForm_Location").val();return setCoordByAddress(b),!1}),a("#searchLocationButton").livequery("click",function(){var b=a("#location_search").val();return searchForAddress(b),!1}),a(".geocodedSearchResults li").livequery("click",function(){var b=a(this),c=b.attr("lat"),d=b.attr("lon"),e=b.html(),f=new google.maps.LatLng(c,d);statusMessage("Setting map to "+e),a(".geocodedSearchResults").html(""),a("#Form_EditForm_Latitude").val(c),a("#Form_EditForm_Longitude").val(d);{var g=a("#GoogleMap"),h=a("input[name="+g.attr("data-latfieldname")+"]"),i=a("input[name="+g.attr("data-lonfieldname")+"]");a("input[name="+g.attr("data-zoomfieldname")+"]")}return h.val(c),i.val(d),map.setZoom(12),setMarker(f,!0),!1}),a("#GoogleMap").livequery(function(){initMap()})}(jQuery)}var marker,bounds;!function(a){function b(){var a=document.createElement("script");a.type="text/javascript",a.src="//maps.googleapis.com/maps/api/js?sensor=false&callback=gmloaded",document.body.appendChild(a)}a(document).ready(function(){b()})}(jQuery); \ No newline at end of file +function gmloaded(){initLivequery()}function initMap(){var a={zoom:16,disableDefaultUI:!1,mapTypeId:google.maps.MapTypeId.ROADMAP,disableDoubleClickZoom:!1,draggable:!0,keyboardShortcuts:!1,scrollwheel:!0};!function(b){var c=b("#GoogleMap"),d=(c.attr("data-latfieldname"),b("input[name="+c.attr("data-latfieldname")+"]")),e=b("input[name="+c.attr("data-lonfieldname")+"]"),f=b("input[name="+c.attr("data-zoomfieldname")+"]"),g=c.attr("data-GuidePoints");""===d.val()&&d.val(0),""===e.val()&&e.val(0),""===f.val()&&f.val(2);var h=[];if("undefined"!=typeof g&&(h=JSON.parse(g)),a.center=new google.maps.LatLng(d.val(),e.val()),f.length&&(a.zoom=parseInt(f.val(),10)),map=new google.maps.Map(document.getElementById("GoogleMap"),a),bounds=new google.maps.LatLngBounds,h.length){for(var i=0,j=0,k=h.length-1;k>=0;k--){var l=h[k].latitude,m=h[k].longitude;addGuideMarker(l,m);var n=new google.maps.LatLng(l,m);i+=parseFloat(l),j+=parseFloat(m),bounds.extend(n)}if(0===d.val()&&0===e.val()){var o=h.length;new google.maps.LatLng(i/o,j/o)}map.fitBounds(bounds)}d.val()&&e.val()&&(marker=null,setMarker(a.center,!0)),google.maps.event.addListener(map,"rightclick",function(a){var b=a.latLng.lat(),c=a.latLng.lng();d.val(b),e.val(c),setMarker(a.latLng,!1),statusMessage("Location changed to "+b+","+c)}),google.maps.event.addListener(map,"zoom_changed",function(a){f.length&&f.val(map.getZoom())}),google.maps.event.trigger(map,"resize"),map.setZoom(map.getZoom()),b(".ui-tabs-anchor").click(function(){google.maps.event.trigger(map,"resize");var a=b("#GoogleMap"),c=a.attr("data-usemapbounds");c?map.fitBounds(bounds):map.setCenter(marker.getPosition())})}(jQuery)}function addGuideMarker(a,b){var c=new google.maps.LatLng(a,b),d="CCCCCC",e=new google.maps.MarkerImage("//chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|"+d,new google.maps.Size(21,34),new google.maps.Point(0,0),new google.maps.Point(10,34)),f=new google.maps.MarkerImage("//chart.apis.google.com/chart?chst=d_map_pin_shadow",new google.maps.Size(40,37),new google.maps.Point(0,0),new google.maps.Point(12,35)),g=new google.maps.Marker({position:c,title:"Marker",icon:e,shadow:f});g.setMap(map)}function setMarker(a,b){null!==marker?marker.setPosition(a):(marker=new google.maps.Marker({position:a,title:"Position",draggable:!0}),marker.setMap(map),google.maps.event.addListener(marker,"dragend",setCoordByMarker)),b&&map.setCenter(a)}function setCoordByMarker(a){!function(b){var c=b("#GoogleMap"),d=b("input[name="+c.attr("data-latfieldname")+"]"),e=b("input[name="+c.attr("data-lonfieldname")+"]"),f=b("input[name="+c.attr("data-zoomfieldname")+"]"),g=a.latLng.lat(),h=a.latLng.lng();d.val(g),e.val(h),setMarker(a.latLng,!0),statusMessage("Location changed to "+g+","+h),f.length&&f.val(map.getZoom()),map.setCenter(a.latLng)}(jQuery)}function searchForAddress(a){!function(b){{var c=new google.maps.Geocoder;new google.maps.ElevationService}c&&(statusMessage("Searching for:"+a),c.geocode({address:a},function(a,c){if(c==google.maps.GeocoderStatus.OK){var d=a.length;d>0?statusMessage("Places found"):0===d&&errorMessage("No places found");var e='
    ';b.each(a,function(a,c){var d=[];b.each(c.address_components,function(a,b){d.push(b.long_name)}),e=e+'
  • '+d+"
  • "}),e+="
",b("#mapSearchResults").html(e)}else errorMessage("Unable to find any geocoded results")}))}(jQuery)}function initLivequery(){!function(a){a("input[name=action_GetCoords]").livequery("click",function(b){var c=a("#Form_EditForm_Location").val();return setCoordByAddress(c),!1}),a("#searchLocationButton").livequery("click",function(b){var c=a("#location_search").val();return searchForAddress(c),!1}),a(".geocodedSearchResults li").livequery("click",function(b){var c=a(this),d=c.attr("lat"),e=c.attr("lon"),f=c.html(),g=new google.maps.LatLng(d,e);statusMessage("Setting map to "+f),a(".geocodedSearchResults").html(""),a("#Form_EditForm_Latitude").val(d),a("#Form_EditForm_Longitude").val(e);{var h=a("#GoogleMap"),i=a("input[name="+h.attr("data-latfieldname")+"]"),j=a("input[name="+h.attr("data-lonfieldname")+"]");a("input[name="+h.attr("data-zoomfieldname")+"]")}return i.val(d),j.val(e),map.setZoom(12),setMarker(g,!0),!1}),a("#GoogleMap").livequery(function(){initMap()})}(jQuery)}var marker,bounds;!function(a){function b(){var a=document.createElement("script");a.type="text/javascript",a.src="//maps.googleapis.com/maps/api/js?sensor=false&callback=gmloaded",document.body.appendChild(a)}a(document).ready(function(){b()})}(jQuery); \ No newline at end of file From 69d34d9582edf16453165db8d1fe820f093a98b4 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 23 Apr 2015 11:24:13 +0700 Subject: [PATCH 282/354] MINOR: Updated versions of node packages --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b0775cc..a2cc4b8 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,9 @@ "version": "0.1.0", "devDependencies": { "grunt": "~0.4.5", - "grunt-contrib-jshint": "~0.11.0", + "grunt-contrib-jshint": "~0.11.2", "grunt-contrib-nodeunit": "~0.4.1", - "grunt-contrib-uglify": "~0.8.0", + "grunt-contrib-uglify": "~0.9.1", "grunt-contrib-concat": "~0.5.1", "grunt-contrib-cssmin": "~0.12.2", "grunt-contrib-watch": "~0.6.1", From 445653d09a3446e0451131d884bd13cdb5486a77 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sun, 26 Apr 2015 15:07:59 +0700 Subject: [PATCH 283/354] MINOR: Addition of cache key generation if using the Cache Key Helper module --- _config/cachekey.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 _config/cachekey.yml diff --git a/_config/cachekey.yml b/_config/cachekey.yml new file mode 100644 index 0000000..e558e52 --- /dev/null +++ b/_config/cachekey.yml @@ -0,0 +1,3 @@ +CacheKeyHelper: + SiteTree: ['POIMapPage'] + DataObject: ['PointOfInterest'] From 53af59447aaac285b61f6937e245343e3b559669 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 16 May 2015 12:58:45 +0700 Subject: [PATCH 284/354] FIX: No need to render a separate JavaScript template if using Unobtrusive JavaScript --- code/MapAPI.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index 1eb67d9..cb717f4 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -854,8 +854,8 @@ public function generate() { ); // JavaScript required to prime the map - $javascript = $this->processTemplateJS('Map', $vars); - $vars->setField('JavaScript', $javascript); + //$javascript = $this->processTemplateJS('Map', $vars); + //$vars->setField('JavaScript', $javascript); // HTML component of the map $this->content = $this->processTemplateHTML('Map', $vars); From 54b22daaf39838b3a4bd63d79f24987bef7e2462 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 16 May 2015 12:59:14 +0700 Subject: [PATCH 285/354] ENHANCEMENT: Data regarding map rendering now passed as data attributes --- javascript/google/MapGoogleHTML.ss | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/javascript/google/MapGoogleHTML.ss b/javascript/google/MapGoogleHTML.ss index 7dbea7a..b00b7db 100644 --- a/javascript/google/MapGoogleHTML.ss +++ b/javascript/google/MapGoogleHTML.ss @@ -1,7 +1,19 @@ <% include GoogleJavaScript %>
style="width:{$Width}; height: {$Height};" -<% end_if %><% if AdditionalCssClasses %> class="$AdditionalCssClasses"<% end_if %>> +<% end_if %><% if AdditionalCssClasses %> class="$AdditionalCssClasses"<% end_if %> +data-map +data-centre='$LatLngCentre' +data-zoom=$Zoom +data-maptype='$MapType' +data-allowfullscreen='$AllowFullScreen' +data-clusterergridsize=$ClustererGridSize, +data-clusterermaxzoom=$ClustererMaxZoom, +data-enableautocentrezoom=$EnableAutomaticCenterZoom +data-mapmarkers= '$MapMarkers' +data-lines='$Lines' +data-kmlfiles='$KmlFiles' +data-mapstyles='$JsonMapStyles' +data-useclusterer=$UseClusterer, + +>
- From c4336e18a5046701cc2c50fb079cf8042097d623 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 16 May 2015 12:59:43 +0700 Subject: [PATCH 286/354] WIP: Removing code bit by bit --- templates/Includes/GoogleJavaScript.ss | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/templates/Includes/GoogleJavaScript.ss b/templates/Includes/GoogleJavaScript.ss index 08a3a75..feb00c4 100644 --- a/templates/Includes/GoogleJavaScript.ss +++ b/templates/Includes/GoogleJavaScript.ss @@ -57,16 +57,7 @@ function registerMap(options) { mapLines[googleMapID] = options.lines; } -function loadScript() { - var script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = 'https://maps.googleapis.com/maps/api/js?' + - '&sensor=false&callback=loadedGoogleMapsAPI&hl=en'; - document.body.appendChild(script); -} -window.addEventListener ? - window.addEventListener("load",loadScript,false) : -window.attachEvent && window.attachEvent("onload",loadScript); + <% if $UseCompressedAssets %> <% require javascript("mappable/javascript/google/mappablegoogle.min.js") %> From 650c2ae86cc6fb61f5d3b09e21a74b5a321e4fa7 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 16 May 2015 13:03:20 +0700 Subject: [PATCH 287/354] WIP: Maps now rendering via UJS ENHANCEMENT: Streetview and embedded maps now UJS also FIX: Remove debug --- javascript/google/maputil.js | 143 ++++++++++++++++++++++------------- 1 file changed, 92 insertions(+), 51 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index 6898da5..77f8bc4 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -1,3 +1,19 @@ +// FIXME - make non global +var infoWindows = []; + + +function loadGoogleMapsScript() { + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = 'https://maps.googleapis.com/maps/api/js?' + + '&sensor=false&callback=loadedGoogleMapsAPI&hl=en'; + document.body.appendChild(script); +} +window.addEventListener ? + window.addEventListener("load",loadGoogleMapsScript,false) : +window.attachEvent && window.attachEvent("onload",loadGoogleMapsScript); + + /** * Create a google map pin from data provided * @param {GoogleMap} map Instance of a google map @@ -37,7 +53,7 @@ function createMarker(map, lat, lng, html, category, icon, useClusterer, enableW infoWindow.open(map, this); }); - gmarkers[mapId].push(marker); + //FIXME gmarkers[mapId].push(marker); if (defaultHideMarker) { marker.hide(); @@ -187,32 +203,39 @@ function convertMapType(mapTypeName) { * After the Google Maps API has been loaded init the relevant Google maps */ function loadShortCodeStreetView() { - for (var i = 0; i < shortcodeStreetview.length; i++) { - view = shortcodeStreetview[i]; - var location = new google.maps.LatLng(view.latitude, view.longitude); - var mapOptions = { - center: location, - zoom: view.zoom - }; + var svs = $('div[data-streetview]'); + + svs.each(function(index) { + var svnode = $(this); + var svnodeid = svnode.attr('id'); + var lat = parseFloat(svnode.attr('data-latitude')); + var lon = parseFloat(svnode.attr('data-longitude')); + var zoom = parseInt(svnode.attr('data-zoom')); + var heading = parseFloat(svnode.attr('data-heading')); + var pitch = parseFloat(svnode.attr('data-pitch')); - var map = new google.maps.Map( - document.getElementById(view.domid), mapOptions); + var location = new google.maps.LatLng(lat,lon); var panoramaOptions = { position: location, pov: { - heading: view.heading, - pitch: view.pitch + heading: heading, + pitch: pitch }, - zoom: view.zoom + zoom: zoom }; - var domNode = document.getElementById(view.domid); + + + var domNode = document.getElementById(svnodeid); var pano = new google.maps.StreetViewPanorama( domNode, panoramaOptions); pano.setVisible(true); - } + }); + + + } @@ -221,26 +244,36 @@ function loadShortCodeStreetView() { * After the Google Maps API has been loaded init the relevant Google maps */ function loadShortCodeMaps() { - for (var i = 0; i < shortcodeMaps.length; i++) { - map = shortcodeMaps[i]; - // deal with map type - var maptype = convertMapType(map.maptype); + var scms = $('div[data-shortcode-map]'); + + scms.each(function(index) { + var scmnode = $(this); + var scmnodeid = scmnode.attr('id'); + var maptype = convertMapType(scmnode.attr('data-maptype')); + var lat = parseFloat(scmnode.attr('data-latitude')); + var lng = parseFloat(scmnode.attr('data-longitude')); + var zoom = parseInt(scmnode.attr('data-zoom')); + var allowfullscreen = parseInt(scmnode.attr('data-allowfullscreen')); // Google Maps API has already been loaded, so init Google Map var mapOptions = { - center: { lat: map.latitude, lng: map.longitude}, - zoom: map.zoom, + center: { lat:lat, lng: lng}, + zoom: zoom, mapTypeId: maptype }; - var gmap = new google.maps.Map(document.getElementById(map.domid), + + + + var gmap = new google.maps.Map(document.getElementById(scmnodeid), mapOptions); - if (map.allowfullscreen == 1) { + if (allowfullscreen == 1) { gmap.controls[google.maps.ControlPosition.TOP_RIGHT].push( FullScreenControl(gmap, "Full Screen", "Original Size") ); } - } + }); + } @@ -253,61 +286,69 @@ function loadedGoogleMapsAPI() { loadShortCodeMaps(); loadShortCodeStreetView(); - for (var i = 1; i <= mappableMapCtr; i++) { - var mapdomid = 'google_map_' + i; - var map_info = mappableMaps[mapdomid]; - var map = new google.maps.Map(document.getElementById(map_info.googleMapID)); + var maps = $('div[data-map]'); + + maps.each(function(index) { + var mapnode = $(this); + mapdomid = mapnode.attr('id'); + var map = new google.maps.Map(document.getElementById(mapdomid)); // initialise geocoder geocoder = new google.maps.Geocoder(); // default of [] renders google maps as per normal - if (map_info.jsonMapStyles) { - map.setOptions({styles: map_info.jsonMapStyles}); + if (mapnode.data('jsonMapStyles')) { + map.setOptions({styles: map.data('jsonMapStyles')}); }; - if (map_info.allowFullScreen) { + if (mapnode.data['data-allowfullscreen']) { map.controls[google.maps.ControlPosition.TOP_RIGHT].push( FullScreenControl(map, "Full Screen", "Original Size") ); } + var markerjson = $.parseJSON(mapnode.attr('data-mapmarkers')); + var useClusterer = mapnode.attr('data-useclusterer'); + var enableAutomaticCenterZoom = mapnode.attr('data-enableautocentrezoom'); + var defaultHideMarker = mapnode.attr('data-defaulthidemarker'); + var markers = addAllMarkers(map, markerjson, useClusterer, + enableAutomaticCenterZoom, defaultHideMarker); - var markers = addAllMarkers(map, map_info.markers, map_info.useClusterer, - map_info.enableAutomaticCenterZoom, map_info.defaultHideMarker); - - if (map_info.enableAutomaticCenterZoom) { - centre = map.centreCoordinates; + if (enableAutomaticCenterZoom == '1') { + centre = $.parseJSON(mapnode.attr('data-centre')); map.setCenter(new google.maps.LatLng(centre.lat, centre.lng)); - map_info.minLat = map.minLat; - map_info.maxLat = map.maxLat; - map_info.minLng = map.minLng; - map_info.maxLng = map.maxLng; - - var bds = new google.maps.LatLngBounds(new google.maps.LatLng(map_info.minLat, map_info.minLng), - new google.maps.LatLng(map_info.maxLat, map_info.maxLng)); + var bds = new google.maps.LatLngBounds(new google.maps.LatLng(map.minLat, map.minLng), + new google.maps.LatLng(map.maxLat, map.maxLng)); map.fitBounds(bds); } else { - var centre = map_info.centreCoordinates; + centre = $.parseJSON(mapnode.attr('data-centre')); map.setCenter(new google.maps.LatLng(centre.lat, centre.lng)); - map.setZoom(map_info.zoom); + var zoom = parseInt(mapnode.attr('data-zoom')); + map.setZoom(zoom); } - var googlemaptype = convertMapType(map_info.mapType); + var googlemaptype = convertMapType(mapnode.attr('data-maptype')); map.setMapTypeId(googlemaptype); - if (map_info.useClusterer) { - var mcOptions = {gridSize: map_info.clustererGridSize, maxZoom: map_info.clustererMaxZoom}; + if (useClusterer) { + // ensure zoom and grid size are integers by prefixing with unary plus + var clustererGridSize = parseInt(mapnode.attr('data-clusterergridsize')); + var clustererMaxZoom = parseInt(mapnode.attr('data-clusterermaxzoom')); + var mcOptions = {gridSize: clustererGridSize, maxZoom: clustererMaxZoom}; var markerCluster = new MarkerClusterer(map,markers,mcOptions); } - addLines(map, map_info.lines); - addKmlFiles(map, map_info.kmlFiles); + var lines =$.parseJSON(mapnode.attr('data-lines')); + addLines(map, lines); + + var kmlFiles =$.parseJSON(mapnode.attr('data-kmlfiles')); + addKmlFiles(map, kmlFiles); var infoWindow = new google.maps.InfoWindow({ content: '' }); infoWindows[mapdomid] = infoWindow; - } + + }); } From ad5b96c3221af968ef01b040d7404294c394b965 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 16 May 2015 13:55:25 +0700 Subject: [PATCH 288/354] FIX: Remove all inline JavaScript, only make require calls to load JavaScript files into SilverStripe FIX: Make embedded Google maps UJS FIX: Make embedded Google Street View UJS --- templates/Includes/GoogleJavaScript.ss | 70 ------------------------ templates/Includes/GoogleMapShortCode.ss | 20 +++---- templates/Includes/GoogleStreetView.ss | 19 +++---- 3 files changed, 14 insertions(+), 95 deletions(-) diff --git a/templates/Includes/GoogleJavaScript.ss b/templates/Includes/GoogleJavaScript.ss index feb00c4..dfa7f52 100644 --- a/templates/Includes/GoogleJavaScript.ss +++ b/templates/Includes/GoogleJavaScript.ss @@ -1,77 +1,7 @@ -<% if DownloadJS %> -<% if DelayLoadMapFunction %> -<% else %> - <% if $UseCompressedAssets %> <% require javascript("mappable/javascript/google/mappablegoogle.min.js") %> <% else %> - <% require javascript("mappable/javascript/google/FullScreenControl.js") %> <% require javascript("mappable/javascript/google/markerclusterer.js") %> <% require javascript("mappable/javascript/google/maputil.js") %> <% end_if %> -<% end_if %> - -<% end_if %> diff --git a/templates/Includes/GoogleMapShortCode.ss b/templates/Includes/GoogleMapShortCode.ss index 7ea35b0..32385b9 100644 --- a/templates/Includes/GoogleMapShortCode.ss +++ b/templates/Includes/GoogleMapShortCode.ss @@ -1,17 +1,11 @@ <% include GoogleJavaScript %>
-
+
<% if $Caption %>

$Caption

<% end_if %>
- diff --git a/templates/Includes/GoogleStreetView.ss b/templates/Includes/GoogleStreetView.ss index 10ea1f2..4361048 100644 --- a/templates/Includes/GoogleStreetView.ss +++ b/templates/Includes/GoogleStreetView.ss @@ -1,16 +1,11 @@ <% include GoogleJavaScript %>
-
+
<% if $Caption %>

$Caption

<% end_if %>
- From e703ccd752cb4d3f35ec825a84f0c70c1c9e35d6 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 16 May 2015 14:19:26 +0700 Subject: [PATCH 289/354] FIX: Ensure correct autozoom for flag, MapExtension fixes #26 --- code/MapExtension.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/code/MapExtension.php b/code/MapExtension.php index 1b64777..95fbd94 100644 --- a/code/MapExtension.php +++ b/code/MapExtension.php @@ -141,10 +141,14 @@ public function BasicMap() { setAdditionalCSSClasses('fullWidthMap')-> setShowInlineMapDivStyle(true); + $autozoom = false; + // add any KML map layers if (Object::has_extension($this->owner->ClassName, 'MapLayerExtension')) { foreach($this->owner->MapLayers() as $layer) { $map->addKML($layer->KmlFile()->getAbsoluteURL()); + // we have a layer, so turn on autozoom + $autozoom = true; } $map->setEnableAutomaticCenterZoom(true); } @@ -162,17 +166,17 @@ public function BasicMap() { $poi->CachedMapPinURL = $layericon->getAbsoluteURL(); } $map->addMarkerAsObject($poi); + + // we have a point of interest, so turn on auto zoom + $autozoom = true; } } } $map->setClusterer(true); - $map->setEnableAutomaticCenterZoom(true); } - $map->setZoom(10); - $map->setAdditionalCSSClasses('fullWidthMap'); + $map->setEnableAutomaticCenterZoom($autozoom); $map->setShowInlineMapDivStyle(true); - $map->setClusterer(true); return $map; } From 25cae9726552bf1ab5f5dce4c275f8f32579187a Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 26 May 2015 22:10:55 +0700 Subject: [PATCH 290/354] FIX: Ensure non short code maps can have the full screen button added --- javascript/google/maputil.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index 77f8bc4..d21d72f 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -313,6 +313,7 @@ function loadedGoogleMapsAPI() { var defaultHideMarker = mapnode.attr('data-defaulthidemarker'); var markers = addAllMarkers(map, markerjson, useClusterer, enableAutomaticCenterZoom, defaultHideMarker); + var allowfullscreen = parseInt(mapnode.attr('data-allowfullscreen')); if (enableAutomaticCenterZoom == '1') { centre = $.parseJSON(mapnode.attr('data-centre')); @@ -328,6 +329,12 @@ function loadedGoogleMapsAPI() { map.setZoom(zoom); } + if (allowfullscreen == 1) { + map.controls[google.maps.ControlPosition.TOP_RIGHT].push( + FullScreenControl(map, "Full Screen", "Original Size") + ); + } + var googlemaptype = convertMapType(mapnode.attr('data-maptype')); map.setMapTypeId(googlemaptype); From f1e82d2e5104ad73e4e1c1458568e6c232d19f60 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 26 May 2015 23:26:32 +0700 Subject: [PATCH 291/354] FIX: Ensure JSON map layers work correctly --- javascript/google/maputil.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index d21d72f..e5742ee 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -297,8 +297,9 @@ function loadedGoogleMapsAPI() { geocoder = new google.maps.Geocoder(); // default of [] renders google maps as per normal - if (mapnode.data('jsonMapStyles')) { - map.setOptions({styles: map.data('jsonMapStyles')}); + if (mapnode.attr('data-mapstyles')) { + var json=$.parseJSON(mapnode.attr('data-mapstyles')); + map.setOptions({styles: json}); }; if (mapnode.data['data-allowfullscreen']) { From d437d70a15a45521a5fb3c91302ccb421282baba Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 26 May 2015 23:27:41 +0700 Subject: [PATCH 292/354] FIX: Remove trailing comma --- javascript/google/MapGoogleHTML.ss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/javascript/google/MapGoogleHTML.ss b/javascript/google/MapGoogleHTML.ss index b00b7db..fabee94 100644 --- a/javascript/google/MapGoogleHTML.ss +++ b/javascript/google/MapGoogleHTML.ss @@ -13,7 +13,6 @@ data-mapmarkers= '$MapMarkers' data-lines='$Lines' data-kmlfiles='$KmlFiles' data-mapstyles='$JsonMapStyles' -data-useclusterer=$UseClusterer, - +data-useclusterer=$UseClusterer >
From 017ab42274b830084bcd425e96e87300dddb6e39 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 27 May 2015 19:54:25 +0700 Subject: [PATCH 293/354] ENHANCEMENT: Trigger an event after the map has been initialised, this allows listeners to be added for events in a separate JS file --- javascript/google/maputil.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index e5742ee..fbd9e6c 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -358,5 +358,8 @@ function loadedGoogleMapsAPI() { }); infoWindows[mapdomid] = infoWindow; + // trigger an event now that the map has been initialised + // Use this for example to add listeners to the map from another JS file + mapnode.trigger( "mapInitialised", [ map ] ); }); } From 063fd6dd2a6d0c30dfe8bdbe4d540a0a0cd08813 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 29 May 2015 16:40:32 +0700 Subject: [PATCH 294/354] ENHANCEMENT: Additional functionality for geolocation from the browser --- javascript/google/maputil.js | 37 ++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index fbd9e6c..914b7ef 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -363,3 +363,40 @@ function loadedGoogleMapsAPI() { mapnode.trigger( "mapInitialised", [ map ] ); }); } + + + + +function nearestPOIs() { + + // normally will only be one + var nears = $('div[data-nearest-poi]'); + + nears.each(function(index) { + var nearnode = $(this); + var layerID = nearnode.attr('data-layer-id'); + + if(geoPosition.init()){ // Geolocation Initialisation + geoPosition.getCurrentPosition(success_callback,error_callback,{enableHighAccuracy:true}); + }else{ + alert('your location is not available'); + } + //geoPositionSimulator.init(); + }); +} + + +function success_callback(p){ + console.log(p.coords); + // p.latitude : latitude value + // p.longitude : longitude value + // + var url = window.location; + url = url + 'find?lat='+p.coords.latitude+'&lng='+p.coords.longitude; + window.location = url; +} + + + function error_callback(p){ + alert('error)'); + }; From f13a4d56221df04e86d54686f118850f38190739 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 29 May 2015 16:40:57 +0700 Subject: [PATCH 295/354] ENHANCEMENT: Third party library to extract geographic location from browser --- javascript/geoPosition.js | 263 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 javascript/geoPosition.js diff --git a/javascript/geoPosition.js b/javascript/geoPosition.js new file mode 100644 index 0000000..b8d74f4 --- /dev/null +++ b/javascript/geoPosition.js @@ -0,0 +1,263 @@ +// +// javascript-mobile-desktop-geolocation +// https://github.com/estebanav/javascript-mobile-desktop-geolocation +// +// Copyright J. Esteban Acosta Villafañe +// Licensed under the MIT licenses. +// +// Based on Stan Wiechers > geo-location-javascript v0.4.8 > http://code.google.com/p/geo-location-javascript/ +// +// Revision: $Rev: 01 $: +// Author: $Author: estebanav $: +// Date: $Date: 2012-09-07 23:03:53 -0300 (Fri, 07 Sep 2012) $: + +var bb = { + success: 0, + error: 0, + blackberryTimeoutId : -1 + }; + +function handleBlackBerryLocationTimeout() +{ + if(bb.blackberryTimeoutId!=-1) { + bb.error({ message: "Timeout error", + code: 3 + }); + } +} +function handleBlackBerryLocation() +{ + clearTimeout(bb.blackberryTimeoutId); + bb.blackberryTimeoutId=-1; + if (bb.success && bb.error) { + if(blackberry.location.latitude==0 && blackberry.location.longitude==0) { + //http://dev.w3.org/geo/api/spec-source.html#position_unavailable_error + //POSITION_UNAVAILABLE (numeric value 2) + bb.error({message:"Position unavailable", code:2}); + } + else + { + var timestamp=null; + //only available with 4.6 and later + //http://na.blackberry.com/eng/deliverables/8861/blackberry_location_568404_11.jsp + if (blackberry.location.timestamp) + { + timestamp = new Date( blackberry.location.timestamp ); + } + bb.success( { timestamp: timestamp , + coords: { + latitude: blackberry.location.latitude, + longitude: blackberry.location.longitude + } + }); + } + //since blackberry.location.removeLocationUpdate(); + //is not working as described http://na.blackberry.com/eng/deliverables/8861/blackberry_location_removeLocationUpdate_568409_11.jsp + //the callback are set to null to indicate that the job is done + + bb.success = null; + bb.error = null; + } +} + +var geoPosition=function() { + + var pub = {}; + var provider=null; + var u="undefined"; + var ipGeolocationSrv = 'http://freegeoip.net/json/?callback=JSONPCallback'; + + pub.getCurrentPosition = function(success,error,opts) + { + provider.getCurrentPosition(success, error,opts); + } + + pub.jsonp = { + callbackCounter: 0, + + fetch: function(url, callback) { + var fn = 'JSONPCallback_' + this.callbackCounter++; + window[fn] = this.evalJSONP(callback); + url = url.replace('=JSONPCallback', '=' + fn); + + var scriptTag = document.createElement('SCRIPT'); + scriptTag.src = url; + document.getElementsByTagName('HEAD')[0].appendChild(scriptTag); + }, + + evalJSONP: function(callback) { + return function(data) { + callback(data); + } + } + }; + + pub.confirmation = function() + { + return confirm('This Webpage wants to track your physical location.\nDo you allow it?'); + }; + + pub.init = function() + { + try + { + var hasGeolocation = typeof(navigator.geolocation)!=u; + if( !hasGeolocation ){ + if( !pub.confirmation() ){ + return false; + } + } + + if ( ( typeof(geoPositionSimulator)!=u ) && (geoPositionSimulator.length > 0 ) ){ + provider=geoPositionSimulator; + } else if (typeof(bondi)!=u && typeof(bondi.geolocation)!=u ) { + provider=bondi.geolocation; + } else if ( hasGeolocation ) { + provider=navigator.geolocation; + pub.getCurrentPosition = function(success, error, opts) { + function _success(p) { + //for mozilla geode,it returns the coordinates slightly differently + var params; + if(typeof(p.latitude)!=u) { + params = { + timestamp: p.timestamp, + coords: { + latitude: p.latitude, + longitude: p.longitude + } + }; + } else { + params = p; + } + success( params ); + } + provider.getCurrentPosition(_success,error,opts); + } + } else if(typeof(window.blackberry)!=u && blackberry.location.GPSSupported) { + // set to autonomous mode + if(typeof(blackberry.location.setAidMode)==u) { + return false; + } + blackberry.location.setAidMode(2); + //override default method implementation + pub.getCurrentPosition = function(success,error,opts) + { + //passing over callbacks as parameter didn't work consistently + //in the onLocationUpdate method, thats why they have to be set outside + bb.success = success; + bb.error = error; + //function needs to be a string according to + //http://www.tonybunce.com/2008/05/08/Blackberry-Browser-Amp-GPS.aspx + if(opts['timeout']) { + bb.blackberryTimeoutId = setTimeout("handleBlackBerryLocationTimeout()",opts['timeout']); + } else { + //default timeout when none is given to prevent a hanging script + bb.blackberryTimeoutId = setTimeout("handleBlackBerryLocationTimeout()",60000); + } + blackberry.location.onLocationUpdate("handleBlackBerryLocation()"); + blackberry.location.refreshLocation(); + } + provider = blackberry.location; + + } else if ( typeof(Mojo) !=u && typeof(Mojo.Service.Request)!="Mojo.Service.Request") { + provider = true; + pub.getCurrentPosition = function(success, error, opts) { + parameters = {}; + if( opts ) { + //http://developer.palm.com/index.php?option=com_content&view=article&id=1673#GPS-getCurrentPosition + if (opts.enableHighAccuracy && opts.enableHighAccuracy == true ){ + parameters.accuracy = 1; + } + if ( opts.maximumAge ) { + parameters.maximumAge = opts.maximumAge; + } + if (opts.responseTime) { + if( opts.responseTime < 5 ) { + parameters.responseTime = 1; + } else if ( opts.responseTime < 20 ) { + parameters.responseTime = 2; + } else { + parameters.timeout = 3; + } + } + } + + r = new Mojo.Service.Request( 'palm://com.palm.location' , { + method:"getCurrentPosition", + parameters:parameters, + onSuccess: function( p ){ + success( { timestamp: p.timestamp, + coords: { + latitude: p.latitude, + longitude: p.longitude, + heading: p.heading + } + }); + }, + onFailure: function( e ){ + if (e.errorCode==1) { + error({ code: 3, + message: "Timeout" + }); + } else if (e.errorCode==2){ + error({ code: 2, + message: "Position unavailable" + }); + } else { + error({ code: 0, + message: "Unknown Error: webOS-code" + errorCode + }); + } + } + }); + } + + } + else if (typeof(device)!=u && typeof(device.getServiceObject)!=u) { + provider=device.getServiceObject("Service.Location", "ILocation"); + + //override default method implementation + pub.getCurrentPosition = function(success, error, opts){ + function callback(transId, eventCode, result) { + if (eventCode == 4) { + error({message:"Position unavailable", code:2}); + } else { + //no timestamp of location given? + success( { timestamp:null, + coords: { + latitude: result.ReturnValue.Latitude, + longitude: result.ReturnValue.Longitude, + altitude: result.ReturnValue.Altitude, + heading: result.ReturnValue.Heading } + }); + } + } + //location criteria + + var criteria = new Object(); + criteria.LocationInformationClass = "BasicLocationInformation"; + //make the call + provider.ILocation.GetLocation(criteria,callback); + } + } else { + pub.getCurrentPosition = function(success, error, opts) { + pub.jsonp.fetch(ipGeolocationSrv, + function( p ){ success( { timestamp: p.timestamp, + coords: { + latitude: p.latitude, + longitude: p.longitude, + heading: p.heading + } + });}); + } + provider = true; + } + } + catch (e){ + if( typeof(console) != u ) console.log(e); + return false; + } + return provider!=null; + } + return pub; +}(); From 94d935c964d268d11486278805807d9d3c8d227b Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 29 May 2015 16:41:44 +0700 Subject: [PATCH 296/354] ENHANCEMENT: Addition of Nearest Point of Interest page --- code/NearestPOIPage.php | 57 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 code/NearestPOIPage.php diff --git a/code/NearestPOIPage.php b/code/NearestPOIPage.php new file mode 100644 index 0000000..64b5e88 --- /dev/null +++ b/code/NearestPOIPage.php @@ -0,0 +1,57 @@ + 'PointsOfInterestLayer'); + + function getCMSFields() { + $fields = parent::getCMSFields(); + $field = DropdownField::create('PointsOfInterestLayerID', 'PointsOfInterestLayer', + PointsOfInterestLayer::get()->map('ID', 'Title')) + ->setEmptyString('-- Select one --'); + $fields->addFieldToTab('Root.Layer', $field); + + return $fields; + } +} + +class NearestPOIPage_Controller extends Page_Controller { + private static $allowed_actions = array('find'); + + /* + For a given point of interest layer, find the 25 nearest places to provided location + */ + public function find() { + $params = $this->request->getVars(); + $latitude = $params['lat']; + $longitude = $params['lng']; + $sql = "SELECT DISTINCT poi.ID,Name,Lat,Lon, ( 6371 * acos( cos( radians({$latitude}) ) * cos( radians( Lat ) ) +* cos( radians( Lon ) - radians({$longitude}) ) + sin( radians({$latitude}) ) * sin(radians(Lat)) ) ) AS distance +FROM PointOfInterest poi +INNER JOIN PointsOfInterestLayer_PointsOfInterest poilpoi +ON poilpoi.PointOfInterestID = poi.ID +WHERE PointsOfInterestLayerID={$this->PointsOfInterestLayerID} +HAVING distance < 25 +ORDER BY distance +LIMIT 0 , 20;"; + + $records = DB::query($sql); + $result = new ArrayList(); + foreach ($records as $record) { + $dob = new DataObject(); + //number_format((float)$number, 2, '.', ''); + $dob->Latitude = number_format((float)$record['Lat'],3,'.',''); + $dob->Longitude = number_format((float)$record['Lon'],3,'.',''); + $dob->Name = $record['Name']; + $dob->Distance = number_format((float)$record['distance'],2,'.',''); + $mapurl = "http://maps.google.com?q={$dob->Latitude},{$dob->Longitude}"; + $dob->MapURL = $mapurl; + $dirurl = "http://maps.google.com?saddr={$latitude},{$longitude}&daddr={$dob->Latitude},{$dob->Longitude}"; + $dob->DirURL = $dirurl; + $result->push($dob); + } + + $vars = new ArrayData(array('Nearest' => $result, 'Action' => $this->Action)); + $this->Nearest = $result; + return array(); + } +} From adbb016335d9b8f46f87aca55da15d0530d8bb9c Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 11 Jun 2015 21:05:09 +0700 Subject: [PATCH 297/354] MINOR: Removed links to documentation now in the separate POI module --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 7e48ad0..1ff9d42 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,6 @@ Short codes are also provided to render Google Street View and Google maps. * [Adding Lines to Maps](./docs/en/AddingLinesToMaps.md) * [Google Map Short Codes](./docs/en/GoogleMapShortCodes.md) * [Google Streeview Short Codes](./docs/en/GoogleStreetViewShortCodes.md) -* [Points of Interest](./docs/en/PointsOfInterest.md) -* [Using OpenStreetMap Data](./docs/en/OpenStreetMap.md) For more documentation about the module see the provided documentation located inside the docs folder. From ee18f972780cee4bd42804fad7fafc866f86ec06 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 11 Jun 2015 21:05:58 +0700 Subject: [PATCH 298/354] MINOR: Removed Points Of Interest extension as now moved to a separate module --- _config/extensions.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/_config/extensions.yml b/_config/extensions.yml index 34246e9..bdef6bd 100644 --- a/_config/extensions.yml +++ b/_config/extensions.yml @@ -17,7 +17,3 @@ DataList: ArrayList: extensions: - MappableDataObjectSet - -POIMapPage: - extensions: - ['PointsOfInterestLayerExtension','MapExtension'] From 9d17a6603cd8f3c8a16e41bfd12d7cdfabff6aa4 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 11 Jun 2015 21:06:55 +0700 Subject: [PATCH 299/354] MINOR: Moved files to separate POI module --- code/NearestPOIPage.php | 57 ----- code/OSMPointOfInterestExtension.php | 19 -- code/PointOfInterest.php | 48 ---- code/PointsOfInterestAdmin.php | 12 - code/PointsOfInterestLayer.php | 28 --- code/PointsOfInterestLayerExtension.php | 32 --- docs/en/OpenStreetMap.md | 213 ------------------ docs/en/PointsOfInterest.md | 96 -------- .../Layout/PointOfInterest_MapInfoWindow.ss | 1 - 9 files changed, 506 deletions(-) delete mode 100644 code/NearestPOIPage.php delete mode 100644 code/OSMPointOfInterestExtension.php delete mode 100644 code/PointOfInterest.php delete mode 100644 code/PointsOfInterestAdmin.php delete mode 100644 code/PointsOfInterestLayer.php delete mode 100644 code/PointsOfInterestLayerExtension.php delete mode 100644 docs/en/OpenStreetMap.md delete mode 100644 docs/en/PointsOfInterest.md delete mode 100644 templates/Layout/PointOfInterest_MapInfoWindow.ss diff --git a/code/NearestPOIPage.php b/code/NearestPOIPage.php deleted file mode 100644 index 64b5e88..0000000 --- a/code/NearestPOIPage.php +++ /dev/null @@ -1,57 +0,0 @@ - 'PointsOfInterestLayer'); - - function getCMSFields() { - $fields = parent::getCMSFields(); - $field = DropdownField::create('PointsOfInterestLayerID', 'PointsOfInterestLayer', - PointsOfInterestLayer::get()->map('ID', 'Title')) - ->setEmptyString('-- Select one --'); - $fields->addFieldToTab('Root.Layer', $field); - - return $fields; - } -} - -class NearestPOIPage_Controller extends Page_Controller { - private static $allowed_actions = array('find'); - - /* - For a given point of interest layer, find the 25 nearest places to provided location - */ - public function find() { - $params = $this->request->getVars(); - $latitude = $params['lat']; - $longitude = $params['lng']; - $sql = "SELECT DISTINCT poi.ID,Name,Lat,Lon, ( 6371 * acos( cos( radians({$latitude}) ) * cos( radians( Lat ) ) -* cos( radians( Lon ) - radians({$longitude}) ) + sin( radians({$latitude}) ) * sin(radians(Lat)) ) ) AS distance -FROM PointOfInterest poi -INNER JOIN PointsOfInterestLayer_PointsOfInterest poilpoi -ON poilpoi.PointOfInterestID = poi.ID -WHERE PointsOfInterestLayerID={$this->PointsOfInterestLayerID} -HAVING distance < 25 -ORDER BY distance -LIMIT 0 , 20;"; - - $records = DB::query($sql); - $result = new ArrayList(); - foreach ($records as $record) { - $dob = new DataObject(); - //number_format((float)$number, 2, '.', ''); - $dob->Latitude = number_format((float)$record['Lat'],3,'.',''); - $dob->Longitude = number_format((float)$record['Lon'],3,'.',''); - $dob->Name = $record['Name']; - $dob->Distance = number_format((float)$record['distance'],2,'.',''); - $mapurl = "http://maps.google.com?q={$dob->Latitude},{$dob->Longitude}"; - $dob->MapURL = $mapurl; - $dirurl = "http://maps.google.com?saddr={$latitude},{$longitude}&daddr={$dob->Latitude},{$dob->Longitude}"; - $dob->DirURL = $dirurl; - $result->push($dob); - } - - $vars = new ArrayData(array('Nearest' => $result, 'Action' => $this->Action)); - $this->Nearest = $result; - return array(); - } -} diff --git a/code/OSMPointOfInterestExtension.php b/code/OSMPointOfInterestExtension.php deleted file mode 100644 index bd0b213..0000000 --- a/code/OSMPointOfInterestExtension.php +++ /dev/null @@ -1,19 +0,0 @@ - 'Decimal(20,0)' - ); - - // given millions of possible POIs an index is handy - private static $indexes = array( - 'OpenStreetMapID' => true - ); - - - /* The openstreetmap id is only for scripting purposes */ - public function updateCMSFields(FieldList $fields) { - $osmid = $fields->removeByName('OpenStreetMapID'); - } -} diff --git a/code/PointOfInterest.php b/code/PointOfInterest.php deleted file mode 100644 index 873027b..0000000 --- a/code/PointOfInterest.php +++ /dev/null @@ -1,48 +0,0 @@ - 'PointsOfInterestLayer'); - - private static $db = array( - 'Name' => 'Varchar' - ); - - private static $summary_fields = array('Name','Lat','Lon'); - - private static $default_sort = 'Name'; - - function getCMSFields() { - $fields = parent::getCMSFields(); - $fields->addFieldToTab('Root.Main', new TextField('Name', 'Name of the item on the map')); - - $layers = $this->PointsOfInterestLayer(); - $ids = array(); - foreach ($layers->getIterator() as $layer) { - array_push($ids, $layer->ID); - if ($layer->ShowGuideMarkers) { - $this->ShowGuideMarkers = true; - } - } - $csv = implode(',', $ids); - - if ($this->ShowGuideMarkers && strlen($csv) > 0) { - $sql = "ID IN (SELECT DISTINCT PointOfInterestID from "; - $sql .= "PointsOfInterestLayer_PointsOfInterest WHERE PointsOfInterestLayerID "; - $sql .= "IN ($csv))"; - - $pois = PointOfInterest::get()->where($sql); - $this->owner->getMapField()->setGuidePoints($pois); - } - - return $fields; - } - - /* - FIXME - possible to populate layer id when adding a new record? - public function populateDefaults() { - parent::populateDefaults(); - } - */ -} diff --git a/code/PointsOfInterestAdmin.php b/code/PointsOfInterestAdmin.php deleted file mode 100644 index fbc6447..0000000 --- a/code/PointsOfInterestAdmin.php +++ /dev/null @@ -1,12 +0,0 @@ - 'Image' - ); -} diff --git a/code/PointsOfInterestLayer.php b/code/PointsOfInterestLayer.php deleted file mode 100644 index ec3f169..0000000 --- a/code/PointsOfInterestLayer.php +++ /dev/null @@ -1,28 +0,0 @@ - 'Varchar', - 'ShowGuideMarkers' => 'Boolean' - ); - - private static $many_many = array('PointsOfInterest' => 'PointOfInterest'); - - static $has_one = array( - 'DefaultIcon' => 'Image' - ); - - function getCMSFields() { - $fields = parent::getCMSFields(); - $fields->addFieldToTab('Root.Main', new TextField('Name', 'Name of this layer')); - $fields->addFieldToTab('Root.Main', - $uf = new UploadField('DefaultIcon', - _t('PointsOfInterest.ICON', - 'Default Icon')) - ); - $fields->addFieldToTab('Root.Main', new CheckboxField('ShowGuideMarkers', - 'Show grey guide markers of others points in this layer')); - $uf->setFolderName('mapicons'); - - return $fields; - } -} diff --git a/code/PointsOfInterestLayerExtension.php b/code/PointsOfInterestLayerExtension.php deleted file mode 100644 index 29e0580..0000000 --- a/code/PointsOfInterestLayerExtension.php +++ /dev/null @@ -1,32 +0,0 @@ - 'PointsOfInterestLayer' - ); - - static $belongs_many_many_extraFields = array( - 'PointsOfInterestLayers' => array( - 'SortOrder' => "Int" - ) - ); - - /** - * Update cms fields - add list of POIs - * @param FieldList $fields list of existing fields on the object - */ - public function updateCMSFields(FieldList $fields) { - $gridConfig2 = GridFieldConfig_RelationEditor::create(); - $gridConfig2->getComponentByType( - 'GridFieldAddExistingAutocompleter')-> - setSearchFields(array('Name') - ); - $gridConfig2->getComponentByType('GridFieldPaginator')->setItemsPerPage(100); - $gridField2 = new GridField("POI Layers", "POI Layers:", - $this->owner->PointsOfInterestLayers(), - $gridConfig2 - ); - $fields->addFieldToTab("Root.MapLayers", $gridField2); - } -} diff --git a/docs/en/OpenStreetMap.md b/docs/en/OpenStreetMap.md deleted file mode 100644 index 2ae4a33..0000000 --- a/docs/en/OpenStreetMap.md +++ /dev/null @@ -1,213 +0,0 @@ -#OpenStreetMap -##Importing OSM Data -The import process is documented numerously on the internet, a good one for Ubuntu is -https://switch2osm.org/serving-tiles/manually-building-a-tile-server-14-04/ - once set up an import -of the OSM data for Thailand took around 20 minutes. - -Snapshots of OSM data are available from http://download.geofabrik.de/ - -##Extracting Points of Interest -The process of extracting points of interest is as follows: -* Select the points you are interested in from the PostGIS database into which the OSM data was -imported. Use SQL and output to a file. -* Parse the result output using a language of your choice to create SQL that can be imported into -SilverStripe as points of interest. -* Import the resultant SQL into your SilverStripe database. - -## Worked Example - Thai Railway Stations -###Extract Data from PostGIS OSM Database -Create a file of arbitrary name, e.g. osm.sql, with the following query, which extracts OSM id, -name, latitude, longitude and tags for nodes: -```sql -select osm_id,name, amenity, -ST_Y((ST_Transform (way, 4326))) as Latitude, -ST_X((ST_Transform (way, 4326))) as Longitude, -tags -from planet_osm_nodes N -inner join planet_osm_point P -on N.id = P.osm_id -; -``` - -At the command line execute this query and grep for railway stations: -```bash -psql gis < osm.sql | grep -i railway > trains.osm -``` -Note that authentication to the Postgres database may differ. - -Analysis of the output shows that further grepping is require in order to extract just stations. -``` -.77085717602143 | 100.437159389841 | {railway,level_crossing} - 3286015332 | Bang Klam | | 7.08769743171173 | 100.415422944743 | {railway,station,name,"Bang Klam"} - 3287950085 | Death Railway viaduct | | 14.1534710197064 | 99.1098331602021 | {tourism,attraction,name,"Death Railway viaduct"} - 3288538284 | | | 9.14452066569457 | 99.1675465934402 | {railway,level_crossing} - 3288549665 | -``` -Execute the following to get just the stations: - ``` -grep -i station trains.osm > stations.osm - ``` -###Create Points of Interest Layer -In the SilverStripe model admin interface create a new PointsOfInterestLayer to contain the railway -stations. Note that a common -icon for the layer can be added here, if none is provided the standard Google Map pin is used. - -![Adding a new points of interest layer] -(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-create-railway-layer.png?raw=true -"Adding a new points of interest layer") - -Obtain the database ID, in this case the value 3. This is required for scripting purposes. - -```sql -mysql> select * from PointsOfInterestLayer; -+----+-----------------------+---------------------+---------------------+------------------------------+---------------+ -| ID | ClassName | Created | LastEdited | Name | DefaultIconID | -+----+-----------------------+---------------------+---------------------+------------------------------+---------------+ -| 1 | PointsOfInterestLayer | 2015-01-13 15:15:27 | 2015-01-13 16:26:01 | BTS Stations | 123 | -| 2 | PointsOfInterestLayer | 2015-03-06 15:57:28 | 2015-03-10 17:38:25 | Seven Elevens in Thailand | 126 | -| 3 | PointsOfInterestLayer | 2015-03-13 11:26:16 | 2015-03-13 11:26:16 | Railway Stations in Thailand | 123 | -+----+-----------------------+---------------------+---------------------+------------------------------+---------------+ -``` - -###Convert Extracted Data to SQL for Import Into SilverStripe -The following is an example Ruby script to extract the English name from the tags field, if one is -defined, and output -SQL that can be imported directly into the SilverStripe database for the site in question. It was -saved as parse_osm.rb, the name is of course arbitrary. - -```ruby -filename = ARGV[0] -layerid = ARGV[1] -ctr = 0 - -puts "/* Extracting from #{filename} */" -puts "begin;" -File.open(filename) do |file| - file.each {|line| \ - ctr = ctr + 1 - if ctr < 3 - next - end - splits = line.split('|') - if (splits.length == 6) - puts - osm_id = splits[0] - name = splits[1] - lat = splits[3] - lon = splits[4].strip - - #Tags are a comma separated list of key value pairs - tags = {} - tagtxt = splits[5].strip - tagtxt[0] = '' - tagtxt[-1] = '' - tagcols = tagtxt.split ',' - tagname = 'UNDEFINED' - while tagcols.length > 0 - value = tagcols.pop - key = tagcols.pop - tags[key] = value - end - - - if tags['name:en'] - tagname = tags['name:en'] - else - tagname = tags['name'] - end - - if tagname == nil - tagname = "UNDEFINED" - end - - # Remove quotation marks - if tagname[0] == '"' - tagname[0]='' - end - - if tagname[-1] == '"' - tagname[-1]='' - end - - tagname.strip! - - if tagname != 'UNDEFINED' - sql = "INSERT INTO PointOfInterest(OpenStreetMapID,Name,Lat,Lon,ZoomLevel,Created,LastEdited,MapPinEdited) VALUES (#{osm_id},'#{tagname}',#{lat},#{lon},16,now(),now(),true);" - puts sql - sql = "INSERT INTO PointsOfInterestLayer_PointsOfInterest(PointsOfInterestLayerID,PointOfInterestID) VALUES(#{layerid}, LAST_INSERT_ID());" - puts sql - end - - end - } -end - -puts "commit;" -``` - -To use this script the format is -``` -ruby parse_osm.rb -``` -so in the case of the example above -``` -ruby parse_osm.rb stations.osm 3 -``` - -Output is many rows of SQL like this, the first line of each pair being the creation of the point -of interest and the second associating it with the point of interest layer representing the -stations. - -```sql -INSERT INTO PointOfInterest(OpenStreetMapID,Name,Lat,Lon,ZoomLevel,Created,LastEdited,MapPinEdited) -VALUES ( 236480470 ,'Khlong Phutsa', 14.1860507762646 ,100.578314006055,16,now(),now(),true); -INSERT INTO PointsOfInterestLayer_PointsOfInterest(PointsOfInterestLayerID,PointOfInterestID) -VALUES(3, LAST_INSERT_ID()); - -INSERT INTO PointOfInterest(OpenStreetMapID,Name,Lat,Lon,ZoomLevel,Created,LastEdited,MapPinEdited) -VALUES ( 237445803 ,'Railway station Ayutthaya', 14.3567211927894 ,100.58319491232,16,now(),now(),true); -INSERT INTO PointsOfInterestLayer_PointsOfInterest(PointsOfInterestLayerID,PointOfInterestID) -VALUES(3, LAST_INSERT_ID()); - -``` -Note that the method LAST_INSERT_ID() is MySQL centric. If your SilverStripe database is hosted -using PostgreSQL then change this to 'currval()'. - -An improvement to this script would be the addition of escaping quotes but it wasn't a necessity -for the data being loaded. - -### Add Points Of Interest Layer to an Existing Page -Add the new layer in the 'Map Layers' tab for any page using the PointsOfInterestLayerExtension -extension. Type the word 'Railway' into the search box to the right of 'Points of Interest Layer'. -After a couple of seconds select 'Railway Stations of Thailand' and click 'Link Existing'. -Save and publish the page. -* During the process of adding the railway stations layer -![Adding railway layer] -(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-adding-railway-layer.png?raw=true -"Adding railway layer") -* After the railway stations layer was added -![Added railway layer] -(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-adding-railway-layer-2.png?raw=true -"Added railway layer") - -###View Data in SilverStripe -The imported railway stations can now be seen and edited in the model admin interface. -* List of railway stations. ![Railway Stations as POIs in Model Admin] -(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-imported-railway-stations.png?raw=true -"Railway Stations as POIs in Model Admin") -* Editing the entry for Bankrut Railway Station (1/2). ![Editing a single station] -(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-editing-bankrut.png?raw=true -"Editing a single station (1/2)") -* Editing the entry for Bankrut Railway Station (2/2). ![Editing a single station] -(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-single-station-location.png?raw=true -"Editing a single station (2/2)") - - -###Public Rendered View -####Map of Thailand with Railway Stations POIs Marked as Clusters -* The entire country ![Railway Stations in Thailand] -(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-clustered-stations.png?raw=true -"Railway Stations in Thailand") -* Zoomed in to show individual stations ![Railway Stations in Thailand - Chachaengsao Area] -(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/poi-clustered-stations-zoomin.png?raw=true -"Railway Stations in Thailand - Chachaengsao Area") diff --git a/docs/en/PointsOfInterest.md b/docs/en/PointsOfInterest.md deleted file mode 100644 index b0c592d..0000000 --- a/docs/en/PointsOfInterest.md +++ /dev/null @@ -1,96 +0,0 @@ -#Points of Interest -A point of interest (or POI) denotes a location on a map that is of interest, for example a bridge, -a railway station, or a level crossing. They are grouped together in layers, a PointOfInterest -having a many many relationship with PointsOfInterestLayer. This allows for the use case where -"Crewe Railway Station" can appear in a "Railway Stations of Great Britain" layer as well as a -"Railway Stations of England" layer. - -#Adding Points of Interest to a Page Type -Whilst points of interest layers are available to edit after installing the Mappable module, they -need to be added to a DataObject for rendering purposes. Do this using the standard extensions -mechanism in a yml file. Note that MapExtension is also required, as this has the method -BasicMap() for rendering the map in a template. - -```yml -PageWithPointsOfInterest: - extensions: - ['MapExtension', 'PointsOfInterestLayerExtension'] -``` -If you wish to remove the Location tab in this case add the following in your equivalent class: - -```php - function getCMSFields() { - $fields = parent::getCMSFields(); - $fields->removeByName('Location'); - return $fields; - } -``` - -#Editing -A new model admin tab is available called "Points of Interest". Here you can add new layers or -edit existing ones. - -##Adding a New Layer -Add a new layer, in this case "Supermarkets of Bangkok" and save it. -![Adding a new layer] -(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/001-poi-create-new-layer.png?raw=true -"Adding a new layer") - - -##Adding a new Point of Interest to that Layer -![Adding a new supermarket] -(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/003-poi-create-new-point-location.png?raw=true -"Adding a new supermarket") -![Editing Location] -(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/004-poi-create-new-point-location.png?raw=true "Editing Location") -![Point of Interest Saved] -(https://github.com/gordonbanderson/Mappable/blob/screenshots/screenshots/005-poi-saved.png?raw=true -"Point of Interest Saved") - -#Rendering -The method BasicMap in MapExtension takes into account points of interest when rendering a map. -If you full control of the rendering from within the object containing POIs then use this code as -a basis: - -```php -public function BicycleRideMap() { - $map = $this->owner->getRenderableMap(); - $map->setZoom( $this->owner->ZoomLevel ); - $map->setAdditionalCSSClasses( 'fullWidthMap' ); - $map->setShowInlineMapDivStyle( true ); - $map->setSize('100%', '500px'); - - if (Object::has_extension($this->owner->ClassName, 'MapLayerExtension')) { - foreach($this->owner->MapLayers() as $layer) { - $map->addKML($layer->KmlFile()->getAbsoluteURL()); - } - } - - // add points of interest taking into account the default icon of the layer as an override - if (Object::has_extension($this->owner->ClassName, 'PointsOfInterestLayerExtension')) { - foreach($this->owner->PointsOfInterestLayers() as $layer) { - $layericon = $layer->DefaultIcon(); - if ($layericon->ID === 0) { - $layericon = null; - } - foreach ($layer->PointsOfInterest() as $poi) { - if ($poi->MapPinEdited) { - if ($poi->MapPinIconID == 0) { - $poi->CachedMapPin = $layericon; - } - $map->addMarkerAsObject($poi); - } - } - } - $map->setClusterer( true ); - $map->setEnableAutomaticCenterZoom(true); - } - - return $map; -} -``` - -#Page with Points of Interest Layer -A page type, POIMapPage, is included in the Mappable module. It is the same as -a page, with the addition of MapExtension and PointsOfInterestLayerException. You -will probably have to override the template, POIMapPage.ss in your theme. diff --git a/templates/Layout/PointOfInterest_MapInfoWindow.ss b/templates/Layout/PointOfInterest_MapInfoWindow.ss deleted file mode 100644 index 5f4079a..0000000 --- a/templates/Layout/PointOfInterest_MapInfoWindow.ss +++ /dev/null @@ -1 +0,0 @@ -$Name From 15f62ea7f61138c1b4ccbce1d5281a04a349657e Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Thu, 11 Jun 2015 21:59:57 +0700 Subject: [PATCH 300/354] FIX: Ensure mappable is dependent on SilverStripe --- composer.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/composer.json b/composer.json index f0b09a4..ccaecdb 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,9 @@ "role": "Developer" } ], + "require": { + "silverstripe/framework": "~3.1" + }, "support": { "issues": "https://github.com/gordonbanderson/Mappable/issues" }, From c13b40868a6a1a3bb5a30f6e33ca09a7257c2512 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 12 Jun 2015 14:54:31 +0700 Subject: [PATCH 301/354] FIX: Add license --- LICENSE.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..40527d1 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,20 @@ + From d71035069301fc0ce07ce2d64353a07034d38e0a Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Fri, 12 Jun 2015 14:54:56 +0700 Subject: [PATCH 302/354] FIX: Add license --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ccaecdb..ab9aac4 100644 --- a/composer.json +++ b/composer.json @@ -20,5 +20,6 @@ "require": { "silverstripe/framework": "~3.1" - } + }, + "license": "MIT" } From 58f8d09d3baba9d47da90402cff35138e79709eb Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 13 Jun 2015 21:23:09 +0700 Subject: [PATCH 303/354] FIX: Ensure Google maps is initialised correctly for the default Simple theme --- javascript/google/maputil.js | 682 ++++++++++++++++++----------------- 1 file changed, 350 insertions(+), 332 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index 914b7ef..c88bb0e 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -1,402 +1,420 @@ -// FIXME - make non global -var infoWindows = []; +var primeMap; + +(function($) { + + // FIXME - make non global + var infoWindows = []; + + /** + * Create a google map pin from data provided + * @param {GoogleMap} map Instance of a google map + * @param {Number} lat Latitude of pin + * @param {Number} lng Longitude of pin + * @param {String} html HTML for information window + * @param {String} icon URL of alternative map icon, or blank for default + * @param {boolean} useClusterer Whether or not to use clusterer + * @param {boolean} enableWindowZoom Whether or not to enable zoom on the rendered map + * @param {boolean} defaultHideMarker Whether or not to hide markers initially + * @return {MapMarker} Google map pin object + */ + function createMarker(map, lat, lng, html, category, icon, useClusterer, enableWindowZoom, + defaultHideMarker) { + + mapId = map.getDiv().getAttribute('id'); + + var marker = new google.maps.Marker(); + marker.setPosition(new google.maps.LatLng(lat, lng)); + marker.mycategory = category; + + if (icon && icon !== '') { + var image = new google.maps.MarkerImage(icon); + marker.setIcon(image); + } + if (!useClusterer) { + marker.setMap(map); + } -function loadGoogleMapsScript() { - var script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = 'https://maps.googleapis.com/maps/api/js?' + - '&sensor=false&callback=loadedGoogleMapsAPI&hl=en'; - document.body.appendChild(script); -} -window.addEventListener ? - window.addEventListener("load",loadGoogleMapsScript,false) : -window.attachEvent && window.attachEvent("onload",loadGoogleMapsScript); - - -/** - * Create a google map pin from data provided - * @param {GoogleMap} map Instance of a google map - * @param {Number} lat Latitude of pin - * @param {Number} lng Longitude of pin - * @param {String} html HTML for information window - * @param {String} icon URL of alternative map icon, or blank for default - * @param {boolean} useClusterer Whether or not to use clusterer - * @param {boolean} enableWindowZoom Whether or not to enable zoom on the rendered map - * @param {boolean} defaultHideMarker Whether or not to hide markers initially - * @return {MapMarker} Google map pin object - */ -function createMarker(map, lat, lng, html, category, icon, useClusterer, enableWindowZoom, - defaultHideMarker) { - - mapId = map.getDiv().getAttribute('id'); - - var marker = new google.maps.Marker(); - marker.setPosition(new google.maps.LatLng(lat, lng)); - marker.mycategory = category; - - if (icon && icon !== '') { - var image = new google.maps.MarkerImage(icon); - marker.setIcon(image); - } + google.maps.event.addListener(marker, "click", function() { + if (enableWindowZoom) { + map.setCenter(new google.maps.LatLng(lat, lng), 12); // $InfoWindowZoom); + } + var infoWindow = infoWindows[mapId]; + infoWindow.setContent(html); + infoWindow.open(map, this); + }); - if (!useClusterer) { - marker.setMap(map); - } + //FIXME gmarkers[mapId].push(marker); - google.maps.event.addListener(marker, "click", function() { - if (enableWindowZoom) { - map.setCenter(new google.maps.LatLng(lat, lng), 12); // $InfoWindowZoom); + if (defaultHideMarker) { + marker.hide(); } - var infoWindow = infoWindows[mapId]; - infoWindow.setContent(html); - infoWindow.open(map, this); - }); + return marker; + } - //FIXME gmarkers[mapId].push(marker); - if (defaultHideMarker) { - marker.hide(); + /** + * Get the current latitude + * @return {float} Current latitude + */ + function getCurrentLat() { + return current_lat; } - return marker; -} -/** - * Get the current latitude - * @return {float} Current latitude - */ -function getCurrentLat() { - return current_lat; -} + /** + * Get the current longitude + * @return {float} The current longitude + */ + function getCurrentLng() { + return current_lng; + } -/** - * Get the current longitude - * @return {float} The current longitude - */ -function getCurrentLng() { - return current_lng; -} + /** + * Convert JSON point data into google map markers + * @param {GoogleMap} map Google Map instance + * @param {array} markers point data loaded from JSON + * @param {boolean} useClusterer Whether or not to use the clusterer + * @param {boolean} enableWindowZoom Whether or not zoom is enabled + * @param {boolean} defaultHideMarker Whether or not to hide markers + * * @return array of Google map markers converted from the JSON data + */ + function addAllMarkers(map, markers, useClusterer, enableWindowZoom, defaultHideMarker) { + // these will be altered by adding markers + map.minLat = 1000000; + map.minLng = 1000000; + map.maxLat = -1000000; + map.maxLng = -1000000; -/** - * Convert JSON point data into google map markers - * @param {GoogleMap} map Google Map instance - * @param {array} markers point data loaded from JSON - * @param {boolean} useClusterer Whether or not to use the clusterer - * @param {boolean} enableWindowZoom Whether or not zoom is enabled - * @param {boolean} defaultHideMarker Whether or not to hide markers - * * @return array of Google map markers converted from the JSON data - */ -function addAllMarkers(map, markers, useClusterer, enableWindowZoom, defaultHideMarker) { - // these will be altered by adding markers - map.minLat = 1000000; - map.minLng = 1000000; - map.maxLat = -1000000; - map.maxLng = -1000000; + var allmarkers = []; + for (var i = 0; i < markers.length; i++) { + var markerinfo = markers[i]; + var marker = createMarker(map, markerinfo.latitude, markerinfo.longitude, markerinfo.html, + markerinfo.category, markerinfo.icon, useClusterer, enableWindowZoom, + defaultHideMarker); + var latitude = parseFloat(markerinfo.latitude); + var longitude = parseFloat(markerinfo.longitude); - var allmarkers = []; - for (var i = 0; i < markers.length; i++) { - var markerinfo = markers[i]; - var marker = createMarker(map, markerinfo.latitude, markerinfo.longitude, markerinfo.html, - markerinfo.category, markerinfo.icon, useClusterer, enableWindowZoom, - defaultHideMarker); + // update lat,lon min to lat,lon max + if (latitude < map.minLat) { + map.minLat = latitude; - var latitude = parseFloat(markerinfo.latitude); - var longitude = parseFloat(markerinfo.longitude); + } + if (latitude > map.maxLat) { + map.maxLat = latitude; + } - // update lat,lon min to lat,lon max - if (latitude < map.minLat) { - map.minLat = latitude; + if (longitude > map.maxLng) { + map.maxLng = longitude; - } - if (latitude > map.maxLat) { - map.maxLat = latitude; - } + } - if (longitude > map.maxLng) { - map.maxLng = longitude; + if (longitude < map.minLng) { + map.minLng = longitude; + } + allmarkers.push(marker); } - if (longitude < map.minLng) { - map.minLng = longitude; - } - allmarkers.push(marker); + var centreCoordinates = []; + centreCoordinates.lat = (parseFloat(map.minLat) + parseFloat(map.maxLat)) / 2; + centreCoordinates.lng = (parseFloat(map.minLng) + parseFloat(map.maxLng)) / 2; + map.centreCoordinates = centreCoordinates; + return allmarkers; } - var centreCoordinates = []; - centreCoordinates.lat = (parseFloat(map.minLat)+parseFloat(map.maxLat))/2; - centreCoordinates.lng = (parseFloat(map.minLng)+parseFloat(map.maxLng))/2; - map.centreCoordinates = centreCoordinates; - return allmarkers; -} - - -/** - * Add lines to a Google map - * @param {googleMap} map Google map instance - * @param {array} lines Line data loaded from json, lat1,lon1 to lat2,lon2 - */ -function addLines(map, lines) { - for (i = 0; i < lines.length; i++) { - var line = lines[i]; - var point1 = new google.maps.LatLng(line.lat1, line.lon1); - var point2 = new google.maps.LatLng(line.lat2, line.lon2); - var points = [point1, point2]; - var pl = new google.maps.Polyline({ - path: points, - strokeColor: line.color, - strokeWeight: 4, - strokeOpacity: 0.8 - }); - pl.setMap(map); + /** + * Add lines to a Google map + * @param {googleMap} map Google map instance + * @param {array} lines Line data loaded from json, lat1,lon1 to lat2,lon2 + */ + function addLines(map, lines) { + for (i = 0; i < lines.length; i++) { + var line = lines[i]; + var point1 = new google.maps.LatLng(line.lat1, line.lon1); + var point2 = new google.maps.LatLng(line.lat2, line.lon2); + var points = [point1, point2]; + var pl = new google.maps.Polyline({ + path: points, + strokeColor: line.color, + strokeWeight: 4, + strokeOpacity: 0.8 + }); + pl.setMap(map); + } } -} -/** - * Add one or more (max of 25) KML files to a Google map - * @param {GoogleMap} map A Google Map instance - * @param {array} kmlFiles array of URLs for KML files - */ -function addKmlFiles(map, kmlFiles) { - for (var i = 0; i < kmlFiles.length; i++) { - var kmlFile = kmlFiles[i]; - var kmlLayer = new google.maps.KmlLayer(kmlFile, { - suppressInfoWindows: true, - map: map - }); + /** + * Add one or more (max of 25) KML files to a Google map + * @param {GoogleMap} map A Google Map instance + * @param {array} kmlFiles array of URLs for KML files + */ + function addKmlFiles(map, kmlFiles) { + for (var i = 0; i < kmlFiles.length; i++) { + var kmlFile = kmlFiles[i]; + var kmlLayer = new google.maps.KmlLayer(kmlFile, { + suppressInfoWindows: true, + map: map + }); + } } -} -/** - * Convert a map type name (road,satellite,hybrid,terrain) to Google map types - * @param String mapTypeName generic name of the map type - * @return google.maps.MapTypeId map type in Google format - */ -function convertMapType(mapTypeName) { - var result = google.maps.MapTypeId.ROADMAP; - switch (mapTypeName) { - case 'aerial': - result = google.maps.MapTypeId.SATELLITE; - break; - case 'hybrid': - result = google.maps.MapTypeId.HYBRID; - break; - case 'terrain': - result = google.maps.MapTypeId.TERRAIN; - break; + /** + * Convert a map type name (road,satellite,hybrid,terrain) to Google map types + * @param String mapTypeName generic name of the map type + * @return google.maps.MapTypeId map type in Google format + */ + function convertMapType(mapTypeName) { + var result = google.maps.MapTypeId.ROADMAP; + switch (mapTypeName) { + case 'aerial': + result = google.maps.MapTypeId.SATELLITE; + break; + case 'hybrid': + result = google.maps.MapTypeId.HYBRID; + break; + case 'terrain': + result = google.maps.MapTypeId.TERRAIN; + break; + } + return result; } - return result; -} - - -/** - * After the Google Maps API has been loaded init the relevant Google maps - */ -function loadShortCodeStreetView() { - - var svs = $('div[data-streetview]'); - - svs.each(function(index) { - var svnode = $(this); - var svnodeid = svnode.attr('id'); - var lat = parseFloat(svnode.attr('data-latitude')); - var lon = parseFloat(svnode.attr('data-longitude')); - var zoom = parseInt(svnode.attr('data-zoom')); - var heading = parseFloat(svnode.attr('data-heading')); - var pitch = parseFloat(svnode.attr('data-pitch')); - - var location = new google.maps.LatLng(lat,lon); - var panoramaOptions = { - position: location, - pov: { - heading: heading, - pitch: pitch - }, - zoom: zoom - }; + /** + * After the Google Maps API has been loaded init the relevant Google maps + */ + function loadShortCodeStreetView() { - var domNode = document.getElementById(svnodeid); - var pano = new google.maps.StreetViewPanorama( - domNode, - panoramaOptions); - pano.setVisible(true); - }); + var svs = $('div[data-streetview]'); + svs.each(function(index) { + var svnode = $(this); + var svnodeid = svnode.attr('id'); + var lat = parseFloat(svnode.attr('data-latitude')); + var lon = parseFloat(svnode.attr('data-longitude')); + var zoom = parseInt(svnode.attr('data-zoom')); + var heading = parseFloat(svnode.attr('data-heading')); + var pitch = parseFloat(svnode.attr('data-pitch')); + var location = new google.maps.LatLng(lat, lon); - -} - - -/** - * After the Google Maps API has been loaded init the relevant Google maps - */ -function loadShortCodeMaps() { - - var scms = $('div[data-shortcode-map]'); - - scms.each(function(index) { - var scmnode = $(this); - var scmnodeid = scmnode.attr('id'); - var maptype = convertMapType(scmnode.attr('data-maptype')); - var lat = parseFloat(scmnode.attr('data-latitude')); - var lng = parseFloat(scmnode.attr('data-longitude')); - var zoom = parseInt(scmnode.attr('data-zoom')); - var allowfullscreen = parseInt(scmnode.attr('data-allowfullscreen')); - - // Google Maps API has already been loaded, so init Google Map - var mapOptions = { - center: { lat:lat, lng: lng}, - zoom: zoom, - mapTypeId: maptype - }; - + var panoramaOptions = { + position: location, + pov: { + heading: heading, + pitch: pitch + }, + zoom: zoom + }; - var gmap = new google.maps.Map(document.getElementById(scmnodeid), - mapOptions); - if (allowfullscreen == 1) { - gmap.controls[google.maps.ControlPosition.TOP_RIGHT].push( - FullScreenControl(gmap, "Full Screen", "Original Size") - ); - } - }); - + var domNode = document.getElementById(svnodeid); + var pano = new google.maps.StreetViewPanorama( + domNode, + panoramaOptions); + pano.setVisible(true); + }); -} -/** - * Callback function after the Google Maps API has been loaded - renders the maps along with - * associated points of interest and layers - */ -function loadedGoogleMapsAPI() { - loadShortCodeMaps(); - loadShortCodeStreetView(); + } - var maps = $('div[data-map]'); - maps.each(function(index) { - var mapnode = $(this); - mapdomid = mapnode.attr('id'); - var map = new google.maps.Map(document.getElementById(mapdomid)); + /** + * After the Google Maps API has been loaded init the relevant Google maps + */ + function loadShortCodeMaps() { + + var scms = $('div[data-shortcode-map]'); + + scms.each(function(index) { + var scmnode = $(this); + var scmnodeid = scmnode.attr('id'); + var maptype = convertMapType(scmnode.attr('data-maptype')); + var lat = parseFloat(scmnode.attr('data-latitude')); + var lng = parseFloat(scmnode.attr('data-longitude')); + var zoom = parseInt(scmnode.attr('data-zoom')); + var allowfullscreen = parseInt(scmnode.attr('data-allowfullscreen')); + + // Google Maps API has already been loaded, so init Google Map + var mapOptions = { + center: { + lat: lat, + lng: lng + }, + zoom: zoom, + mapTypeId: maptype + }; + + + + var gmap = new google.maps.Map(document.getElementById(scmnodeid), + mapOptions); + if (allowfullscreen == 1) { + gmap.controls[google.maps.ControlPosition.TOP_RIGHT].push( + FullScreenControl(gmap, "Full Screen", "Original Size") + ); + } + }); - // initialise geocoder - geocoder = new google.maps.Geocoder(); - // default of [] renders google maps as per normal - if (mapnode.attr('data-mapstyles')) { - var json=$.parseJSON(mapnode.attr('data-mapstyles')); - map.setOptions({styles: json}); - }; + } - if (mapnode.data['data-allowfullscreen']) { - map.controls[google.maps.ControlPosition.TOP_RIGHT].push( - FullScreenControl(map, "Full Screen", "Original Size") - ); - } - var markerjson = $.parseJSON(mapnode.attr('data-mapmarkers')); - var useClusterer = mapnode.attr('data-useclusterer'); - var enableAutomaticCenterZoom = mapnode.attr('data-enableautocentrezoom'); - var defaultHideMarker = mapnode.attr('data-defaulthidemarker'); - var markers = addAllMarkers(map, markerjson, useClusterer, - enableAutomaticCenterZoom, defaultHideMarker); - var allowfullscreen = parseInt(mapnode.attr('data-allowfullscreen')); - - if (enableAutomaticCenterZoom == '1') { - centre = $.parseJSON(mapnode.attr('data-centre')); - map.setCenter(new google.maps.LatLng(centre.lat, centre.lng)); - - var bds = new google.maps.LatLngBounds(new google.maps.LatLng(map.minLat, map.minLng), - new google.maps.LatLng(map.maxLat, map.maxLng)); - map.fitBounds(bds); - } else { - centre = $.parseJSON(mapnode.attr('data-centre')); - map.setCenter(new google.maps.LatLng(centre.lat, centre.lng)); - var zoom = parseInt(mapnode.attr('data-zoom')); - map.setZoom(zoom); - } + /** + * Callback function after the Google Maps API has been loaded - renders the maps along with + * associated points of interest and layers + */ + primeMap = function loadedGoogleMapsAPI() { + loadShortCodeMaps(); + loadShortCodeStreetView(); + + var maps = $('div[data-map]'); + + maps.each(function(index) { + var mapnode = $(this); + mapdomid = mapnode.attr('id'); + var map = new google.maps.Map(document.getElementById(mapdomid)); + + // initialise geocoder + geocoder = new google.maps.Geocoder(); + + // default of [] renders google maps as per normal + if (mapnode.attr('data-mapstyles')) { + var json = $.parseJSON(mapnode.attr('data-mapstyles')); + map.setOptions({ + styles: json + }); + } + + if (mapnode.data['data-allowfullscreen']) { + map.controls[google.maps.ControlPosition.TOP_RIGHT].push( + FullScreenControl(map, "Full Screen", "Original Size") + ); + } + + var markerjson = $.parseJSON(mapnode.attr('data-mapmarkers')); + var useClusterer = mapnode.attr('data-useclusterer'); + var enableAutomaticCenterZoom = mapnode.attr('data-enableautocentrezoom'); + var defaultHideMarker = mapnode.attr('data-defaulthidemarker'); + var markers = addAllMarkers(map, markerjson, useClusterer, + enableAutomaticCenterZoom, defaultHideMarker); + var allowfullscreen = parseInt(mapnode.attr('data-allowfullscreen')); + + if (enableAutomaticCenterZoom == '1') { + centre = $.parseJSON(mapnode.attr('data-centre')); + map.setCenter(new google.maps.LatLng(centre.lat, centre.lng)); + + var bds = new google.maps.LatLngBounds(new google.maps.LatLng(map.minLat, map.minLng), + new google.maps.LatLng(map.maxLat, map.maxLng)); + map.fitBounds(bds); + } else { + centre = $.parseJSON(mapnode.attr('data-centre')); + map.setCenter(new google.maps.LatLng(centre.lat, centre.lng)); + var zoom = parseInt(mapnode.attr('data-zoom')); + map.setZoom(zoom); + } + + if (allowfullscreen == 1) { + map.controls[google.maps.ControlPosition.TOP_RIGHT].push( + FullScreenControl(map, "Full Screen", "Original Size") + ); + } + + var googlemaptype = convertMapType(mapnode.attr('data-maptype')); + map.setMapTypeId(googlemaptype); + + if (useClusterer) { + // ensure zoom and grid size are integers by prefixing with unary plus + var clustererGridSize = parseInt(mapnode.attr('data-clusterergridsize')); + var clustererMaxZoom = parseInt(mapnode.attr('data-clusterermaxzoom')); + var mcOptions = { + gridSize: clustererGridSize, + maxZoom: clustererMaxZoom + }; + var markerCluster = new MarkerClusterer(map, markers, mcOptions); + } + + var lines = $.parseJSON(mapnode.attr('data-lines')); + addLines(map, lines); + + var kmlFiles = $.parseJSON(mapnode.attr('data-kmlfiles')); + addKmlFiles(map, kmlFiles); + + var infoWindow = new google.maps.InfoWindow({ + content: '' + }); + infoWindows[mapdomid] = infoWindow; + + // trigger an event now that the map has been initialised + // Use this for example to add listeners to the map from another JS file + mapnode.trigger("mapInitialised", [map]); + }); + }; - if (allowfullscreen == 1) { - map.controls[google.maps.ControlPosition.TOP_RIGHT].push( - FullScreenControl(map, "Full Screen", "Original Size") - ); - } - var googlemaptype = convertMapType(mapnode.attr('data-maptype')); - map.setMapTypeId(googlemaptype); - if (useClusterer) { - // ensure zoom and grid size are integers by prefixing with unary plus - var clustererGridSize = parseInt(mapnode.attr('data-clusterergridsize')); - var clustererMaxZoom = parseInt(mapnode.attr('data-clusterermaxzoom')); - var mcOptions = {gridSize: clustererGridSize, maxZoom: clustererMaxZoom}; - var markerCluster = new MarkerClusterer(map,markers,mcOptions); - } + function nearestPOIs() { - var lines =$.parseJSON(mapnode.attr('data-lines')); - addLines(map, lines); + // normally will only be one + var nears = $('div[data-nearest-poi]'); - var kmlFiles =$.parseJSON(mapnode.attr('data-kmlfiles')); - addKmlFiles(map, kmlFiles); + nears.each(function(index) { + var nearnode = $(this); + var layerID = nearnode.attr('data-layer-id'); - var infoWindow = new google.maps.InfoWindow({ - content: '' + if (geoPosition.init()) { // Geolocation Initialisation + geoPosition.getCurrentPosition(success_callback, error_callback, { + enableHighAccuracy: true + }); + } else { + alert('your location is not available'); + } + //geoPositionSimulator.init(); }); - infoWindows[mapdomid] = infoWindow; - - // trigger an event now that the map has been initialised - // Use this for example to add listeners to the map from another JS file - mapnode.trigger( "mapInitialised", [ map ] ); - }); -} + } + function success_callback(p) { + console.log(p.coords); + // p.latitude : latitude value + // p.longitude : longitude value + // + var url = window.location; + url = url + 'find?lat=' + p.coords.latitude + '&lng=' + p.coords.longitude; + window.location = url; + } -function nearestPOIs() { + function error_callback(p) { + alert('error)'); + } - // normally will only be one - var nears = $('div[data-nearest-poi]'); + window.loadGoogleMapsScript = function() { + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = 'https://maps.googleapis.com/maps/api/js?' + + '&sensor=false&callback=loadedGoogleMapsAPI&hl=en'; + document.body.appendChild(script); + }; - nears.each(function(index) { - var nearnode = $(this); - var layerID = nearnode.attr('data-layer-id'); - if(geoPosition.init()){ // Geolocation Initialisation - geoPosition.getCurrentPosition(success_callback,error_callback,{enableHighAccuracy:true}); - }else{ - alert('your location is not available'); - } - //geoPositionSimulator.init(); - }); -} + window.addEventListener ? + window.addEventListener("load", loadGoogleMapsScript, false) : + window.attachEvent && window.attachEvent("onload", loadGoogleMapsScript); +})(jQuery); -function success_callback(p){ - console.log(p.coords); - // p.latitude : latitude value - // p.longitude : longitude value - // - var url = window.location; - url = url + 'find?lat='+p.coords.latitude+'&lng='+p.coords.longitude; - window.location = url; +function loadedGoogleMapsAPI() { + primeMap(); } - - - function error_callback(p){ - alert('error)'); - }; From 1e4e778a0a8f359178cacaba14c37a0be9e38143 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 19 Aug 2015 12:43:29 +0700 Subject: [PATCH 304/354] FIX: Include jQuery from framework to get map showing with simple theme --- templates/Includes/GoogleJavaScript.ss | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/Includes/GoogleJavaScript.ss b/templates/Includes/GoogleJavaScript.ss index dfa7f52..0e47f96 100644 --- a/templates/Includes/GoogleJavaScript.ss +++ b/templates/Includes/GoogleJavaScript.ss @@ -1,3 +1,4 @@ +<% require javascript("framework/thirdparty/jquery/jquery.js") %> <% if $UseCompressedAssets %> <% require javascript("mappable/javascript/google/mappablegoogle.min.js") %> <% else %> From 96385f3143c26c1737614580810523669982c846 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 2 Nov 2015 22:15:26 +0700 Subject: [PATCH 305/354] FIX: Deleting files that should be in MappablePointsOfInterest module --- code/POIMapPage.php | 13 ------------- templates/Layout/POIMapPage.ss | 10 ---------- 2 files changed, 23 deletions(-) delete mode 100644 code/POIMapPage.php delete mode 100644 templates/Layout/POIMapPage.ss diff --git a/code/POIMapPage.php b/code/POIMapPage.php deleted file mode 100644 index 95ab2a3..0000000 --- a/code/POIMapPage.php +++ /dev/null @@ -1,13 +0,0 @@ -removeByName('Location'); - return $fields; - } -} - -class POIMapPage_Controller extends Page_Controller { - -} diff --git a/templates/Layout/POIMapPage.ss b/templates/Layout/POIMapPage.ss deleted file mode 100644 index 33bf0b1..0000000 --- a/templates/Layout/POIMapPage.ss +++ /dev/null @@ -1,10 +0,0 @@ -<% include SideBar %> -
-
-

$Title

- $BasicMap -
$Content
-
- $Form - $PageComments -
From 013de28fcaab81107a981e685312bf06dbf1f31a Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 11 Nov 2015 21:49:52 +0700 Subject: [PATCH 306/354] FIX: Fix the comparison of true/false for useClusterer. It can now be turned off optionally as is desired. This fixes issue #29 --- javascript/google/maputil.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index c88bb0e..e58bb64 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -31,7 +31,7 @@ var primeMap; marker.setIcon(image); } - if (!useClusterer) { + if (!(useClusterer == '1')) { marker.setMap(map); } @@ -311,7 +311,7 @@ var primeMap; enableAutomaticCenterZoom, defaultHideMarker); var allowfullscreen = parseInt(mapnode.attr('data-allowfullscreen')); - if (enableAutomaticCenterZoom == '1') { + if (enableAutomaticCenterZoom == 1) { centre = $.parseJSON(mapnode.attr('data-centre')); map.setCenter(new google.maps.LatLng(centre.lat, centre.lng)); @@ -334,7 +334,7 @@ var primeMap; var googlemaptype = convertMapType(mapnode.attr('data-maptype')); map.setMapTypeId(googlemaptype); - if (useClusterer) { + if (useClusterer == 1) { // ensure zoom and grid size are integers by prefixing with unary plus var clustererGridSize = parseInt(mapnode.attr('data-clusterergridsize')); var clustererMaxZoom = parseInt(mapnode.attr('data-clusterermaxzoom')); From 46b0efadaf7211c586248cfed2d689564bc8fed3 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:03:01 +0700 Subject: [PATCH 307/354] ENHANCEMENT: CI Tool for code analysis --- .scrutinizer.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .scrutinizer.yml diff --git a/.scrutinizer.yml b/.scrutinizer.yml new file mode 100644 index 0000000..785d80c --- /dev/null +++ b/.scrutinizer.yml @@ -0,0 +1,22 @@ +inherit: true + +#Copied from https://www.adayinthelifeof.nl/2013/11/20/external-code-coverage-with-travis-scrutinizer/ +tools: + external_code_coverage: + timeout: 600 + php_mess_detector: true + php_code_sniffer: true + sensiolabs_security_checker: true + php_pdepend: true + php_loc: + enabled: true + excluded_dirs: [vendor, tests] + php_cpd: + enabled: true + excluded_dirs: [vendor, tests] + +checks: + php: true + +filter: + paths: [templates/*, tests/*,code/*] From 10a9653d06dc183f4c2610131e5011aff909cac5 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:03:18 +0700 Subject: [PATCH 308/354] ENHANCEMENT: Run tests using travis --- .travis.yml | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..b71a3a3 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,46 @@ +language: php + +sudo: false + +addons: + apt: + packages: + - tidy + +env: + global: + - DB=MYSQL CORE_RELEASE=3.1 + - MODULE_PATH="mappable" + +matrix: + allow_failures: + - php: hhvm-nightly + include: + - php: 5.6 + env: DB=MYSQL + - php: 5.5 + env: DB=MYSQL + - php: 5.4 + env: DB=MYSQL + - php: 5.3 + env: DB=MYSQL + - php: hhvm + env: DB=MYSQL + before_install: + + +before_script: + - phpenv rehash + - composer self-update || true + - git clone git://github.com/silverstripe-labs/silverstripe-travis-support.git ~/travis-support + - php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss + - cd ~/builds/ss + +script: + - vendor/bin/phpunit --coverage-clover=coverage.clover -c $MODULE_PATH/phpunit.xml $MODULE_PATH/tests/ + +after_script: + - mv coverage.clover ~/build/$TRAVIS_REPO_SLUG/ + - cd ~/build/$TRAVIS_REPO_SLUG + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover coverage.clover From 5a8b6e3ec7a4e6f0d12ebb46d75636ec94b5be72 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:03:58 +0700 Subject: [PATCH 309/354] ENHANCEMENT: FIlter coverage report to just this module --- phpunit.xml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 phpunit.xml diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..a46bdd0 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,23 @@ + + + + ./tests + + + + + + + + + sanitychecks + + + + + + ../mappable + + + + From 323caffeac20c61466d86743929072061d24e9ba Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:04:19 +0700 Subject: [PATCH 310/354] ENHANCEMENT: Test suite --- tests/GoogleMapShortCodeTest.php | 92 +++ tests/GoogleStreetViewShortCodeTest.php | 59 ++ tests/LatLongFieldTest.php | 127 ++++ tests/MapAPITest.php | 567 ++++++++++++++++++ tests/MapExtensionTest.php | 295 +++++++++ tests/MapFieldTest.php | 23 + tests/MapLayerExtensionTest.php | 26 + tests/MapLayerTest.php | 17 + tests/MapMarkerSetsExtensionTest.php | 25 + tests/MapUtilTest.php | 157 +++++ tests/MappableDataObjectSetTest.php | 78 +++ tests/MappableDataTest.php | 202 +++++++ tests/images/mapicontest.png | Bin 0 -> 1329 bytes tests/kml/example.kml | 41 ++ tests/mapextensions.yml | 4 + tests/models/TestPageMapExtension.php | 5 + tests/shortcodes.yml | 34 ++ .../Includes/Member_MapInfoWindow.ss | 1 + 18 files changed, 1753 insertions(+) create mode 100644 tests/GoogleMapShortCodeTest.php create mode 100644 tests/GoogleStreetViewShortCodeTest.php create mode 100644 tests/LatLongFieldTest.php create mode 100644 tests/MapAPITest.php create mode 100644 tests/MapExtensionTest.php create mode 100644 tests/MapFieldTest.php create mode 100644 tests/MapLayerExtensionTest.php create mode 100644 tests/MapLayerTest.php create mode 100644 tests/MapMarkerSetsExtensionTest.php create mode 100644 tests/MapUtilTest.php create mode 100644 tests/MappableDataObjectSetTest.php create mode 100644 tests/MappableDataTest.php create mode 100644 tests/images/mapicontest.png create mode 100644 tests/kml/example.kml create mode 100644 tests/mapextensions.yml create mode 100644 tests/models/TestPageMapExtension.php create mode 100644 tests/shortcodes.yml create mode 100644 tests/templates/Includes/Member_MapInfoWindow.ss diff --git a/tests/GoogleMapShortCodeTest.php b/tests/GoogleMapShortCodeTest.php new file mode 100644 index 0000000..ff853ca --- /dev/null +++ b/tests/GoogleMapShortCodeTest.php @@ -0,0 +1,92 @@ +objFromFixture('Page', 'RoadMap'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $expected = << +
+

Roads in Central Bangkok

+
+ +TEXT; + $this->assertEquals($expected, $html); + } + + + public function testAerialMap() { + GoogleMapShortCodeHandler::resetCounter(); + $page = $this->objFromFixture('Page', 'AerialMap'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $expected = << +
+

Bang Sue Train Depot, Thailand

+ + +TEXT; + $this->assertEquals($expected, $html); + } + + + public function testHybridMap() { + GoogleMapShortCodeHandler::resetCounter(); + $page = $this->objFromFixture('Page', 'HybridMap'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $expected = << +
+

Junction in Bangkok, Thailand

+ + +TEXT; + $this->assertEquals($expected, $html); + } + + + public function testTerrainmap() { + GoogleMapShortCodeHandler::resetCounter(); + $page = $this->objFromFixture('Page', 'TerrainMap'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $expected = << +
+

Mountains west of Chiang Mai

+ + +TEXT; + $this->assertEquals($expected, $html); + } + + + public function testNoLongitude() { + $page = $this->objFromFixture('Page', 'MapWithNoLongitude'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $this->assertEquals('Some text', $html); + } + + + public function testNoLatitude() { + $page = $this->objFromFixture('Page', 'MapWithNoLatitude'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $this->assertEquals('Some text', $html); + } + + +} diff --git a/tests/GoogleStreetViewShortCodeTest.php b/tests/GoogleStreetViewShortCodeTest.php new file mode 100644 index 0000000..d116acd --- /dev/null +++ b/tests/GoogleStreetViewShortCodeTest.php @@ -0,0 +1,59 @@ +objFromFixture('Page', 'StreetView'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $expected = << +
+

Canal south from Pracha Rat 1 Soi 28

+ + +TEXT; + $this->assertEquals($expected, $html); + } + + public function testNoLongitude() { + GoogleStreetViewShortCodeHandler::resetCounter(); + $page = $this->objFromFixture('Page', 'StreetViewNoLongitude'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $this->assertEquals('Some text', $html); + } + + public function testNoLatitude() { + GoogleStreetViewShortCodeHandler::resetCounter(); + $page = $this->objFromFixture('Page', 'StreetViewNoLatitude'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $this->assertEquals('Some text', $html); + } + + public function testNoHeading() { + GoogleStreetViewShortCodeHandler::resetCounter(); + $page = $this->objFromFixture('Page', 'StreetViewNoHeading'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $this->assertEquals('Some text', $html); + } + + public function testZoom() { + GoogleStreetViewShortCodeHandler::resetCounter(); + $page = $this->objFromFixture('Page', 'StreetViewWithZoom'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $expected = <<< TEXT +Some text + +
+
+

Canal south from Pracha Rat 1 Soi 28

+
+ +TEXT; + $this->assertEquals($expected, $html); + } +} diff --git a/tests/LatLongFieldTest.php b/tests/LatLongFieldTest.php new file mode 100644 index 0000000..0101f2b --- /dev/null +++ b/tests/LatLongFieldTest.php @@ -0,0 +1,127 @@ +fail('Creation of lat long field should have failed'); + } catch (Exception $e) { + $expected = 'LatLongField argument 1 must be an array containing at' + . ' least two FormField objects for Lat/Long values, resp' + . 'ectively.'; + $this->assertEquals($expected, $e->getMessage()); + } + } + + + public function testConstructTwoFieldsValid() { + $mapField = new LatLongField( + array( + new TextField('Lat', 'Latitude'), + new TextField('Lon', 'Longitude') + ) + ); + + $html = $mapField->FieldHolder(); + $this->assertContains( + '', + $html + ); + $this->assertContains( + '', + $html + ); + $this->assertContains( + '', + $html + ); + $this->assertContains( + '', + $html + ); + + } + + + public function testConstructThreeFieldsValid() { + $mapField = new LatLongField( + array( + new TextField('Lat', 'Latitude'), + new TextField('Lon', 'Longitude'), + new TextField('ZoomLevel', 'Zoom') + ) + ); + + $html = $mapField->FieldHolder(); + $this->assertContains( + '', + $html + ); + $this->assertContains( + '', + $html + ); + $this->assertContains( + '', + $html + ); + $this->assertContains( + '', + $html + ); + $this->assertContains( + '', + $html + ); + $this->assertContains( + '', + $html + ); + } + + + public function testGeocode() { + $this->markTestSkipped('TODO'); + } + + + public function testSetGuidePoints() { + $mapField = new LatLongField( + array( + new TextField('Lat', 'Latitude'), + new TextField('Lon', 'Longitude'), + new TextField('ZoomLevel', 'Zoom') + ) + ); + $guidePoints = array( + array('latitude' => 42, 'longitude' => '113.1'), + array('latitude' => 14.9, 'longitude' => '113.2'), + array('latitude' => 42.3, 'longitude' => '113.4'), + ); + $mapField->setGuidePoints($guidePoints); + + $html = $mapField->FieldHolder(); + $expected = 'data-GuidePoints="[{"latitude":42,"longitude":"113.1&' + . 'quot;},{"latitude":14.9,"longitude":"113.2"},{&q' + . 'uot;latitude":42.3,"longitude":"113.4"}]"'; + + $this->assertContains($expected, $html); + } + +} diff --git a/tests/MapAPITest.php b/tests/MapAPITest.php new file mode 100644 index 0000000..1c80fae --- /dev/null +++ b/tests/MapAPITest.php @@ -0,0 +1,567 @@ +requiredExtensions = array( + 'Member' => array('MapExtension') + ); + parent::setupOnce(); + } + + public function setUp() { + MapUtil::reset(); + parent::setUp(); + } + + public function testSetClusterer() { + $map = $this->getMap(); + $map->setClusterer(true); + $html = $map->forTemplate(); + $this->assertContains('data-clusterergridsize=50', $html); + $this->assertContains('data-clusterermaxzoom=17', $html); + $this->assertContains('data-useclusterer=1', $html); + + $map = $this->getMap(); + $map->setClusterer(true, 60, 14); + $html = $map->forTemplate(); + $this->assertContains('data-clusterergridsize=60', $html); + $this->assertContains('data-clusterermaxzoom=14', $html); + $this->assertContains('data-useclusterer=1', $html); + + $map = $this->getMap(); + $map->setClusterer(false); + $html = $map->forTemplate(); + $this->assertContains('data-useclusterer=false', $html); + $this->assertContains('data-clusterergridsize=50', $html); + $this->assertContains('data-clusterermaxzoom=17', $html); + } + + /* + Toggle as to whether or not to include a style= attribute with width/height + */ + public function testSetShowInlineMapDivStyle() { + $map = $this->getMap(); + $map->setShowInlineMapDivStyle(true); + $html = $map->forTemplate(); + $expected = 'style="width:100%; height: 400px;"'; + $this->assertContains($expected, $html); + + $map->setShowInlineMapDivStyle(false); + $html = $map->forTemplate(); + $this->assertNotContains($expected, $html); + } + + public function testSetAdditionalCSSClasses() { + $map = $this->getMap(); + $map->setAdditionalCSSClasses('bigMap shadowMap'); + $html = $map->forTemplate(); + $expected = 'class="bigMap shadowMap mappable"'; + $this->assertContains($expected, $html); + $map->setAdditionalCSSClasses('bigMap shadowMap'); + } + + + public function testSetMapStyle() { + $style = << + n/a +#trailsstyleWat bot loop: Segment (1) +1 + +100.502187,13.893589,12.489117 +100.502306,13.893550,11.420303 +100.502403,13.893468,8.498596 +100.502467,13.893355,11.156882 +100.502521,13.893244,12.405776 +100.502569,13.893151,11.533149 +100.502618,13.893058,12.998710 +100.502692,13.892954,10.693356 +100.502768,13.892871,13.830301 +100.502873,13.892789,19.022263 +100.502994,13.892735,12.802382 +100.503103,13.892699,12.251826 +100.503229,13.892690,13.695211 +100.503350,13.892695,11.813846 +100.503473,13.892706,13.870461 +100.503598,13.892708,10.766499 +100.503702,13.892706,13.136336 +100.503827,13.892696,11.557716 +100.503930,13.892685,11.176296 +100.504035,13.892648,8.320370 +100.504122,13.892604,14.492889 +100.504221,13.892550,4.760454 +100.504342,13.892488,5.680111 +100.504445,13.892393,9.436865 +100.504510,13.892323,10.983356 + + + + + diff --git a/tests/mapextensions.yml b/tests/mapextensions.yml new file mode 100644 index 0000000..93d6f1f --- /dev/null +++ b/tests/mapextensions.yml @@ -0,0 +1,4 @@ +Member: + WithLayers: + Title: Member With Map and Layers + Content: Some content diff --git a/tests/models/TestPageMapExtension.php b/tests/models/TestPageMapExtension.php new file mode 100644 index 0000000..ab9a8ac --- /dev/null +++ b/tests/models/TestPageMapExtension.php @@ -0,0 +1,5 @@ + 'Boolean'); +} diff --git a/tests/shortcodes.yml b/tests/shortcodes.yml new file mode 100644 index 0000000..11bc665 --- /dev/null +++ b/tests/shortcodes.yml @@ -0,0 +1,34 @@ +Page: + RoadMap: + Title: Road Map + Content: Some text[GoogleMap latitude='13.7402946' longitude='100.5525439' caption="Roads in Central Bangkok" zoom="14" maptype="road"] + AerialMap: + Title: Aerial Map + Content: Some text[GoogleMap latitude='13.815483' longitude='100.5447213' caption="Bang Sue Train Depot, Thailand" zoom="20" maptype="aerial"] + HybridMap: + Title: Hybrid Map + Content: Some text[GoogleMap latitude='13.8309545' longitude='100.5577219' caption="Junction in Bangkok, Thailand" zoom="18" maptype="hybrid"] + TerrainMap: + Title: Terrain Map + Content: Some text[GoogleMap latitude='18.8032393' longitude='98.9166518' caption="Mountains west of Chiang Mai" zoom="14" maptype="terrain"] + MapWithNoLatitude: + Title: Map With No Latitude + Content: Some text[GoogleMap longitude='98.9166518' caption="Mountains west of Chiang Mai" zoom="14" maptype="terrain"] + MapWithNoLongitude: + Title: Map With No Longitude + Content: Some text[GoogleMap latitude='18.8032393' caption="Mountains west of Chiang Mai" zoom="14" maptype="terrain"] + StreetView: + Title: Valid StreetView + Content: Some text[GoogleStreetView latitude="13.811841" longitude="100.527309" heading="162.43" pitch="-10" caption="Canal south from Pracha Rat 1 Soi 28"] + StreetViewWithZoom: + Title: Valid StreetView With Zoom + Content: Some text[GoogleStreetView latitude="13.811841" longitude="100.527309" zoom="12" heading="162.43" pitch="-10" caption="Canal south from Pracha Rat 1 Soi 28"] + StreetViewNoHeading: + Title: StreetView with no Heading + Content: Some text[GoogleStreetView longitude="100.527309" latitude="13.811841" pitch="-10" caption="Canal south from Pracha Rat 1 Soi 28"] + StreetViewNoLatitude: + Title: StreetView with no Latitude + Content: Some text[GoogleStreetView longitude="100.527309" heading="162.43" pitch="-10" caption="Canal south from Pracha Rat 1 Soi 28"] + StreetViewNoLongitude: + Title: StreetView with no Longitude + Content: Some text[GoogleStreetView latitude="13.811841" heading="162.43" pitch="-10" caption="Canal south from Pracha Rat 1 Soi 28"] diff --git a/tests/templates/Includes/Member_MapInfoWindow.ss b/tests/templates/Includes/Member_MapInfoWindow.ss new file mode 100644 index 0000000..e4914d1 --- /dev/null +++ b/tests/templates/Includes/Member_MapInfoWindow.ss @@ -0,0 +1 @@ +MEMBER: $Name{$TestKey} From dd054f4bc9daa0c444fb195e08a74390ae9bc340 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:05:14 +0700 Subject: [PATCH 311/354] FIX: Remove obsolete method that was for DataObjectManager --- code/MapLayer.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/code/MapLayer.php b/code/MapLayer.php index bcdcc9a..0d59261 100644 --- a/code/MapLayer.php +++ b/code/MapLayer.php @@ -11,13 +11,4 @@ class MapLayer extends DataObject { 'KmlFile' => 'File' ); - function getCMSFields_forPopup() { - $fields = new FieldSet(); - - $fields->push(new TextField('Title')); - $fields->push(new FileIFrameField('KmlFile')); - - return $fields; - } - } From 6bd0586d48561d351f67d7c24b7d84573bcd3ed6 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:05:35 +0700 Subject: [PATCH 312/354] MINOR: Horizontal white, debug removal --- code/MapField.php | 49 +++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/code/MapField.php b/code/MapField.php index 797b569..ef24f40 100755 --- a/code/MapField.php +++ b/code/MapField.php @@ -10,40 +10,35 @@ class MapField extends DatalessField { protected $headingLevel = 2; private $divId; + /** + * @param string $name + * @param string $title + */ function __construct($name, $title = null, $headingLevel = 2, $allowHTML = false, $form = null) { $this->divId = $name; - // legacy handling for old parameters: $title, $heading, ... - // instead of new handling: $name, $title, $heading, ... - $args = func_get_args(); - if(!isset($args[1]) || is_numeric($args[1])) { - $title = (isset($args[0])) ? $args[0] : null; - // Use "HeaderField(title)" as the default field name for a HeaderField; - // if it's just set to title then we risk causing accidental duplicate-field creation. - - // this means i18nized fields won't be easily accessible through fieldByName() - $name = 'MapField' . $title; - $headingLevel = (isset($args[1])) ? $args[1] : null; - $allowHTML = (isset($args[2])) ? $args[2] : null; - $form = (isset($args[3])) ? $args[3] : null; - } - - if($headingLevel) $this->headingLevel = $headingLevel; - $this->allowHTML = $allowHTML; - parent::__construct($name, $title, null, $allowHTML, $form); + // legacy handling for old parameters: $title, $heading, ... + // instead of new handling: $name, $title, $heading, ... + $args = func_get_args(); + if(!isset($args[1]) || is_numeric($args[1])) { + $title = (isset($args[0])) ? $args[0] : null; + // Use "HeaderField(title)" as the default field name for a HeaderField; + // if it's just set to title then we risk causing accidental duplicate-field creation. + + // this means i18nized fields won't be easily accessible through fieldByName() + $name = 'MapField' . $title; + $headingLevel = (isset($args[1])) ? $args[1] : null; + $allowHTML = (isset($args[2])) ? $args[2] : null; + $form = (isset($args[3])) ? $args[3] : null; + } + + if($headingLevel) $this->headingLevel = $headingLevel; + $this->allowHTML = $allowHTML; + parent::__construct($name, $title, null, $allowHTML, $form); } function Field($properties = array()) { Requirements::javascript('framework/thirdparty/jquery/jquery.js'); Requirements::javascript('framework/thirdparty/jquery-livequery/jquery.livequery.js'); - // Requirements::javascript('http://maps.google.com/maps/api/js?sensor=false'); - - - // Requirements::javascript('mappable/javascript/mapField.js'); - - - - - $attributes = array( 'class' => 'middleColumn', 'id' => $this->divId, From ca0524cd9a61448507e417bcb7af16364c12933c Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:06:13 +0700 Subject: [PATCH 313/354] FIX: Remove geocoding function as not used. Tidied code using Scrutinizer --- code/LatLongField.php | 124 +++++++++++++++--------------------------- 1 file changed, 45 insertions(+), 79 deletions(-) diff --git a/code/LatLongField.php b/code/LatLongField.php index aff90f6..bda31f9 100755 --- a/code/LatLongField.php +++ b/code/LatLongField.php @@ -2,12 +2,6 @@ class LatLongField extends FieldGroup { - static $allowed_actions = array ( - 'geocode' - ); - - protected $addressFields = array(); - protected $latField; protected $longField; @@ -16,25 +10,23 @@ class LatLongField extends FieldGroup { protected $buttonText; - private static $ctr = 0; + private $guidePoints = null; - public function __construct($children = array(), $addressFields = array(), $buttonText = null) { - $id = spl_object_hash($this); + private static $ctr = 0; + /** + * @param string[] $buttonText + */ + public function __construct($children = array(), $buttonText = null) { self::$ctr++; - if (self::$ctr == 2) { - //asdfsda; - } - if ((sizeof($children) < 2) || + if ((sizeof($children) < 2) || (sizeof($children) > 3) || (!$children[0] instanceof FormField) || (!$children[1] instanceof FormField) - ) { - user_error('LatLongField argument 1 must be an array containing at least two FormField '. + ) user_error('LatLongField argument 1 must be an array containing at least two FormField '. 'objects for Lat/Long values, respectively.', E_USER_ERROR); - } + parent::__construct($children); - $this->addressFields = $addressFields; $this->buttonText = $buttonText ? $buttonText : _t('LatLongField.LOOKUP', 'Search'); $this->latField = $children[0]->getName(); @@ -57,94 +49,68 @@ public function __construct($children = array(), $addressFields = array(), $butt } - public function hasData() { - return true; - } - - public function FieldHolder($properties = array()) { Requirements::javascript(THIRDPARTY_DIR.'/jquery/jquery.js'); Requirements::javascript(THIRDPARTY_DIR.'/jquery-livequery/jquery.livequery.js'); Requirements::javascript(THIRDPARTY_DIR.'/jquery-metadata/jquery.metadata.js'); - //Requirements::javascript(MAPPABLE_MODULE_PATH.'/javascript/mapField.js'); - - $js = ' - - '; - Requirements::javascript(MAPPABLE_MODULE_PATH.'/javascript/mapField.js'); + $attributes = array( - 'class' => 'editableMap', - 'id' => 'GoogleMap', - 'data-LatFieldName' => $this->latField, + 'class' => 'editableMap', + 'id' => 'GoogleMap', + 'data-LatFieldName' => $this->latField, 'data-LonFieldName' => $this->longField, 'data-ZoomFieldName' => $this->zoomField, 'data-UseMapBounds' => false - ); + ); - Requirements::css('mappable/css/mapField.css'); - $guidePointsJSON = ''; - if (isset($this->guidePoints)) { - $latlongps = array(); + Requirements::css('mappable/css/mapField.css'); + + // check for and if required add guide points + if (!empty($this->guidePoints)) { + $latlongps = array(); foreach ($this->guidePoints as $guidepoint) { array_push($latlongps, $guidepoint); } - $guidePointsJSON = json_encode($latlongps); - // convert the mappable guidepoints to lat lon - - $attributes['data-GuidePoints'] = $guidePointsJSON; + $guidePointsJSON = json_encode($latlongps); + // convert the mappable guidepoints to lat lon - // we only wish to change the bounds to those of all the points iff - // the item currently has no location - $attributes['data-useMapBounds'] = true; - } - $content = '
' . $this->createTag( - "div", - $attributes - ) . '
'; + $attributes['data-GuidePoints'] = $guidePointsJSON; - $this->FieldList()->push(new LiteralField('locationEditor', $content)); - - $content2 = '
- - -
-
-
- '; + // we only wish to change the bounds to those of all the points iff + // the item currently has no location + $attributes['data-useMapBounds'] = true; + } + $content = '
'.$this->create_tag( + "div", + $attributes + ).'
'; + + $this->FieldList()->push(new LiteralField('locationEditor', $content)); + + $content2 = << + + +
+
+ +HTML; $this->FieldList()->push(new LiteralField('mapSearch', $content2)); return parent::FieldHolder(); } - - /* - Perform place name search as a means of navigation when editing locations - */ - public function geocode(SS_HTTPRequest $r) { - if ($address = $r->requestVar('address')) { - if ($json = @file_get_contents( - "http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=". - urlencode($address))) { - $response = Convert::json2array($json); - $location = $response['results'][0]->geometry->location; - return new SS_HTTPResponse($location->lat.",".$location->lng); - } - } - } - - /* Set guidance points for the map being edited. For example in a photographic set show the map position of some other images so that subsequent photo edits do not start with a map centred - on the horizon + at the origin + + @var newGuidePoints array of points expressed as associative arrays containing keys latitude + and longitude mapping to geographical locations */ public function setGuidePoints($newGuidePoints) { $this->guidePoints = $newGuidePoints; From 6b100609db7af6291bda3675e7d2ded67dbc304d Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:06:41 +0700 Subject: [PATCH 314/354] FIXES: Fixes post tests and removal of obsolete methods --- code/MapAPI.php | 382 ++++++++++++++---------------------------------- 1 file changed, 108 insertions(+), 274 deletions(-) diff --git a/code/MapAPI.php b/code/MapAPI.php index cb717f4..abb7fd2 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -23,9 +23,6 @@ class MapAPI extends ViewableData /** GoogleMap ID for the HTML DIV **/ protected $googleMapId = 'googlemapapi'; - /** GoogleMap Direction ID for the HTML DIV **/ - protected $googleMapDirectionId = 'route'; - /* Additional CSS classes to use when rendering the map */ protected $set_additional_css_classes = ''; @@ -35,12 +32,6 @@ class MapAPI extends ViewableData /** Height of the gmap **/ protected $height = 600; - /** Icon width of the gmarker **/ - protected $iconWidth = 20; - - /** Icon height of the gmarker **/ - protected $iconHeight = 34; - /* array of lines to be drawn on the map */ protected $lines = array(); @@ -75,57 +66,8 @@ class MapAPI extends ViewableData protected $latLongCenter = null; - - - - /* - Map styles for google maps, ignored if null -
-var styles = [
-			 {
-							 featureType: 'water',
-							 elementType: 'all',
-							 stylers: [
-											 { hue: '#B6C5CF' },
-											 { saturation: -54 },
-											 { lightness: 1 },
-											 { visibility: 'on' }
-							 ]
-			 },{
-							 featureType: 'landscape',
-							 elementType: 'all',
-							 stylers: [
-											 { hue: '#D9D4C8' },
-											 { saturation: -32 },
-											 { lightness: -8 },
-											 { visibility: 'on' }
-							 ]
-			 },{
-							 featureType: 'road',
-							 elementType: 'all',
-							 stylers: [
-											 { hue: '#A69D97' },
-											 { saturation: -92 },
-											 { lightness: -3 },
-											 { visibility: 'on' }
-							 ]
-			 },{
-							 featureType: 'poi',
-							 elementType: 'all',
-							 stylers: [
-											 { hue: '#E7E6DB' },
-											 { saturation: -53 },
-											 { lightness: 47 },
-											 { visibility: 'on' }
-							 ]
-			 }
-];
-		
- */ protected $jsonMapStyles = '[]'; - protected $delayLoadMapFunction = false; - /** * Type of the gmap, can be: * 'road' (roadmap), @@ -143,14 +85,11 @@ class MapAPI extends ViewableData protected $mapService = 'google'; - /** Add the direction button to the infowindow **/ - protected $displayDirectionFields = false; - /** Hide the marker by default **/ protected $defaultHideMarker = false; /** Extra content (marker, etc...) **/ - //protected $contentMarker = ''; + protected $contentMarker = ''; // a list of markers, markers being associative arrays protected $markers = array(); @@ -191,41 +130,16 @@ class MapAPI extends ViewableData /* set this to true to render button to maximize / minimize a map */ protected $allowFullScreen = null; - - protected static $include_download_javascript = false; - - /** * Class constructor * * @param string $googleMapKey the googleMapKey - * - * @return void */ - public function __construct($googleMapKey='') { + public function __construct($googleMapKey = '') { $this->googleMapKey = $googleMapKey; } - /** - * Set the key of the gmap - * - * @param string $googleMapKey the googleMapKey - * - * @return void - */ - - public function setKey($googleMapKey) { - $this->googleMapKey = $googleMapKey; - return $this; - } - - public function setIncludeDownloadJavascript($inclusion) { - self::$include_download_javascript = $inclusion; - return $this; - } - - public function setShowInlineMapDivStyle($new_show_inline_map_div_style) { $this->show_inline_map_div_style = $new_show_inline_map_div_style; return $this; @@ -242,28 +156,18 @@ public function setMapStyle($newStyles) { return $this; } - - - public function setDelayLoadMapFunction($newDelay) { - $this->delayLoadMapFunction = $newDelay; - return $this; - } - /** * Set the useClusterer parameter (optimization to display a lot of marker) * - * @param boolean $useClusterer use cluster or not - * @param string $clusterIcon the cluster icon - * @param int $maxVisibleMarkers max visible markers - * @param int $gridSize grid size - * @param int $minMarkersPerClusterer minMarkersPerClusterer - * @param int $maxLinesPerInfoBox maxLinesPerInfoBox + * @param boolean $useClusterer use cluster or not + * @param int $gridSize grid size + * @param int $maxZoom max zoom to cluster at * - * @return void + * * @return MapAPI This same object, in order to enable chaining of methods */ - public function setClusterer($useClusterer, $gridSize=50, $maxZoom=17, - $clustererLibraryPath='/mappable/javascript/google/markerclusterer.js') { + public function setClusterer($useClusterer, $gridSize = 50, $maxZoom = 17, + $clustererLibraryPath = '/mappable/javascript/google/markerclusterer.js') { $this->useClusterer = $useClusterer; $this->gridSize = $gridSize; $this->maxZoom = $maxZoom; @@ -276,7 +180,7 @@ public function setClusterer($useClusterer, $gridSize=50, $maxZoom=17, * * @param string $googleMapId the google div ID * - * @return void + * @return MapAPI This same object, in order to enable chaining of methods */ public function setDivId($googleMapId) { @@ -284,19 +188,6 @@ public function setDivId($googleMapId) { return $this; } - /** - * Set the ID of the default gmap direction DIV - * - * @param string $googleMapDirectionId GoogleMap Direction ID for the HTML DIV - * - * @return void - */ - - public function setDirectionDivId($googleMapDirectionId) { - $this->googleMapDirectionId = $googleMapDirectionId; - return $this; - } - /** * Set the size of the gmap. If these values are not provided * then CSS is used instead @@ -304,7 +195,7 @@ public function setDirectionDivId($googleMapDirectionId) { * @param int $width GoogleMap width * @param int $height GoogleMap height * - * @return void + * @return MapAPI This same object, in order to enable chaining of methods */ public function setSize($width, $height) { @@ -313,28 +204,12 @@ public function setSize($width, $height) { return $this; } - - /** - * Set the size of the icon markers - * - * @param int $iconWidth GoogleMap marker icon width - * @param int $iconHeight GoogleMap marker icon height - * - * @return void - */ - - public function setIconSize($iconWidth, $iconHeight) { - $this->iconWidth = $iconWidth; - $this->iconHeight = $iconHeight; - return $this; - } - /** * Set the lang of the gmap * * @param string $lang GoogleMap lang : fr,en,.. * - * @return void + * @return MapAPI This same object, in order to enable chaining of methods */ public function setLang($lang) { @@ -345,9 +220,9 @@ public function setLang($lang) { /** * Set the zoom of the gmap * - * @param int $zoom GoogleMap zoom. + * @param int $zoom GoogleMap zoom. * - * @return void + * @return MapAPI This same object, in order to enable chaining of methods */ public function setZoom($zoom) { @@ -358,9 +233,9 @@ public function setZoom($zoom) { /** * Set the zoom of the infowindow * - * @param int $zoom GoogleMap zoom. + * @param int $infoWindowZoom GoogleMap information window zoom. * - * @return void + * @return MapAPI This same object, in order to enable chaining of methods */ public function setInfoWindowZoom($infoWindowZoom) { @@ -371,9 +246,9 @@ public function setInfoWindowZoom($infoWindowZoom) { /** * Enable the zoom on the marker when you click on it * - * @param int $zoom GoogleMap zoom. + * @param boolean $enableWindowZoom info window enabled zoom. * - * @return void + * @return MapAPI This same object, in order to enable chaining of methods */ public function setEnableWindowZoom($enableWindowZoom) { @@ -384,9 +259,9 @@ public function setEnableWindowZoom($enableWindowZoom) { /** * Enable theautomatic center/zoom at the gmap load * - * @param int $zoom GoogleMap zoom. + * @param boolean $enableAutomaticCenterZoom enable automatic centre zoom * - * @return void + * @return MapAPI This same object, in order to enable chaining of methods */ public function setEnableAutomaticCenterZoom($enableAutomaticCenterZoom) { @@ -399,7 +274,7 @@ public function setEnableAutomaticCenterZoom($enableAutomaticCenterZoom) { * * @param string $center GoogleMap center (an address) * - * @return void + * @return MapAPI This same object, in order to enable chaining of methods */ public function setCenter($center) { @@ -410,9 +285,11 @@ public function setCenter($center) { /** * Set the type of the gmap. Also takes into account legacy settings * + * FIXME - allow other valid settings in config for map type + * * @param string $mapType Can be one of road,satellite,hybrid or terrain. Defaults to road * - * @return void + * @return MapAPI This same object, in order to enable chaining of methods */ public function setMapType($mapType) { @@ -423,22 +300,22 @@ public function setMapType($mapType) { case 'google.maps.MapTypeId.SATELLITE': $this->mapType = "satellite"; break; - case 'google.maps.MapTypeId.SATELLITE': - $this->mapType = "satellite"; + case 'google.maps.MapTypeId.G_HYBRID_MAP': + $this->mapType = "hybrid"; break; - case 'google.maps.MapTypeId.SATELLITE': - $this->mapType = "satellite"; + case 'google.maps.MapTypeId.G_PHYSICAL_MAP': + $this->mapType = "terrain"; break; - default: - $this->MapType = "road"; + case 'google.maps.MapTypeId.ROADMAP': + $this->mapType = "road"; break; } - return $this; } /* Set whether or not to allow the full screen tools + @return MapAPI This same object, in order to enable chaining of methods */ public function setAllowFullScreen($allowed) { $this->allowFullScreen = $allowed; @@ -447,22 +324,23 @@ public function setAllowFullScreen($allowed) { /** * Set the center of the gmap + * + * @return MapAPI This same object, in order to enable chaining of methods **/ public function setLatLongCenter($center) { - $this->latLongCenter = $center; - return $this; - } + // error check, we want an associative array with lat,lng keys numeric - /** - * Decide whether or not to show direction controls - * - * @param boolean $displayDirectionFields display directions or not in the info window - * - * @return void - */ + if (!is_array($center)) { + throw new InvalidArgumentException('Center must be an associative array containing lat,lng'); + } - public function setDisplayDirectionFields($displayDirectionFields) { - $this->displayDirectionFields = $displayDirectionFields; + $keys = array_keys($center); + sort($keys); + if (implode(',', $keys) != 'lat,lng') { + throw new InvalidArgumentException('Keys provided must be lat, lng'); + } + + $this->latLongCenter = $center; return $this; } @@ -471,7 +349,7 @@ public function setDisplayDirectionFields($displayDirectionFields) { * * @param boolean $defaultHideMarker hide all the markers on the map by default * - * @return void + * @return MapAPI */ public function setDefaultHideMarker($defaultHideMarker) { @@ -516,34 +394,26 @@ public function getContent($url) { * * @param string $address an address * - * @return array array with precision, lat & lng + * @return string array with precision, lat & lng */ public function geocoding($address) { - $encodeAddress = urlencode($address); - $url = "//maps.google.com/maps/geo?q=".$encodeAddress."&output=csv&key=".$this->googleMapKey; - - if (function_exists('curl_init')) { - $data = $this->getContent($url); - } else { - $data = file_get_contents($url); - } + $geocoder = new MappableGoogleGeocoder(); + $locations = $geocoder->getLocations($address); + $result = null; + if (!empty($locations)) { + $place = $locations[0]; + $location = $place['geometry']['location']; + $result = array( + 'lat' => $location['lat'], + 'lon' => $location['lng'], + 'geocoded' => true + ); - $csvSplit = preg_split("/,/", $data); - $status = $csvSplit[0]; - - if (strcmp($status, "200") == 0) { - /* - For a successful geocode: - - $precision = $csvSplit[1 - - $lat = $csvSplit[2] - - $lng = $csvSplit[3]; - */ - $return = $csvSplit; } else { - $return = null; // failure to geocode + $result = array(); // no results } - return $return; + return $result; } /** @@ -555,10 +425,10 @@ public function geocoding($address) { * @param string $category marker category * @param string $icon an icon url * - * @return void + * @return MapAPI */ - public function addMarkerByCoords($lat, $lng, $html='', $category='', $icon='') { + public function addMarkerByCoords($lat, $lng, $html = '', $category = '', $icon = '') { $m = array( 'latitude' => $lat, 'longitude' => $lng, @@ -579,15 +449,13 @@ public function addMarkerByCoords($lat, $lng, $html='', $category='', $icon='') * @param string $category marker category * @param string $icon an icon url * - * @return void + * @return MapAPI */ - public function addMarkerByAddress($address, $content='', $category='', $icon='') { + public function addMarkerByAddress($address, $content = '', $category = '', $icon = '') { $point = $this->geocoding($address); - if ($point!==null) { - $this->addMarkerByCoords($point[2], $point[3], $content, $category, $icon); - } else { - // throw new Exception('Adress not found : '.$address); + if ($point !== null) { + $this->addMarkerByCoords($point['lat'], $point['lon'], $content, $category, $icon); } return $this; } @@ -595,14 +463,14 @@ public function addMarkerByAddress($address, $content='', $category='', $icon='' /** * Add marker by an array of coord * - * @param string $coordtab an array of lat,lng,content + * @param array $coordtab an array of lat,lng,content * @param string $category marker category * @param string $icon an icon url * - * @return void + * @return MapAPI */ - public function addArrayMarkerByCoords($coordtab, $category='', $icon='') { + public function addArrayMarkerByCoords($coordtab, $category = '', $icon = '') { foreach ($coordtab as $coord) { $this->addMarkerByCoords($coord[0], $coord[1], $coord[2], $category, $icon); } @@ -620,19 +488,21 @@ public function addArrayMarkerByCoords($coordtab, $category='', $icon='') { public function addMarkerAsObject(ViewableData $obj, $infowindowtemplateparams = null) { $extensionsImplementMappable = false; $extensions = Object::get_extensions(get_class($obj)); + if (is_array($extensions)) { - foreach ($extensions as $extension) { - $class = new ReflectionClass($extension); - if ($class->implementsInterface('Mappable')) { - $extensionsImplementMappable = true; - } + foreach ($extensions as $extension) { + $class = new ReflectionClass($extension); + if ($class->implementsInterface('Mappable')) { + $extensionsImplementMappable = true; + } + } } + if ($extensionsImplementMappable || ($obj instanceof Mappable) || (Object::has_extension($obj->ClassName, 'MapExtension')) ) { - //if(($obj->getMappableLatitude() > 0) || ($obj->getMappableLongitude() > 0)) { $cat = $obj->hasMethod('getMappableMapCategory') ? $obj->getMappableMapCategory() : "default"; if ($infowindowtemplateparams !== null) { foreach ($infowindowtemplateparams as $key => $value) { @@ -667,50 +537,19 @@ public function connectPoints(ViewableData $one, ViewableData $two, $color = "#F ); } - public function forTemplate() { $this->generate(); + MapUtil::set_map_already_rendered(true); return $this->getGoogleMap(); } - - /** - * Add marker by an array of address - * - * @param string $coordtab an array of address - * @param string $category marker category - * @param string $icon an icon url - * - * @return void - */ - - public function addArrayMarkerByAddress($coordtab, $category='', $icon='') { - foreach ($coordtab as $coord) { - $this->addMarkerByAddress($coord[0], $coord[1], $category, $icon); - } - return $this; - } - /** - * Set a direction between 2 addresss and set a text panel - * - * @param string $from an address - * @param string $to an address - * @param string $idpanel id of the div panel - * - * @return void - */ - - public function addDirection($from, $to, $idpanel='') { - $this->contentMarker .= 'addDirection("'.$from.'","'.$to.'","'.$idpanel.'");'; - } - - /** - * Parse a KML file and add markers to a category + * Add a KML file which will be rendered on this map. Normally used for likes + * of GPS traces from activities * * @param string $url url of the kml file compatible with gmap and gearth * - * @return void + * @return MapAPI */ public function addKML($url) { @@ -740,7 +579,7 @@ public function addLine($from = array(), $to = array(), $color = "#FF3300") { /* For php 5.3 */ - function jsonRemoveUnicodeSequences($struct) { + public static function jsonRemoveUnicodeSequences($struct) { return preg_replace("/\\\\u([a-f0-9]{4})/e", "iconv('UCS-4LE','UTF-8',pack('V', hexdec('U$1')))", json_encode($struct)); @@ -761,26 +600,28 @@ public function generate() { // prior to PHP version 5.4, one needs to use regex if (PHP_VERSION_ID < 50400) { - $jsonMarkers = stripslashes($this->jsonRemoveUnicodeSequences($this->markers)); - $linesJson = stripslashes($this->jsonRemoveUnicodeSequences($this->lines)); - $kmlJson = stripslashes($this->jsonRemoveUnicodeSequences($this->kmlFiles)); + $jsonMarkers = stripslashes(MapAPI::jsonRemoveUnicodeSequences($this->markers)); + $linesJson = stripslashes(MapAPI::jsonRemoveUnicodeSequences($this->lines)); + $kmlJson = stripslashes(MapAPI::jsonRemoveUnicodeSequences($this->kmlFiles)); } else { - $jsonMarkers = stripslashes(json_encode($this->markers,JSON_UNESCAPED_UNICODE)); - $linesJson = stripslashes(json_encode($this->lines,JSON_UNESCAPED_UNICODE)); - $kmlJson = stripslashes(json_encode($this->kmlFiles,JSON_UNESCAPED_UNICODE)); + $jsonMarkers = stripslashes(json_encode($this->markers, JSON_UNESCAPED_UNICODE)); + $linesJson = stripslashes(json_encode($this->lines, JSON_UNESCAPED_UNICODE)); + $kmlJson = stripslashes(json_encode($this->kmlFiles, JSON_UNESCAPED_UNICODE)); } - - - // Center of the GMap + // Center of the GMap - text centre takes precedence $geocodeCentre = ($this->latLongCenter) ? $this->latLongCenter : $this->geocoding($this->center); + $latlngCentre = null; // coordinates for centre depending on which method used - if ($geocodeCentre[0]=="200") { // success - $latlngCentre = array('lat'=>$geocodeCentre[2],'lng' => $geocodeCentre[3]); - } else { // Paris - $latlngCentre = array('lat'=>48.8792, 'lng' => 2.34778); + if (isset($geocodeCentre['geocoded'])) { + $latlngCentre = array( + 'lat' => $geocodeCentre['lat'], + 'lng' => $geocodeCentre['lon'] + ); + } else if (is_array($this->latLongCenter)) { + $latlngCentre = $this->latLongCenter; } $this->LatLngCentreJSON = stripslashes(json_encode($latlngCentre)); @@ -807,10 +648,6 @@ public function generate() { $this->defaultHideMarker = 'false'; } - if (!$this->MapTypeId) { - $this->MapTypeId = 'false'; - } - // initialise full screen as the config value if not already set if ($this->allowFullScreen === null) { $this->allowFullScreen = Config::inst()->get('Mappable', 'allow_full_screen'); @@ -820,9 +657,12 @@ public function generate() { $this->allowFullScreen = 'false'; } - + if (!$this->enableWindowZoom) { + $this->enableWindowZoom = 'false'; + } $vars = new ArrayData(array( + 'JsonMapStyles' => $this->jsonMapStyles, 'AdditionalCssClasses' => $this->additional_css_classes, 'Width' => $this->width, @@ -831,7 +671,6 @@ public function generate() { 'InfoWindowZoom' => $this->infoWindowZoom, 'EnableWindowZoom' => $this->enableWindowZoom, 'MapMarkers' => $jsonMarkers, - 'DelayLoadMapFunction' => $this->delayLoadMapFunction, 'DefaultHideMarker' => $this->defaultHideMarker, 'LatLngCentre' => $this->LatLngCentreJSON, 'EnableAutomaticCenterZoom' => $this->enableAutomaticCenterZoom, @@ -842,7 +681,6 @@ public function generate() { 'GoogleMapID' => $this->googleMapId, 'Lang'=>$this->lang, 'UseClusterer'=>$this->useClusterer, - 'DownloadJS' => !(self::$include_download_javascript), 'ClustererLibraryPath' => $this->clustererLibraryPath, 'ClustererMaxZoom' => $this->maxZoom, 'ClustererGridSize' => $this->gridSize, @@ -853,24 +691,20 @@ public function generate() { ) ); - // JavaScript required to prime the map - //$javascript = $this->processTemplateJS('Map', $vars); - //$vars->setField('JavaScript', $javascript); + if (!MapUtil::get_map_already_rendered()) { + $vars->setField('GoogleMapKey', $this->googleMapKey); + $vars->setField('GoogleMapLang', $this->lang); + } // HTML component of the map $this->content = $this->processTemplateHTML('Map', $vars); } - function processTemplateJS($templateName, $templateVariables = null) { - if (!$templateVariables) { - $templateVariables = new ArrayList(); - } - $mappingService = Config::inst()->get('Mappable', 'mapping_service'); - $result = $templateVariables->renderWith($templateName.$mappingService.'JS'); - return $result; - } - - function processTemplateHTML($templateName, $templateVariables = null ) { + /** + * @param string $templateName + * @param ArrayData $templateVariables + */ + public function processTemplateHTML($templateName, $templateVariables = null) { if (!$templateVariables) { $templateVariables = new ArrayList(); } From 507cd84856a67ad86a2cce1a89b116dbde687676 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:07:43 +0700 Subject: [PATCH 315/354] FIX: Use an extension for HasGeo and BasicMap methods, allowing extension --- code/MapExtension.php | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/code/MapExtension.php b/code/MapExtension.php index 95fbd94..7f438a9 100644 --- a/code/MapExtension.php +++ b/code/MapExtension.php @@ -25,6 +25,11 @@ class MapExtension extends DataExtension implements Mappable { 'MapPinEdited' => false ); + /* + Map editing field + */ + private $mapField = null; + /* Add a Location tab containing the map @@ -73,6 +78,7 @@ public function getMappableMapContent() { $this->owner->ClassName, Config::inst()->get('MapExtension', 'map_info_window_suffix') ); + $template = count($classTemplate) ? $classTemplate : $defaultTemplate; return MapUtil::sanitize($this->owner->renderWith($template)); } @@ -100,8 +106,8 @@ public function onBeforeWrite() { public function getMappableMapPin() { $result = false; if ($this->owner->MapPinIconID != 0) { - $mappin = $this->owner->MapPinIcon(); - $result = $mappin->getAbsoluteURL(); + $mapPin = $this->owner->MapPinIcon(); + $result = $mapPin->getAbsoluteURL(); } else { // check for a cached map pin already having been provided for the layer if ($this->owner->CachedMapPinURL) { @@ -116,18 +122,25 @@ public function getMappableMapPin() { Check for non zero coordinates, on the assumption that (0,0) will never be the desired coordinates */ public function HasGeo() { - $result = ($this->owner->Lat != 0) && ($this->owner->Lon != 0); + $isOrigin = ($this->owner->Lat == 0) && ($this->owner->Lon == 0); + $result = !$isOrigin; if ($this->owner->hasExtension('MapLayerExtension')) { if ($this->owner->MapLayers()->count() > 0) { $result = true; } } + + $this->owner->extend('updateHasGeo', $result); + /** + * FIXME - move this to PointsOfInterest module if ($this->owner->hasExtension('PointsOfInterestLayerExtension')) { if ($this->owner->PointsOfInterestLayers()->count() > 0) { $result = true; } } + */ + return $result; } @@ -153,7 +166,10 @@ public function BasicMap() { $map->setEnableAutomaticCenterZoom(true); } - // add points of interest taking into account the default icon of the layer as an override + $this->owner->extend('updateBasicMap', $map); + + /** + FIXME - move to POI module if (Object::has_extension($this->owner->ClassName, 'PointsOfInterestLayerExtension')) { foreach($this->owner->PointsOfInterestLayers() as $layer) { $layericon = $layer->DefaultIcon(); @@ -175,6 +191,8 @@ public function BasicMap() { $map->setClusterer(true); } + **/ + $map->setEnableAutomaticCenterZoom($autozoom); $map->setShowInlineMapDivStyle(true); @@ -192,8 +210,7 @@ public function getMapField() { new TextField('Lat', 'Latitude'), new TextField('Lon', 'Longitude'), new TextField('ZoomLevel', 'Zoom') - ), - array('Address') + ) ); } return $this->mapField; From 08a5dc7fec8c7c7cb7f88d1c521388fbcdf023b3 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:07:59 +0700 Subject: [PATCH 316/354] MINOR: Title of GridField corrected --- code/MapMarkerSetsExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/MapMarkerSetsExtension.php b/code/MapMarkerSetsExtension.php index 1486656..4044edb 100644 --- a/code/MapMarkerSetsExtension.php +++ b/code/MapMarkerSetsExtension.php @@ -22,7 +22,7 @@ public function updateCMSFields(FieldList $fields) { $gridConfig2->getComponentByType('GridFieldPaginator')->setItemsPerPage(100); $gridField2 = new GridField("MapMarkerSets", - "MapMarkers", + "MapMarker Sets", $this->owner->MapMarkerSets(), $gridConfig2 ); From 73d1b355c99ec450a80961cf83d146bd3651a32c Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:08:30 +0700 Subject: [PATCH 317/354] FIXES: Scrutinizer fixes, removal of dead code --- code/MapUtil.php | 146 ++++++++++++++++++----------------------------- 1 file changed, 57 insertions(+), 89 deletions(-) diff --git a/code/MapUtil.php b/code/MapUtil.php index c96de72..51214a9 100644 --- a/code/MapUtil.php +++ b/code/MapUtil.php @@ -23,12 +23,6 @@ class MapUtil */ public static $map_height = '400px'; - /** @var int Icon width of the gmarker **/ - public static $iconWidth = 24; - - /** @var int Icon height of the gmarker **/ - public static $iconHeight = 24; - /** * @var int Prefix for the div ID of the map */ @@ -39,11 +33,6 @@ class MapUtil */ public static $automatic_center = true; - /** - * @var boolean Show directions fields on the map - */ - public static $direction_fields = false; - /** * @var boolean Show the marker fields on the map */ @@ -59,9 +48,6 @@ class MapUtil */ public static $center = 'Paris, France'; - /* Width of the map information window */ - public static $info_window_width = 250; - /* Signals whether at least one map has already been rendered */ private static $map_already_rendered = false; @@ -69,6 +55,21 @@ class MapUtil private static $allow_full_screen = null; + public static function reset() { + self::$api_key = null; + self::$instances = 0; + self::$map_width = '100%'; + self::$map_height = '400px'; + self::$div_id = "google_map"; + self::$automatic_center = true; + self::$hide_marker = false; + self::$map_type = 'google.maps.MapTypeId.ROADMAP'; + self::$center = 'Paris, France'; + self::$map_already_rendered = false; + self::$allow_full_screen = null; + Config::inst()->update('Mappable', 'language', 'en'); + } + /** * Set the API key for Google Maps * @@ -78,7 +79,9 @@ public static function set_api_key($key) { self::$api_key = $key; } - + /** + * @param boolean $new_map_already_rendered + */ public static function set_map_already_rendered($new_map_already_rendered) { self::$map_already_rendered = $new_map_already_rendered; } @@ -87,7 +90,6 @@ public static function get_map_already_rendered() { return self::$map_already_rendered; } - /** * Set the default size of the map * @@ -99,62 +101,35 @@ public static function set_map_size($width, $height) { self::$map_height = $height; } - /** - * Set the type of the gmap - * - * @param string $mapType (can be 'google.maps.MapTypeId.ROADMAP', 'G_SATELLITE_MAP', - * 'G_HYBRID_MAP', 'G_PHYSICAL_MAP') - * - * @return void - */ - public function set_map_type($mapType) - { - self::$map_type = $mapType; - } - - /** - * Set the with of the gmap infowindow (on marker clik) - * - * @param int $info_window_width GoogleMap info window width - * - * @return void - */ - public function set_info_window_width($info_window_width) - { - self::$info_window_width = $info_window_width; - } - - /** - * Set the center of the gmap (an address) - * - * @param string $center GoogleMap center (an address) - * - * @return void - */ - public function set_center($center) - { - self::$center = $center; - } - - /** - * Set the size of the icon markers - * - * @param int $iconWidth GoogleMap marker icon width - * @param int $iconHeight GoogleMap marker icon height - * - * @return void - */ - - public function set_icon_size($iconWidth,$iconHeight) - { - self::$iconWidth = $iconWidth; - self::$iconHeight = $iconHeight; - } + /** + * FIXME - NOT USED? + * Set the type of the gmap + * + * @param string $mapType (can be 'google.maps.MapTypeId.ROADMAP', 'G_SATELLITE_MAP', + * 'G_HYBRID_MAP', 'G_PHYSICAL_MAP') + * + * @return void + */ + public static function set_map_type($mapType) { + self::$map_type = $mapType; + } + + /** + * Set the center of the gmap (an address, using text geocoder query) + * + * @param string $center GoogleMap center (an address) + * + * @return void + */ + public static function set_center($center) + { + self::$center = $center; + } /** * Get a new GoogleMapAPI object and load it with the default settings * - * @return GoogleMapAPI + * @return MapAPI */ public static function instance() { @@ -164,11 +139,6 @@ public static function instance() self::$allow_full_screen = Config::inst()->get('Mappable', 'allow_full_screen'); } - // for JS - if (self::$allow_full_screen === false) { - self:$allow_full_screen = 'asdfsda'; - } - $url = Director::absoluteBaseURL(); // remove http and https @@ -184,18 +154,16 @@ public static function instance() $key = $key[$host]; } - $gmap = new MapAPI($key); $gmap->setDivId(self::$div_id."_".self::$instances); $gmap->setEnableAutomaticCenterZoom(self::$automatic_center); - $gmap->setDisplayDirectionFields(self::$direction_fields); $gmap->setSize(self::$map_width, self::$map_height); $gmap->setDefaultHideMarker(self::$hide_marker); - $gmap->setMapType(self::$map_type); - $gmap->setCenter(self::$center); - $gmap->setIconSize(self::$iconWidth, self::$iconHeight); - $gmap->setIncludeDownloadJavascript(self::$map_already_rendered); - $gmap->setAllowFullScreen(self::$allow_full_screen); + $gmap->setMapType(self::$map_type); + $gmap->setCenter(self::$center); + $gmap->setAllowFullScreen(self::$allow_full_screen); + $language = Config::inst()->get('Mappable', 'language'); + $gmap->setLang($language); return $gmap; } @@ -207,7 +175,7 @@ public static function instance() * @return string */ public static function sanitize($content) { - return addslashes(str_replace(array("\n","\r", "\t"), '' ,$content)); + return addslashes(str_replace(array("\n", "\r", "\t"), '', $content)); } @@ -216,16 +184,16 @@ public static function sanitize($content) { * and places all of the items in a {@link SS_List} * e.g. {@link DataList} or {@link ArrayList} on the map * - * @param SS_List $set + * @param SS_List list of objects to display on a map + * @param array $infowindowtemplateparams Optional array of extra parameters to pass to the map info window * @return MapAPI */ - public static function get_map(SS_List $list, $optionalinfowindowtemplatevalues) { + public static function get_map(SS_List $list, $infowindowtemplateparams) { $gmap = self::instance(); - if($list) { + if ($list) { foreach ($list as $mappable) { - if (self::ChooseToAddDataobject($mappable)) { - $gmap->addMarkerAsObject($mappable, $optionalinfowindowtemplatevalues); - } + if (self::ChooseToAddDataobject($mappable)) + $gmap->addMarkerAsObject($mappable, $infowindowtemplateparams); } } return $gmap; @@ -242,7 +210,7 @@ public static function get_map(SS_List $list, $optionalinfowindowtemplatevalues) private static function ChooseToAddDataobject(DataObject $do) { $isMappable = $do->is_a('Mappable'); - foreach($do->getExtensionInstances() as $extension) { + foreach ($do->getExtensionInstances() as $extension) { $isMappable = $isMappable || $extension instanceof Mappable; } @@ -250,6 +218,6 @@ private static function ChooseToAddDataobject(DataObject $do) { ? $do->MapPinEdited : true; - return $isMappable && $filterMapPinEdited ; + return $isMappable && $filterMapPinEdited; } } From 2f4a02e92d95098ca93d856c33b27c01841a6e5d Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:09:44 +0700 Subject: [PATCH 318/354] FIX: Static map fixed after testing --- code/MappableData.php | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/code/MappableData.php b/code/MappableData.php index d965a6d..715c02c 100644 --- a/code/MappableData.php +++ b/code/MappableData.php @@ -45,14 +45,10 @@ public function getRenderableMap($width = null, $height = null, $zoom = 9) { $gmap->setEnableAutomaticCenterZoom(false); if ($this->owner->MapPinEdited) { $gmap->setLatLongCenter(array( - '200', - '4', - $this->owner->getMappableLatitude(), - $this->owner->getMappableLongitude() + 'lat' => $this->owner->getMappableLatitude(), + 'lng' => $this->owner->getMappableLongitude() )); } - - MapUtil::set_map_already_rendered(true); return $gmap; } @@ -63,34 +59,42 @@ public function getRenderableMap($width = null, $height = null, $zoom = 9) { * You can use MappableData.staticmap_api_url config var to set the domain of the static map. * You can use MappableData.staticmap_default_zoom config var to set the default zoom for the static map. * - * @uses Mappable::getMappableMapPin() to draw a special marker, be sure this image is public available + * @uses Mappable::getMappableMapPin() to draw a special marker, be sure this image is publicly available * - * @param null $width - * @param null $height + * @param int $width + * @param int $height * @return string */ - public function StaticMap($width = null, $height = null) { - $w = $width ? $width : MapUtil::$map_width; - $h = $height ? $height : MapUtil::$map_height; + public function StaticMap($width, $height, $zoom = null, $mapType = 'roadmap') { $lat = $this->owner->getMappableLatitude(); $lng = $this->owner->getMappableLongitude(); $pin = $this->owner->getMappableMapPin(); + // use provided zoom or set a default + if ($zoom == null) { + $zoom = Config::inst()->get('MappableData', 'staticmap_default_zoom'); + } + + //https://maps.googleapis.com/maps/api/staticmap?center=Berkeley,CA&zoom=14&size=400x400&key=YOUR_API_KEY + //maps.googleapis.com/maps/api/staticmap'; + $apiurl = Config::inst()->get('MappableData', 'staticmap_api_url'); $urlparts = array( 'center' => "$lat,$lng", 'markers' => "$lat,$lng", - 'zoom' => Config::inst()->get('MappableData', 'staticmap_default_zoom'), - 'size' => "${w}x$h", - 'sensor' => 'false' //@todo: make sensor param configurable + 'zoom' => $zoom, + 'size' => "{$width}x{$height}", + 'sensor' => 'false', //@todo: make sensor param configurable + 'maptype' => $mapType ); if ($pin) { $urlparts['markers'] = "icon:$pin|$lat,$lng"; } $src = htmlentities($apiurl . '?' . http_build_query($urlparts)); - return ''.$this->owner->Title.''; + + return ''.$this->owner->Title.''; } } From d64a3c7f869142b9e98d9c78d202507bf0adc5b5 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:10:11 +0700 Subject: [PATCH 319/354] MINOR: Reset map counter method for testing purposes --- code/shortcodes/GoogleMapShortCodeHandler.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/code/shortcodes/GoogleMapShortCodeHandler.php b/code/shortcodes/GoogleMapShortCodeHandler.php index 489b496..b54b679 100644 --- a/code/shortcodes/GoogleMapShortCodeHandler.php +++ b/code/shortcodes/GoogleMapShortCodeHandler.php @@ -64,4 +64,11 @@ public static function parse_googlemap($arguments, $caption = null, $parser = nu //return the template customised with the parmameters return $template->process(new ArrayData($customised)); } + + /** + * This is only used for testing, otherwise the sequence of tests change the number returned + */ + public static function resetCounter() { + self::$gsv_ctr = 1; + } } From b0137800a3a1e1d1b8212d689db533aef35c4b48 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:10:22 +0700 Subject: [PATCH 320/354] MINOR: Reset map counter method for testing purposes --- code/shortcodes/GoogleStreetViewShortCodeHandler.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/code/shortcodes/GoogleStreetViewShortCodeHandler.php b/code/shortcodes/GoogleStreetViewShortCodeHandler.php index f5041d0..2c2aba0 100644 --- a/code/shortcodes/GoogleStreetViewShortCodeHandler.php +++ b/code/shortcodes/GoogleStreetViewShortCodeHandler.php @@ -69,4 +69,11 @@ public static function parse_googlestreetview($arguments, $caption = null, $pars //return the template customised with the parmameters return $template->process(new ArrayData($customised)); } + + /** + * This is only used for testing, otherwise the sequence of tests change the number returned + */ + public static function resetCounter() { + self::$gsv_ctr = 1; + } } From d981127c2ea04e5d573988426290978d97c94056 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:11:58 +0700 Subject: [PATCH 321/354] FIX: Move map key and language into the config file. Also make map info window consistent --- _config/maps.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/_config/maps.yml b/_config/maps.yml index f0552ca..be0c6e1 100644 --- a/_config/maps.yml +++ b/_config/maps.yml @@ -6,6 +6,10 @@ Mappable: allow_full_screen: true use_compressed_assets: false mapping_service: 'Google' + #service_key: 'YOUR SERVICE KEY' + + #Language to load the Map Service in + language: 'en' MapExtension: - map_info_window_suffix: 'MapInfoWindow' + map_info_window_suffix: '_MapInfoWindow' From 2d85bf310237816a6e90e31f58885e28554c558a Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:13:22 +0700 Subject: [PATCH 322/354] ENHANCEMENT: Separated geocoder out for easy of replacing --- geocoder/MappableGeocoder.php | 11 ++++++ geocoder/MappableGeocoderException.php | 5 +++ geocoder/MappableGoogleGeocoder.php | 48 ++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 geocoder/MappableGeocoder.php create mode 100644 geocoder/MappableGeocoderException.php create mode 100644 geocoder/MappableGoogleGeocoder.php diff --git a/geocoder/MappableGeocoder.php b/geocoder/MappableGeocoder.php new file mode 100644 index 0000000..abda7b9 --- /dev/null +++ b/geocoder/MappableGeocoder.php @@ -0,0 +1,11 @@ +load($cacheKey))) { + if ($json = @file_get_contents( + "http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=". + urlencode($searchString))) { + $response = Convert::json2array($json); + + if ($response['status'] != 'OK') { + if ($response['status'] == 'ZERO_RESULTS') { + $locations = array(); + } else { + throw new Exception('Google status returned error'); + } + + } else { + $locations = $response['results']; + } + + // save result in cache + $cache->save($json, $cacheKey); + } + } else { + $cached = true; + } + + if ($cached) { + $response = Convert::json2array($json); + $locations = $response['results']; + } + return $locations; + + } +} From 323d377d9ea07e8e93daf9bbb3fae761a2ceb650 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:15:12 +0700 Subject: [PATCH 323/354] ENHANCEMENT: Cache google geocoder queries for a day --- _config.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/_config.php b/_config.php index 9e757ab..f7e4b8c 100755 --- a/_config.php +++ b/_config.php @@ -8,3 +8,6 @@ ShortcodeParser::get('default')->register('GoogleStreetView',array('GoogleStreetViewShortCodeHandler','parse_googlestreetview')); ShortcodeParser::get('default')->register('GoogleMap',array('GoogleMapShortCodeHandler','parse_googlemap')); + +// Cache for a day +SS_Cache::set_cache_lifetime('mappablegeocoder', 24*60*60); From 9e4e31873153b3d781926450b81fdf674486d897 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sat, 26 Dec 2015 17:16:22 +0700 Subject: [PATCH 324/354] MINOR: Remove class no longer used for tests --- tests/models/TestPageMapExtension.php | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 tests/models/TestPageMapExtension.php diff --git a/tests/models/TestPageMapExtension.php b/tests/models/TestPageMapExtension.php deleted file mode 100644 index ab9a8ac..0000000 --- a/tests/models/TestPageMapExtension.php +++ /dev/null @@ -1,5 +0,0 @@ - 'Boolean'); -} From 29f572d5e032d7db9f1a8032f64d7f1be7b7501e Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sun, 27 Dec 2015 14:04:04 +0700 Subject: [PATCH 325/354] ENHANCEMENT: Coverage submitted to coveralls --- .travis.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b71a3a3..5062db8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ addons: env: global: - DB=MYSQL CORE_RELEASE=3.1 - - MODULE_PATH="mappable" + - MODULE_PATH=mappable matrix: allow_failures: @@ -35,11 +35,15 @@ before_script: - git clone git://github.com/silverstripe-labs/silverstripe-travis-support.git ~/travis-support - php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss - cd ~/builds/ss + - composer require satooshi/php-coveralls script: - vendor/bin/phpunit --coverage-clover=coverage.clover -c $MODULE_PATH/phpunit.xml $MODULE_PATH/tests/ -after_script: +after_success: + - cp coverage.clover ~/coverage.xml + - mkdir -p build/logs + - travis_retry php vendor/bin/coveralls -v --coverage_clover coverage.clover - mv coverage.clover ~/build/$TRAVIS_REPO_SLUG/ - cd ~/build/$TRAVIS_REPO_SLUG - wget https://scrutinizer-ci.com/ocular.phar From c29dd79190c3d302e32fd4b7d6c571e22609fea1 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sun, 27 Dec 2015 15:07:30 +0700 Subject: [PATCH 326/354] FIX: Added missing items discovered during unit testing --- composer.json | 6 +----- javascript/google/MapGoogleHTML.ss | 7 +++++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index ab9aac4..8c05a55 100644 --- a/composer.json +++ b/composer.json @@ -12,14 +12,10 @@ } ], "require": { - "silverstripe/framework": "~3.1" + "silverstripe/cms": "~3.1" }, "support": { "issues": "https://github.com/gordonbanderson/Mappable/issues" }, - "require": - { - "silverstripe/framework": "~3.1" - }, "license": "MIT" } diff --git a/javascript/google/MapGoogleHTML.ss b/javascript/google/MapGoogleHTML.ss index fabee94..566aec4 100644 --- a/javascript/google/MapGoogleHTML.ss +++ b/javascript/google/MapGoogleHTML.ss @@ -1,5 +1,5 @@ <% include GoogleJavaScript %> -
style="width:{$Width}; height: {$Height};" +
data-google-map-key="$GoogleMapKey"<% end_if %><% if $GoogleMapLang %> data-google-map-lang="$GoogleMapLang" <% end_if %><% if ShowInlineMapDivStyle %> style="width:{$Width}; height: {$Height};" <% end_if %><% if AdditionalCssClasses %> class="$AdditionalCssClasses"<% end_if %> data-map data-centre='$LatLngCentre' @@ -9,7 +9,10 @@ data-allowfullscreen='$AllowFullScreen' data-clusterergridsize=$ClustererGridSize, data-clusterermaxzoom=$ClustererMaxZoom, data-enableautocentrezoom=$EnableAutomaticCenterZoom -data-mapmarkers= '$MapMarkers' +data-enablewindowzoom=$EnableWindowZoom +data-infowindowzoom=$InfoWindowZoom +data-mapmarkers='$MapMarkers' +data-defaulthidemarker=$DefaultHideMarker data-lines='$Lines' data-kmlfiles='$KmlFiles' data-mapstyles='$JsonMapStyles' From 57c3e05a94cdf990efff5b8f557b83939a67109f Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sun, 27 Dec 2015 15:10:43 +0700 Subject: [PATCH 327/354] FIX: Remove template that is not longer used --- javascript/google/MapGoogleJS.ss | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 javascript/google/MapGoogleJS.ss diff --git a/javascript/google/MapGoogleJS.ss b/javascript/google/MapGoogleJS.ss deleted file mode 100644 index a3800da..0000000 --- a/javascript/google/MapGoogleJS.ss +++ /dev/null @@ -1,23 +0,0 @@ -/* mapping of google_map_N to an array of markers */ -<% if DownloadJS %> -var infoWindows = []; -var gmarkers = []; -var mapLayers = []; -var mapLines = []; -<% end_if %> -var options_$GoogleMapID = { - centre: $LatLngCentre, - zoom: $Zoom, - maptype: '$MapType', - domid: '$GoogleMapID', - allowfullscreen: $AllowFullScreen, - mapmarkers: $MapMarkers, - lines: $Lines, - kmlfiles: $KmlFiles, - mapstyles: $JsonMapStyles, - useclusterer: $UseClusterer, - clusterergridsize: $ClustererGridSize, - clusterermaxzoom: $ClustererMaxZoom, - enableautocentrezoom: $EnableAutomaticCenterZoom -} -registerMap(options_$GoogleMapID); From 421d5e42f3d2b860092dd9035b4526bf1ba4bbdb Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sun, 27 Dec 2015 15:11:44 +0700 Subject: [PATCH 328/354] FIXES: Default hide marker, info window zoom now added --- javascript/google/maputil.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/javascript/google/maputil.js b/javascript/google/maputil.js index e58bb64..42f60ba 100644 --- a/javascript/google/maputil.js +++ b/javascript/google/maputil.js @@ -14,11 +14,12 @@ var primeMap; * @param {String} icon URL of alternative map icon, or blank for default * @param {boolean} useClusterer Whether or not to use clusterer * @param {boolean} enableWindowZoom Whether or not to enable zoom on the rendered map + * @param {boolean} infoWindowZoom FIXME: To do * @param {boolean} defaultHideMarker Whether or not to hide markers initially * @return {MapMarker} Google map pin object */ function createMarker(map, lat, lng, html, category, icon, useClusterer, enableWindowZoom, - defaultHideMarker) { + infoWindowZoom, defaultHideMarker) { mapId = map.getDiv().getAttribute('id'); @@ -37,7 +38,7 @@ var primeMap; google.maps.event.addListener(marker, "click", function() { if (enableWindowZoom) { - map.setCenter(new google.maps.LatLng(lat, lng), 12); // $InfoWindowZoom); + map.setCenter(new google.maps.LatLng(lat, lng), infoWindowZoom); } var infoWindow = infoWindows[mapId]; infoWindow.setContent(html); @@ -45,10 +46,13 @@ var primeMap; }); //FIXME gmarkers[mapId].push(marker); - - if (defaultHideMarker) { - marker.hide(); + console.log(defaultHideMarker === false); + if (defaultHideMarker == '1') { + marker.setVisible(false); + } else { + marker.setVisible(true); } + return marker; } @@ -80,7 +84,7 @@ var primeMap; * @param {boolean} defaultHideMarker Whether or not to hide markers * * @return array of Google map markers converted from the JSON data */ - function addAllMarkers(map, markers, useClusterer, enableWindowZoom, defaultHideMarker) { + function addAllMarkers(map, markers, useClusterer, enableWindowZoom, infoWindowZoom, defaultHideMarker) { // these will be altered by adding markers map.minLat = 1000000; @@ -94,7 +98,7 @@ var primeMap; var markerinfo = markers[i]; var marker = createMarker(map, markerinfo.latitude, markerinfo.longitude, markerinfo.html, markerinfo.category, markerinfo.icon, useClusterer, enableWindowZoom, - defaultHideMarker); + infoWindowZoom, defaultHideMarker); var latitude = parseFloat(markerinfo.latitude); var longitude = parseFloat(markerinfo.longitude); @@ -306,9 +310,10 @@ var primeMap; var markerjson = $.parseJSON(mapnode.attr('data-mapmarkers')); var useClusterer = mapnode.attr('data-useclusterer'); var enableAutomaticCenterZoom = mapnode.attr('data-enableautocentrezoom'); + var infoWindowZoom = mapnode.attr('data-infowindowzoom'); var defaultHideMarker = mapnode.attr('data-defaulthidemarker'); var markers = addAllMarkers(map, markerjson, useClusterer, - enableAutomaticCenterZoom, defaultHideMarker); + enableAutomaticCenterZoom, infoWindowZoom, defaultHideMarker); var allowfullscreen = parseInt(mapnode.attr('data-allowfullscreen')); if (enableAutomaticCenterZoom == 1) { From 656ec309cc174d962e062cda706e0c2d80d22439 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sun, 27 Dec 2015 15:28:36 +0700 Subject: [PATCH 329/354] FIX: Roll statements onto a single line in order to reduce vertical whitespace --- templates/Includes/GoogleJavaScript.ss | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/templates/Includes/GoogleJavaScript.ss b/templates/Includes/GoogleJavaScript.ss index 0e47f96..4489c20 100644 --- a/templates/Includes/GoogleJavaScript.ss +++ b/templates/Includes/GoogleJavaScript.ss @@ -1,8 +1 @@ -<% require javascript("framework/thirdparty/jquery/jquery.js") %> -<% if $UseCompressedAssets %> -<% require javascript("mappable/javascript/google/mappablegoogle.min.js") %> -<% else %> -<% require javascript("mappable/javascript/google/FullScreenControl.js") %> -<% require javascript("mappable/javascript/google/markerclusterer.js") %> -<% require javascript("mappable/javascript/google/maputil.js") %> -<% end_if %> +<% require javascript("framework/thirdparty/jquery/jquery.js") %><% if $UseCompressedAssets %><% require javascript("mappable/javascript/google/mappablegoogle.min.js") %><% else %><% require javascript("mappable/javascript/google/FullScreenControl.js") %><% require javascript("mappable/javascript/google/markerclusterer.js") %><% require javascript("mappable/javascript/google/maputil.js") %><% end_if %> From 437b501f9968baa6992de7e8f26423a501092883 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Sun, 27 Dec 2015 16:02:08 +0700 Subject: [PATCH 330/354] MINOR: Added badges to README for Travis, Scrutinzer and Coveralls --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 1ff9d42..cb58642 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ # Mappable +[![Build Status](https://travis-ci.org/gordonbanderson/Mappable.svg?branch=3.1-WIP)](https://travis-ci.org/gordonbanderson/Mappable) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/badges/quality-score.png?b=3.1-WIP)](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/?branch=3.1-WIP) +[![Code Coverage](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/badges/coverage.png?b=3.1-WIP)](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/?branch=3.1-WIP) +[![Build Status](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/badges/build.png?b=3.1-WIP)](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/build-status/3.1-WIP) +[![Coverage Status](https://coveralls.io/repos/gordonbanderson/Mappable/badge.svg?branch=3.1-WIP&service=github)](https://coveralls.io/github/gordonbanderson/Mappable?branch=3.1-WIP) ## Maintainers From cd22fcbf810556a0afb94bc6e06976daaf352a10 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 28 Dec 2015 15:40:12 +0700 Subject: [PATCH 331/354] ENHANCEMENT: Addition of getGuidePoints method, mainly for testing --- code/LatLongField.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/code/LatLongField.php b/code/LatLongField.php index bda31f9..bd58d2b 100755 --- a/code/LatLongField.php +++ b/code/LatLongField.php @@ -116,4 +116,12 @@ public function setGuidePoints($newGuidePoints) { $this->guidePoints = $newGuidePoints; } + /** + * Accessor to guidepoints. For testing purposes + * @return array guidepoints + */ + public function getGuidePoints() { + return $this->guidePoints; + } + } From 43b60094098b162bdeb52d0867e738f04bb86a22 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 28 Dec 2015 15:39:50 +0700 Subject: [PATCH 332/354] FIX: Move application of MapExtension for PointOfInterest to the points of interest module --- _config/extensions.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/_config/extensions.yml b/_config/extensions.yml index bdef6bd..94b4bb7 100644 --- a/_config/extensions.yml +++ b/_config/extensions.yml @@ -2,10 +2,6 @@ Name: mappable After: 'framework/*','cms/*' --- -PointOfInterest: - extensions: - - MapExtension - DataObject: extensions: - MappableData From e2a1aa2113f306c53f43ade3fe44cec810f9429f Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 28 Dec 2015 15:39:28 +0700 Subject: [PATCH 333/354] FIX: Move cache key to points of interest module --- _config/cachekey.yml | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 _config/cachekey.yml diff --git a/_config/cachekey.yml b/_config/cachekey.yml deleted file mode 100644 index e558e52..0000000 --- a/_config/cachekey.yml +++ /dev/null @@ -1,3 +0,0 @@ -CacheKeyHelper: - SiteTree: ['POIMapPage'] - DataObject: ['PointOfInterest'] From 93ff38e98d85b83b3ecf16ee75ed470ca6084022 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 29 Dec 2015 18:02:15 +0700 Subject: [PATCH 334/354] WIP: Trying codecov --- .travis.yml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5062db8..db2b4e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,9 @@ addons: packages: - tidy +before_install: + - pip install --user codecov + env: global: - DB=MYSQL CORE_RELEASE=3.1 @@ -41,10 +44,11 @@ script: - vendor/bin/phpunit --coverage-clover=coverage.clover -c $MODULE_PATH/phpunit.xml $MODULE_PATH/tests/ after_success: - - cp coverage.clover ~/coverage.xml - - mkdir -p build/logs - - travis_retry php vendor/bin/coveralls -v --coverage_clover coverage.clover - - mv coverage.clover ~/build/$TRAVIS_REPO_SLUG/ - - cd ~/build/$TRAVIS_REPO_SLUG - - wget https://scrutinizer-ci.com/ocular.phar - - php ocular.phar code-coverage:upload --format=php-clover coverage.clover + - cp coverage.clover ~/coverage.xml + - mkdir -p build/logs + - travis_retry php vendor/bin/coveralls -v --coverage_clover coverage.clover + - mv coverage.clover ~/build/$TRAVIS_REPO_SLUG/ + - cd ~/build/$TRAVIS_REPO_SLUG + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover coverage.clover + - codecov From 6661541548481859e37f8fe548cfc3b697b26d58 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 30 Dec 2015 00:31:11 +0700 Subject: [PATCH 335/354] WIP: Changing code coverage provider. Tidied up badges --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index cb58642..4de73e4 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@ # Mappable [![Build Status](https://travis-ci.org/gordonbanderson/Mappable.svg?branch=3.1-WIP)](https://travis-ci.org/gordonbanderson/Mappable) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/badges/quality-score.png?b=3.1-WIP)](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/?branch=3.1-WIP) -[![Code Coverage](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/badges/coverage.png?b=3.1-WIP)](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/?branch=3.1-WIP) [![Build Status](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/badges/build.png?b=3.1-WIP)](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/build-status/3.1-WIP) -[![Coverage Status](https://coveralls.io/repos/gordonbanderson/Mappable/badge.svg?branch=3.1-WIP&service=github)](https://coveralls.io/github/gordonbanderson/Mappable?branch=3.1-WIP) +[![codecov.io](https://codecov.io/github/gordonbanderson/Mappable/coverage.svg?branch=3.1)](https://codecov.io/github/gordonbanderson/Mappable?branch=3.1) ## Maintainers From b908baef26c44f119315e6d2d893ece4ad59f2b3 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Wed, 30 Dec 2015 00:35:46 +0700 Subject: [PATCH 336/354] FIX: Seems codecov only goes with main branch --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4de73e4..35333d6 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://travis-ci.org/gordonbanderson/Mappable.svg?branch=3.1-WIP)](https://travis-ci.org/gordonbanderson/Mappable) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/badges/quality-score.png?b=3.1-WIP)](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/?branch=3.1-WIP) [![Build Status](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/badges/build.png?b=3.1-WIP)](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/build-status/3.1-WIP) -[![codecov.io](https://codecov.io/github/gordonbanderson/Mappable/coverage.svg?branch=3.1)](https://codecov.io/github/gordonbanderson/Mappable?branch=3.1) +[![codecov.io](https://codecov.io/github/gordonbanderson/Mappable/coverage.svg?branch=3.1)](https://codecov.io/github/gordonbanderson/Mappable?branch=3.1-WIP) ## Maintainers From 9ddab05639b056c0a939b2b74edc40f4dbb61010 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 4 Jan 2016 20:43:57 +0700 Subject: [PATCH 337/354] ENHANCEMENT: Test against 3.2 also as tests pass on a separate test branch --- .travis.yml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index db2b4e7..5ffc367 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,10 @@ matrix: include: - php: 5.6 env: DB=MYSQL + - php: 5.6 + env: DB=PGSQL + - php: 5.6 + env: DB=SQLITE - php: 5.5 env: DB=MYSQL - php: 5.4 @@ -29,7 +33,20 @@ matrix: env: DB=MYSQL - php: hhvm env: DB=MYSQL - before_install: + - php: 5.6 + env: DB=MYSQL CORE_RELEASE=3.2 + - php: 5.6 + env: DB=PGSQL CORE_RELEASE=3.2 + - php: 5.6 + env: DB=SQLITE CORE_RELEASE=3.2 + - php: 5.5 + env: DB=MYSQL CORE_RELEASE=3.2 + - php: 5.4 + env: DB=MYSQL CORE_RELEASE=3.2 + - php: 5.3 + env: DB=MYSQL CORE_RELEASE=3.2 + - php: hhvm + env: DB=MYSQL CORE_RELEASE=3.2 before_script: From 3a26d64877a7d17ea21c66bcb86b38f4f8ac4e11 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 4 Jan 2016 21:33:55 +0700 Subject: [PATCH 338/354] Autogenerated change log --- CHANGELOG.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..0c7af5c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,46 @@ +# Change Log + +## [Unreleased](https://github.com/gordonbanderson/Mappable/tree/HEAD) + +[Full Changelog](https://github.com/gordonbanderson/Mappable/compare/3.1.0...HEAD) + +**Closed issues:** + +- Icon Width and Height Not Used [\#42](https://github.com/gordonbanderson/Mappable/issues/42) +- Can not disable clusterer [\#29](https://github.com/gordonbanderson/Mappable/issues/29) +- Multiple markers on one map [\#28](https://github.com/gordonbanderson/Mappable/issues/28) +- Move Points of Interest to a Separate Module [\#27](https://github.com/gordonbanderson/Mappable/issues/27) +- Autozoom Overly Enabled for Map Extension when Layers Extensions Also Enabled [\#26](https://github.com/gordonbanderson/Mappable/issues/26) +- Change to Unobtrusive JavaScript [\#25](https://github.com/gordonbanderson/Mappable/issues/25) +- StaticMap doesn't respect getMappableMapPin [\#22](https://github.com/gordonbanderson/Mappable/issues/22) +- Ensure That Map Extension Returns a String, not an Image File [\#19](https://github.com/gordonbanderson/Mappable/issues/19) +- Ensure Clusterer Settings Used [\#16](https://github.com/gordonbanderson/Mappable/issues/16) +- refactor maputil.js [\#15](https://github.com/gordonbanderson/Mappable/issues/15) +- Implement Guide Markers for POIs [\#12](https://github.com/gordonbanderson/Mappable/issues/12) +- Combine and Minify JavaScript Files [\#11](https://github.com/gordonbanderson/Mappable/issues/11) +- SilverStripe Code Guidline Compliance [\#10](https://github.com/gordonbanderson/Mappable/issues/10) +- Add Escape Button Option to Close Full Screen [\#9](https://github.com/gordonbanderson/Mappable/issues/9) +- Clusterer: merge info windows for cluster [\#5](https://github.com/gordonbanderson/Mappable/issues/5) +- Apparent debug text showing in map editing interface [\#4](https://github.com/gordonbanderson/Mappable/issues/4) + +**Merged pull requests:** + +- Scrutinizer Auto-Fixes [\#43](https://github.com/gordonbanderson/Mappable/pull/43) ([scrutinizer-auto-fixer](https://github.com/scrutinizer-auto-fixer)) +- Scrutinizer Auto-Fixes [\#39](https://github.com/gordonbanderson/Mappable/pull/39) ([scrutinizer-auto-fixer](https://github.com/scrutinizer-auto-fixer)) +- Scrutinizer Auto-Fixes [\#38](https://github.com/gordonbanderson/Mappable/pull/38) ([scrutinizer-auto-fixer](https://github.com/scrutinizer-auto-fixer)) +- Scrutinizer Auto-Fixes [\#36](https://github.com/gordonbanderson/Mappable/pull/36) ([scrutinizer-auto-fixer](https://github.com/scrutinizer-auto-fixer)) +- Scrutinizer Auto-Fixes [\#34](https://github.com/gordonbanderson/Mappable/pull/34) ([scrutinizer-auto-fixer](https://github.com/scrutinizer-auto-fixer)) +- Scrutinizer Auto-Fixes [\#33](https://github.com/gordonbanderson/Mappable/pull/33) ([scrutinizer-auto-fixer](https://github.com/scrutinizer-auto-fixer)) +- Scrutinizer Auto-Fixes [\#32](https://github.com/gordonbanderson/Mappable/pull/32) ([scrutinizer-auto-fixer](https://github.com/scrutinizer-auto-fixer)) +- Scrutinizer Auto-Fixes [\#31](https://github.com/gordonbanderson/Mappable/pull/31) ([scrutinizer-auto-fixer](https://github.com/scrutinizer-auto-fixer)) +- StaticMap update, fixes \#22 [\#23](https://github.com/gordonbanderson/Mappable/pull/23) ([wernerkrauss](https://github.com/wernerkrauss)) +- some refinement to filter dataobjects [\#21](https://github.com/gordonbanderson/Mappable/pull/21) ([wernerkrauss](https://github.com/wernerkrauss)) +- do not use window.onload - can be overwritten later [\#20](https://github.com/gordonbanderson/Mappable/pull/20) ([wernerkrauss](https://github.com/wernerkrauss)) +- Make template suffix for info window configurable [\#3](https://github.com/gordonbanderson/Mappable/pull/3) ([wernerkrauss](https://github.com/wernerkrauss)) +- get\_map now accepts more generic SS\_List instead of DataList [\#2](https://github.com/gordonbanderson/Mappable/pull/2) ([wernerkrauss](https://github.com/wernerkrauss)) +- Fixes for 3.1 [\#1](https://github.com/gordonbanderson/Mappable/pull/1) ([wernerkrauss](https://github.com/wernerkrauss)) + +## [3.1.0](https://github.com/gordonbanderson/Mappable/tree/3.1.0) (2013-01-23) + + +\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* \ No newline at end of file From e3eead350677e66630e0871776d1638851406595 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Mon, 4 Jan 2016 22:34:07 +0700 Subject: [PATCH 339/354] ENHANCEMENT: Added packagist badges --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 35333d6..16f0505 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://travis-ci.org/gordonbanderson/Mappable.svg?branch=3.1-WIP)](https://travis-ci.org/gordonbanderson/Mappable) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/badges/quality-score.png?b=3.1-WIP)](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/?branch=3.1-WIP) [![Build Status](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/badges/build.png?b=3.1-WIP)](https://scrutinizer-ci.com/g/gordonbanderson/Mappable/build-status/3.1-WIP) -[![codecov.io](https://codecov.io/github/gordonbanderson/Mappable/coverage.svg?branch=3.1)](https://codecov.io/github/gordonbanderson/Mappable?branch=3.1-WIP) +[![codecov.io](https://codecov.io/github/gordonbanderson/Mappable/coverage.svg?branch=3.1)](https://codecov.io/github/gordonbanderson/Mappable?branch=3.1-WIP)[![Latest Stable Version](https://poser.pugx.org/weboftalent/mappable/v/stable)](https://packagist.org/packages/weboftalent/mappable) [![Total Downloads](https://poser.pugx.org/weboftalent/mappable/downloads)](https://packagist.org/packages/weboftalent/mappable) [![Latest Unstable Version](https://poser.pugx.org/weboftalent/mappable/v/unstable)](https://packagist.org/packages/weboftalent/mappable) [![License](https://poser.pugx.org/weboftalent/mappable/license)](https://packagist.org/packages/weboftalent/mappable) ## Maintainers From 2b7eae2ed67ba5e1e29195462a93d77540d07492 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 5 Jan 2016 12:57:20 +0700 Subject: [PATCH 340/354] ENHANCEMENT: Scrutinizer will now check for PSR2 code violations --- .scrutinizer.yml | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 785d80c..93c0fde 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -4,19 +4,23 @@ inherit: true tools: external_code_coverage: timeout: 600 - php_mess_detector: true - php_code_sniffer: true - sensiolabs_security_checker: true - php_pdepend: true - php_loc: - enabled: true - excluded_dirs: [vendor, tests] - php_cpd: - enabled: true - excluded_dirs: [vendor, tests] - -checks: - php: true + php_code_sniffer: + config: + standard: PSR2 + php_cs_fixer: + extensions: + # Default: + - php + fixers: [] + enabled: false + filter: + paths: [tests/*,code/*] + excluded_paths: [] +coding_style: + php: + indentation: + general: + use_tabs: false filter: - paths: [templates/*, tests/*,code/*] + paths: [tests/*,code/*] From 9f3042ed3da49023b4de16313f1ad959463564a6 Mon Sep 17 00:00:00 2001 From: Gordon Anderson Date: Tue, 5 Jan 2016 13:04:04 +0700 Subject: [PATCH 341/354] FIX: Converted to PSR2 compliance --- code/LatLongField.php | 237 +-- code/MapAPI.php | 1373 +++++++++-------- code/MapExtension.php | 450 +++--- code/MapField.php | 94 +- code/MapLayer.php | 20 +- code/MapLayerExtension.php | 49 +- code/MapMarkerSetsExtension.php | 56 +- code/MapUtil.php | 442 +++--- code/Mappable.php | 106 +- code/MappableData.php | 186 +-- code/MappableDataObjectSet.php | 46 +- code/shortcodes/GoogleMapShortCodeHandler.php | 144 +- .../GoogleStreetViewShortCodeHandler.php | 152 +- tests/GoogleMapShortCodeTest.php | 107 +- tests/GoogleStreetViewShortCodeTest.php | 79 +- tests/LatLongFieldTest.php | 247 ++- tests/MapAPITest.php | 1003 ++++++------ tests/MapExtensionTest.php | 593 +++---- tests/MapFieldTest.php | 39 +- tests/MapLayerExtensionTest.php | 45 +- tests/MapLayerTest.php | 25 +- tests/MapMarkerSetsExtensionTest.php | 43 +- tests/MapUtilTest.php | 320 ++-- tests/MappableDataObjectSetTest.php | 104 +- tests/MappableDataTest.php | 298 ++-- 25 files changed, 3180 insertions(+), 3078 deletions(-) diff --git a/code/LatLongField.php b/code/LatLongField.php index bd58d2b..c7fb05f 100755 --- a/code/LatLongField.php +++ b/code/LatLongField.php @@ -1,96 +1,99 @@ 3) || - (!$children[0] instanceof FormField) || - (!$children[1] instanceof FormField) - ) user_error('LatLongField argument 1 must be an array containing at least two FormField '. - 'objects for Lat/Long values, respectively.', E_USER_ERROR); - - parent::__construct($children); - - $this->buttonText = $buttonText ? $buttonText : _t('LatLongField.LOOKUP', 'Search'); - $this->latField = $children[0]->getName(); - $this->longField = $children[1]->getName(); - - if (sizeof($children) == 3) { - $this->zoomField = $children[2]->getName(); - } - $name = ""; - foreach ($children as $field) { - $name .= $field->getName(); - } - - // hide the lat long and zoom fields from the interface - foreach ($this->FieldList() as $fieldToHide) { - $fieldToHide->addExtraClass('hide'); - } - - $this->name = $name; - } - - - public function FieldHolder($properties = array()) { - Requirements::javascript(THIRDPARTY_DIR.'/jquery/jquery.js'); - Requirements::javascript(THIRDPARTY_DIR.'/jquery-livequery/jquery.livequery.js'); - Requirements::javascript(THIRDPARTY_DIR.'/jquery-metadata/jquery.metadata.js'); - Requirements::javascript(MAPPABLE_MODULE_PATH.'/javascript/mapField.js'); - - $attributes = array( - 'class' => 'editableMap', - 'id' => 'GoogleMap', - 'data-LatFieldName' => $this->latField, - 'data-LonFieldName' => $this->longField, - 'data-ZoomFieldName' => $this->zoomField, - 'data-UseMapBounds' => false - ); - - Requirements::css('mappable/css/mapField.css'); - - // check for and if required add guide points - if (!empty($this->guidePoints)) { - $latlongps = array(); - - foreach ($this->guidePoints as $guidepoint) { - array_push($latlongps, $guidepoint); - } - - $guidePointsJSON = json_encode($latlongps); - // convert the mappable guidepoints to lat lon - - $attributes['data-GuidePoints'] = $guidePointsJSON; - - // we only wish to change the bounds to those of all the points iff - // the item currently has no location - $attributes['data-useMapBounds'] = true; - } - $content = '
'.$this->create_tag( - "div", - $attributes - ).'
'; - - $this->FieldList()->push(new LiteralField('locationEditor', $content)); - - $content2 = << 3) || + (!$children[0] instanceof FormField) || + (!$children[1] instanceof FormField) + ) { + user_error('LatLongField argument 1 must be an array containing at least two FormField '. + 'objects for Lat/Long values, respectively.', E_USER_ERROR); + } + + parent::__construct($children); + + $this->buttonText = $buttonText ? $buttonText : _t('LatLongField.LOOKUP', 'Search'); + $this->latField = $children[0]->getName(); + $this->longField = $children[1]->getName(); + + if (sizeof($children) == 3) { + $this->zoomField = $children[2]->getName(); + } + $name = ''; + foreach ($children as $field) { + $name .= $field->getName(); + } + + // hide the lat long and zoom fields from the interface + foreach ($this->FieldList() as $fieldToHide) { + $fieldToHide->addExtraClass('hide'); + } + + $this->name = $name; + } + + public function FieldHolder($properties = array()) + { + Requirements::javascript(THIRDPARTY_DIR.'/jquery/jquery.js'); + Requirements::javascript(THIRDPARTY_DIR.'/jquery-livequery/jquery.livequery.js'); + Requirements::javascript(THIRDPARTY_DIR.'/jquery-metadata/jquery.metadata.js'); + Requirements::javascript(MAPPABLE_MODULE_PATH.'/javascript/mapField.js'); + + $attributes = array( + 'class' => 'editableMap', + 'id' => 'GoogleMap', + 'data-LatFieldName' => $this->latField, + 'data-LonFieldName' => $this->longField, + 'data-ZoomFieldName' => $this->zoomField, + 'data-UseMapBounds' => false, + ); + + Requirements::css('mappable/css/mapField.css'); + + // check for and if required add guide points + if (!empty($this->guidePoints)) { + $latlongps = array(); + + foreach ($this->guidePoints as $guidepoint) { + array_push($latlongps, $guidepoint); + } + + $guidePointsJSON = json_encode($latlongps); + // convert the mappable guidepoints to lat lon + + $attributes['data-GuidePoints'] = $guidePointsJSON; + + // we only wish to change the bounds to those of all the points iff + // the item currently has no location + $attributes['data-useMapBounds'] = true; + } + $content = '
'.$this->create_tag( + 'div', + $attributes + ).'
'; + + $this->FieldList()->push(new LiteralField('locationEditor', $content)); + + $content2 = << @@ -99,29 +102,31 @@ public function FieldHolder($properties = array()) {
HTML; - $this->FieldList()->push(new LiteralField('mapSearch', $content2)); - - return parent::FieldHolder(); - } - - /* - Set guidance points for the map being edited. For example in a photographic set show the map - position of some other images so that subsequent photo edits do not start with a map centred - at the origin - - @var newGuidePoints array of points expressed as associative arrays containing keys latitude - and longitude mapping to geographical locations - */ - public function setGuidePoints($newGuidePoints) { - $this->guidePoints = $newGuidePoints; - } - - /** - * Accessor to guidepoints. For testing purposes - * @return array guidepoints - */ - public function getGuidePoints() { - return $this->guidePoints; - } - + $this->FieldList()->push(new LiteralField('mapSearch', $content2)); + + return parent::FieldHolder(); + } + + /* + Set guidance points for the map being edited. For example in a photographic set show the map + position of some other images so that subsequent photo edits do not start with a map centred + at the origin + + @var newGuidePoints array of points expressed as associative arrays containing keys latitude + and longitude mapping to geographical locations + */ + public function setGuidePoints($newGuidePoints) + { + $this->guidePoints = $newGuidePoints; + } + + /** + * Accessor to guidepoints. For testing purposes. + * + * @return array guidepoints + */ + public function getGuidePoints() + { + return $this->guidePoints; + } } diff --git a/code/MapAPI.php b/code/MapAPI.php index abb7fd2..d1d99b9 100644 --- a/code/MapAPI.php +++ b/code/MapAPI.php @@ -16,700 +16,719 @@ class MapAPI extends ViewableData { + /** GoogleMap key **/ + protected $googleMapKey = ''; - /** GoogleMap key **/ - protected $googleMapKey = ''; + /** GoogleMap ID for the HTML DIV **/ + protected $googleMapId = 'googlemapapi'; - /** GoogleMap ID for the HTML DIV **/ - protected $googleMapId = 'googlemapapi'; + /* Additional CSS classes to use when rendering the map */ + protected $set_additional_css_classes = ''; - /* Additional CSS classes to use when rendering the map */ - protected $set_additional_css_classes = ''; + /** Width of the gmap **/ + protected $width = 800; - /** Width of the gmap **/ - protected $width = 800; + /** Height of the gmap **/ + protected $height = 600; - /** Height of the gmap **/ - protected $height = 600; + /* array of lines to be drawn on the map */ + protected $lines = array(); - /* array of lines to be drawn on the map */ - protected $lines = array(); + /* kml file to be rendered */ + protected $kmlFiles = array(); - /* kml file to be rendered */ - protected $kmlFiles = array(); + /** Default zoom of the gmap **/ + protected $zoom = 9; - /** Default zoom of the gmap **/ - protected $zoom = 9; + /** Enable the zoom of the Infowindow **/ + protected $enableWindowZoom = false; - /** Enable the zoom of the Infowindow **/ - protected $enableWindowZoom = false; + /** Default zoom of the Infowindow **/ + protected $infoWindowZoom = 13; - /** Default zoom of the Infowindow **/ - protected $infoWindowZoom = 13; + /** Lang of the gmap **/ + protected $lang = 'en'; - /** Lang of the gmap **/ - protected $lang = 'en'; + /**Center of the gmap **/ + protected $center = 'Paris, France'; - /**Center of the gmap **/ - protected $center = 'Paris, France'; + /* + Additional CSS classes to render as a class attribute for the div of the + map. Use this if you want more fine grained control over your map using + CSS. If blank it will be ignored + */ + protected $additional_css_classes = ''; - /* - Additional CSS classes to render as a class attribute for the div of the - map. Use this if you want more fine grained control over your map using - CSS. If blank it will be ignored - */ - protected $additional_css_classes = ''; + /* Decided whether or not to show the inline map css style on div creation */ + protected $show_inline_map_div_style = true; + protected $latLongCenter = null; - /* Decided whether or not to show the inline map css style on div creation */ - protected $show_inline_map_div_style = true; + protected $jsonMapStyles = '[]'; - protected $latLongCenter = null; + /** + * Type of the gmap, can be: + * 'road' (roadmap), + * 'satellite' (sattelite/aerial photographs) + * 'hybrid' (hybrid of road and satellite) + * 'terrain' (terrain) + * The JavaScript for the mapping service will convert this into a suitable mapping type. + */ + protected $mapType = 'road'; - protected $jsonMapStyles = '[]'; + /** Content of the HTML generated **/ + protected $content = ''; - /** - * Type of the gmap, can be: - * 'road' (roadmap), - * 'satellite' (sattelite/aerial photographs) - * 'hybrid' (hybrid of road and satellite) - * 'terrain' (terrain) - * The JavaScript for the mapping service will convert this into a suitable mapping type - */ + protected $mapService = 'google'; - protected $mapType = 'road'; - - - /** Content of the HTML generated **/ - protected $content = ''; - - protected $mapService = 'google'; - - /** Hide the marker by default **/ - protected $defaultHideMarker = false; - - /** Extra content (marker, etc...) **/ - protected $contentMarker = ''; - - // a list of markers, markers being associative arrays - protected $markers = array(); - - /** Use clusterer to display a lot of markers on the gmap **/ - protected $useClusterer = false; - protected $gridSize = 50; - protected $maxZoom = 17; - protected $clustererLibraryPath = "/mappable/javascript/google/markerclusterer.js"; - - /** Enable automatic center/zoom **/ - protected $enableAutomaticCenterZoom = false; - - /** maximum longitude of all markers **/ - protected $maxLng = -1000000; - - /** minimum longitude of all markers **/ - protected $minLng = 1000000; - - /** max latitude of all markers **/ - protected $maxLat = -1000000; - - /** min latitude of all markers **/ - protected $minLat = 1000000; - - /** map center latitude (horizontal), calculated automatically as markers - are added to the map **/ - protected $centerLat = null; - - /** map center longitude (vertical), calculated automatically as markers - are added to the map **/ - protected $centerLng = null; - - /** factor by which to fudge the boundaries so that when we zoom encompass, - the markers aren't too close to the edge **/ - protected $coordCoef = 0.01; - - /* set this to true to render button to maximize / minimize a map */ - protected $allowFullScreen = null; - - /** - * Class constructor - * - * @param string $googleMapKey the googleMapKey - */ - - public function __construct($googleMapKey = '') { - $this->googleMapKey = $googleMapKey; - } - - public function setShowInlineMapDivStyle($new_show_inline_map_div_style) { - $this->show_inline_map_div_style = $new_show_inline_map_div_style; - return $this; - } - - public function setAdditionalCSSClasses($new_additional_css_classes) { - $this->additional_css_classes = $new_additional_css_classes; - return $this; - } - - - public function setMapStyle($newStyles) { - $this->jsonMapStyles = $newStyles; - return $this; - } - - /** - * Set the useClusterer parameter (optimization to display a lot of marker) - * - * @param boolean $useClusterer use cluster or not - * @param int $gridSize grid size - * @param int $maxZoom max zoom to cluster at - * - * * @return MapAPI This same object, in order to enable chaining of methods - */ - - public function setClusterer($useClusterer, $gridSize = 50, $maxZoom = 17, - $clustererLibraryPath = '/mappable/javascript/google/markerclusterer.js') { - $this->useClusterer = $useClusterer; - $this->gridSize = $gridSize; - $this->maxZoom = $maxZoom; - $this->clustererLibraryPath = $clustererLibraryPath; - return $this; - } - - /** - * Set the ID of the default gmap DIV - * - * @param string $googleMapId the google div ID - * - * @return MapAPI This same object, in order to enable chaining of methods - */ - - public function setDivId($googleMapId) { - $this->googleMapId = $googleMapId; - return $this; - } - - /** - * Set the size of the gmap. If these values are not provided - * then CSS is used instead - * - * @param int $width GoogleMap width - * @param int $height GoogleMap height - * - * @return MapAPI This same object, in order to enable chaining of methods - */ - - public function setSize($width, $height) { - $this->width = $width; - $this->height = $height; - return $this; - } - - /** - * Set the lang of the gmap - * - * @param string $lang GoogleMap lang : fr,en,.. - * - * @return MapAPI This same object, in order to enable chaining of methods - */ - - public function setLang($lang) { - $this->lang = $lang; - return $this; - } - - /** - * Set the zoom of the gmap - * - * @param int $zoom GoogleMap zoom. - * - * @return MapAPI This same object, in order to enable chaining of methods - */ - - public function setZoom($zoom) { - $this->zoom = $zoom; - return $this; - } - - /** - * Set the zoom of the infowindow - * - * @param int $infoWindowZoom GoogleMap information window zoom. - * - * @return MapAPI This same object, in order to enable chaining of methods - */ - - public function setInfoWindowZoom($infoWindowZoom) { - $this->infoWindowZoom = $infoWindowZoom; - return $this; - } - - /** - * Enable the zoom on the marker when you click on it - * - * @param boolean $enableWindowZoom info window enabled zoom. - * - * @return MapAPI This same object, in order to enable chaining of methods - */ - - public function setEnableWindowZoom($enableWindowZoom) { - $this->enableWindowZoom = $enableWindowZoom; - return $this; - } - - /** - * Enable theautomatic center/zoom at the gmap load - * - * @param boolean $enableAutomaticCenterZoom enable automatic centre zoom - * - * @return MapAPI This same object, in order to enable chaining of methods - */ - - public function setEnableAutomaticCenterZoom($enableAutomaticCenterZoom) { - $this->enableAutomaticCenterZoom = $enableAutomaticCenterZoom; - return $this; - } - - /** - * Set the center of the gmap (an address) - * - * @param string $center GoogleMap center (an address) - * - * @return MapAPI This same object, in order to enable chaining of methods - */ - - public function setCenter($center) { - $this->center = $center; - return $this; - } - - /** - * Set the type of the gmap. Also takes into account legacy settings - * - * FIXME - allow other valid settings in config for map type - * - * @param string $mapType Can be one of road,satellite,hybrid or terrain. Defaults to road - * - * @return MapAPI This same object, in order to enable chaining of methods - */ - - public function setMapType($mapType) { - $this->mapType = $mapType; - - // deal with legacy values for backwards compatbility - switch ($mapType) { - case 'google.maps.MapTypeId.SATELLITE': - $this->mapType = "satellite"; - break; - case 'google.maps.MapTypeId.G_HYBRID_MAP': - $this->mapType = "hybrid"; - break; - case 'google.maps.MapTypeId.G_PHYSICAL_MAP': - $this->mapType = "terrain"; - break; - case 'google.maps.MapTypeId.ROADMAP': - $this->mapType = "road"; - break; - } - return $this; - } - - /* - Set whether or not to allow the full screen tools - @return MapAPI This same object, in order to enable chaining of methods - */ - public function setAllowFullScreen($allowed) { - $this->allowFullScreen = $allowed; - return $this; - } - - /** - * Set the center of the gmap - * - * @return MapAPI This same object, in order to enable chaining of methods - **/ - public function setLatLongCenter($center) { - // error check, we want an associative array with lat,lng keys numeric - - if (!is_array($center)) { - throw new InvalidArgumentException('Center must be an associative array containing lat,lng'); - } - - $keys = array_keys($center); - sort($keys); - if (implode(',', $keys) != 'lat,lng') { - throw new InvalidArgumentException('Keys provided must be lat, lng'); - } - - $this->latLongCenter = $center; - return $this; - } - - /** - * Set the defaultHideMarker - * - * @param boolean $defaultHideMarker hide all the markers on the map by default - * - * @return MapAPI - */ - - public function setDefaultHideMarker($defaultHideMarker) { - $this->defaultHideMarker = $defaultHideMarker; - return $this; - } - - /** - * Get the google map content - * - * @return string the google map html code - */ - - public function getGoogleMap() { - return $this->content; - } - - - /** - * Get URL content using cURL. - * - * @param string $url the url - * - * @return string the html code - * - * @todo add proxy settings - */ - - public function getContent($url) { - $curl = curl_init(); - curl_setopt($curl, CURLOPT_TIMEOUT, 10); - curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); - curl_setopt($curl, CURLOPT_URL, $url); - $data = curl_exec($curl); - curl_close($curl); - return $data; - } - - /** - * Geocoding an address (address -> lat,lng) - * - * @param string $address an address - * - * @return string array with precision, lat & lng - */ - - public function geocoding($address) { - $geocoder = new MappableGoogleGeocoder(); - $locations = $geocoder->getLocations($address); - $result = null; - if (!empty($locations)) { - $place = $locations[0]; - $location = $place['geometry']['location']; - $result = array( - 'lat' => $location['lat'], - 'lon' => $location['lng'], - 'geocoded' => true - ); - - } else { - $result = array(); // no results - } - return $result; - } - - /** - * Add marker by his coord - * - * @param string $lat lat - * @param string $lng lngs - * @param string $html html code display in the info window - * @param string $category marker category - * @param string $icon an icon url - * - * @return MapAPI - */ - - public function addMarkerByCoords($lat, $lng, $html = '', $category = '', $icon = '') { - $m = array( - 'latitude' => $lat, - 'longitude' => $lng, - 'html' => $html, - 'category' => $category, - 'icon' => $icon - ); - array_push($this->markers, $m); - return $this; - } - - - /** - * Add marker by his address - * - * @param string $address an ddress - * @param string $content html code display in the info window - * @param string $category marker category - * @param string $icon an icon url - * - * @return MapAPI - */ - - public function addMarkerByAddress($address, $content = '', $category = '', $icon = '') { - $point = $this->geocoding($address); - if ($point !== null) { - $this->addMarkerByCoords($point['lat'], $point['lon'], $content, $category, $icon); - } - return $this; - } - - /** - * Add marker by an array of coord - * - * @param array $coordtab an array of lat,lng,content - * @param string $category marker category - * @param string $icon an icon url - * - * @return MapAPI - */ - - public function addArrayMarkerByCoords($coordtab, $category = '', $icon = '') { - foreach ($coordtab as $coord) { - $this->addMarkerByCoords($coord[0], $coord[1], $coord[2], $category, $icon); - } - return $this; - } - - - /** - * Adds a {@link ViewableData} object that implements {@link Mappable} - * to the map. - * @param $infowindowtemplateparams Optional array of extra parameters to pass to the map info window - * - * @param ViewableData $obj - */ - public function addMarkerAsObject(ViewableData $obj, $infowindowtemplateparams = null) { - $extensionsImplementMappable = false; - $extensions = Object::get_extensions(get_class($obj)); - if (is_array($extensions)) { - - foreach ($extensions as $extension) { - $class = new ReflectionClass($extension); - if ($class->implementsInterface('Mappable')) { - $extensionsImplementMappable = true; - } - - } - } - - if ($extensionsImplementMappable || - ($obj instanceof Mappable) || - (Object::has_extension($obj->ClassName, 'MapExtension')) - ) { - $cat = $obj->hasMethod('getMappableMapCategory') ? $obj->getMappableMapCategory() : "default"; - if ($infowindowtemplateparams !== null) { - foreach ($infowindowtemplateparams as $key => $value) { - $obj->{$key} = $value; - } - } - $this->addMarkerByCoords( - $obj->getMappableLatitude(), - $obj->getMappableLongitude(), - $obj->getMappableMapContent(), - $cat, - $obj->getMappableMapPin() - ); - } - - return $this; - } - - - /** - * Draws a line between two {@link ViewableData} objects - * - * @param ViewableData $one The first point - * @param ViewableData $two The second point - * @param string $color The hexidecimal color of the line - */ - public function connectPoints(ViewableData $one, ViewableData $two, $color = "#FF3300") { - $this->addLine( - array($one->getMappableLatitude(), $one->getMappableLongitude()), - array($two->getMappableLatitude(), $two->getMappableLongitude()), - $color - ); - } - - public function forTemplate() { - $this->generate(); - MapUtil::set_map_already_rendered(true); - return $this->getGoogleMap(); - } - - /** - * Add a KML file which will be rendered on this map. Normally used for likes - * of GPS traces from activities - * - * @param string $url url of the kml file compatible with gmap and gearth - * - * @return MapAPI - */ - - public function addKML($url) { - array_push($this->kmlFiles, $url); - return $this; - } - - - /* - Add a line to the map - - */ - public function addLine($from = array(), $to = array(), $color = "#FF3300") { - $line = array( - 'lat1' => $from[0], - 'lon1' => $from[1], - 'lat2' => $to[0], - 'lon2' => $to[1], - 'color' => $color - ); - - array_push($this->lines, $line); - return $this; - } - - - /* - For php 5.3 - */ - public static function jsonRemoveUnicodeSequences($struct) { - return preg_replace("/\\\\u([a-f0-9]{4})/e", - "iconv('UCS-4LE','UTF-8',pack('V', hexdec('U$1')))", - json_encode($struct)); - } - - - /** - * Generate the gmap - * - * @return void - */ - - public function generate() { - // from http://stackoverflow.com/questions/3586401/cant-decode-json-string-in-php - $jsonMarkers = null; - $linesJson = null; - $kmlJson = null; - - // prior to PHP version 5.4, one needs to use regex - if (PHP_VERSION_ID < 50400) { - $jsonMarkers = stripslashes(MapAPI::jsonRemoveUnicodeSequences($this->markers)); - $linesJson = stripslashes(MapAPI::jsonRemoveUnicodeSequences($this->lines)); - $kmlJson = stripslashes(MapAPI::jsonRemoveUnicodeSequences($this->kmlFiles)); - } else { - $jsonMarkers = stripslashes(json_encode($this->markers, JSON_UNESCAPED_UNICODE)); - $linesJson = stripslashes(json_encode($this->lines, JSON_UNESCAPED_UNICODE)); - $kmlJson = stripslashes(json_encode($this->kmlFiles, JSON_UNESCAPED_UNICODE)); - } - - // Center of the GMap - text centre takes precedence - $geocodeCentre = ($this->latLongCenter) ? - $this->latLongCenter : $this->geocoding($this->center); - - $latlngCentre = null; - // coordinates for centre depending on which method used - if (isset($geocodeCentre['geocoded'])) { - $latlngCentre = array( - 'lat' => $geocodeCentre['lat'], - 'lng' => $geocodeCentre['lon'] - ); - } else if (is_array($this->latLongCenter)) { - $latlngCentre = $this->latLongCenter; - } - - $this->LatLngCentreJSON = stripslashes(json_encode($latlngCentre)); - - $lenLng = $this->maxLng - $this->minLng; - $lenLat = $this->maxLat - $this->minLat; - $this->minLng -= $lenLng * $this->coordCoef; - $this->maxLng += $lenLng * $this->coordCoef; - $this->minLat -= $lenLat * $this->coordCoef; - $this->maxLat += $lenLat * $this->coordCoef; - - // add the css class mappable as a handle onto the map styling - $this->additional_css_classes .= ' mappable'; - - if (!$this->enableAutomaticCenterZoom) { - $this->enableAutomaticCenterZoom = 'false'; - } - - if (!$this->useClusterer) { - $this->useClusterer = 'false'; - } - - if (!$this->defaultHideMarker) { - $this->defaultHideMarker = 'false'; - } - - // initialise full screen as the config value if not already set - if ($this->allowFullScreen === null) { - $this->allowFullScreen = Config::inst()->get('Mappable', 'allow_full_screen'); - } - - if (!$this->allowFullScreen) { - $this->allowFullScreen = 'false'; - } - - if (!$this->enableWindowZoom) { - $this->enableWindowZoom = 'false'; - } - - $vars = new ArrayData(array( - - 'JsonMapStyles' => $this->jsonMapStyles, - 'AdditionalCssClasses' => $this->additional_css_classes, - 'Width' => $this->width, - 'Height' => $this->height, - 'ShowInlineMapDivStyle' => $this->show_inline_map_div_style, - 'InfoWindowZoom' => $this->infoWindowZoom, - 'EnableWindowZoom' => $this->enableWindowZoom, - 'MapMarkers' => $jsonMarkers, - 'DefaultHideMarker' => $this->defaultHideMarker, - 'LatLngCentre' => $this->LatLngCentreJSON, - 'EnableAutomaticCenterZoom' => $this->enableAutomaticCenterZoom, - 'Zoom' => $this->zoom, - 'MaxZoom' => $this->maxZoom, - 'GridSize' => $this->gridSize, - 'MapType' => $this->mapType, - 'GoogleMapID' => $this->googleMapId, - 'Lang'=>$this->lang, - 'UseClusterer'=>$this->useClusterer, - 'ClustererLibraryPath' => $this->clustererLibraryPath, - 'ClustererMaxZoom' => $this->maxZoom, - 'ClustererGridSize' => $this->gridSize, - 'Lines' => $linesJson, - 'KmlFiles' => $kmlJson, - 'AllowFullScreen' => $this->allowFullScreen, - 'UseCompressedAssets' => Config::inst()->get('Mappable', 'use_compressed_assets') - ) - ); - - if (!MapUtil::get_map_already_rendered()) { - $vars->setField('GoogleMapKey', $this->googleMapKey); - $vars->setField('GoogleMapLang', $this->lang); - } - - // HTML component of the map - $this->content = $this->processTemplateHTML('Map', $vars); - } - - /** - * @param string $templateName - * @param ArrayData $templateVariables - */ - public function processTemplateHTML($templateName, $templateVariables = null) { - if (!$templateVariables) { - $templateVariables = new ArrayList(); - } - $mappingService = Config::inst()->get('Mappable', 'mapping_service'); - $result = $templateVariables->renderWith($templateName.$mappingService.'HTML'); - return $result; - } + /** Hide the marker by default **/ + protected $defaultHideMarker = false; + + /** Extra content (marker, etc...) **/ + protected $contentMarker = ''; + + // a list of markers, markers being associative arrays + protected $markers = array(); + + /** Use clusterer to display a lot of markers on the gmap **/ + protected $useClusterer = false; + protected $gridSize = 50; + protected $maxZoom = 17; + protected $clustererLibraryPath = '/mappable/javascript/google/markerclusterer.js'; + + /** Enable automatic center/zoom **/ + protected $enableAutomaticCenterZoom = false; + + /** maximum longitude of all markers **/ + protected $maxLng = -1000000; + + /** minimum longitude of all markers **/ + protected $minLng = 1000000; + + /** max latitude of all markers **/ + protected $maxLat = -1000000; + + /** min latitude of all markers **/ + protected $minLat = 1000000; + + /** map center latitude (horizontal), calculated automatically as markers + are added to the map **/ + protected $centerLat = null; + + /** map center longitude (vertical), calculated automatically as markers + are added to the map **/ + protected $centerLng = null; + + /** factor by which to fudge the boundaries so that when we zoom encompass, + the markers aren't too close to the edge **/ + protected $coordCoef = 0.01; + + /* set this to true to render button to maximize / minimize a map */ + protected $allowFullScreen = null; + + /** + * Class constructor. + * + * @param string $googleMapKey the googleMapKey + */ + public function __construct($googleMapKey = '') + { + $this->googleMapKey = $googleMapKey; + } + + public function setShowInlineMapDivStyle($new_show_inline_map_div_style) + { + $this->show_inline_map_div_style = $new_show_inline_map_div_style; + + return $this; + } + + public function setAdditionalCSSClasses($new_additional_css_classes) + { + $this->additional_css_classes = $new_additional_css_classes; + + return $this; + } + + public function setMapStyle($newStyles) + { + $this->jsonMapStyles = $newStyles; + + return $this; + } + + /** + * Set the useClusterer parameter (optimization to display a lot of marker). + * + * @param bool $useClusterer use cluster or not + * @param int $gridSize grid size + * @param int $maxZoom max zoom to cluster at + * + * * @return MapAPI This same object, in order to enable chaining of methods + */ + public function setClusterer($useClusterer, $gridSize = 50, $maxZoom = 17, + $clustererLibraryPath = '/mappable/javascript/google/markerclusterer.js') + { + $this->useClusterer = $useClusterer; + $this->gridSize = $gridSize; + $this->maxZoom = $maxZoom; + $this->clustererLibraryPath = $clustererLibraryPath; + + return $this; + } + + /** + * Set the ID of the default gmap DIV. + * + * @param string $googleMapId the google div ID + * + * @return MapAPI This same object, in order to enable chaining of methods + */ + public function setDivId($googleMapId) + { + $this->googleMapId = $googleMapId; + + return $this; + } + + /** + * Set the size of the gmap. If these values are not provided + * then CSS is used instead. + * + * @param int $width GoogleMap width + * @param int $height GoogleMap height + * + * @return MapAPI This same object, in order to enable chaining of methods + */ + public function setSize($width, $height) + { + $this->width = $width; + $this->height = $height; + + return $this; + } + + /** + * Set the lang of the gmap. + * + * @param string $lang GoogleMap lang : fr,en,.. + * + * @return MapAPI This same object, in order to enable chaining of methods + */ + public function setLang($lang) + { + $this->lang = $lang; + + return $this; + } + + /** + * Set the zoom of the gmap. + * + * @param int $zoom GoogleMap zoom. + * + * @return MapAPI This same object, in order to enable chaining of methods + */ + public function setZoom($zoom) + { + $this->zoom = $zoom; + + return $this; + } + + /** + * Set the zoom of the infowindow. + * + * @param int $infoWindowZoom GoogleMap information window zoom. + * + * @return MapAPI This same object, in order to enable chaining of methods + */ + public function setInfoWindowZoom($infoWindowZoom) + { + $this->infoWindowZoom = $infoWindowZoom; + + return $this; + } + + /** + * Enable the zoom on the marker when you click on it. + * + * @param bool $enableWindowZoom info window enabled zoom. + * + * @return MapAPI This same object, in order to enable chaining of methods + */ + public function setEnableWindowZoom($enableWindowZoom) + { + $this->enableWindowZoom = $enableWindowZoom; + + return $this; + } + + /** + * Enable theautomatic center/zoom at the gmap load. + * + * @param bool $enableAutomaticCenterZoom enable automatic centre zoom + * + * @return MapAPI This same object, in order to enable chaining of methods + */ + public function setEnableAutomaticCenterZoom($enableAutomaticCenterZoom) + { + $this->enableAutomaticCenterZoom = $enableAutomaticCenterZoom; + + return $this; + } + + /** + * Set the center of the gmap (an address). + * + * @param string $center GoogleMap center (an address) + * + * @return MapAPI This same object, in order to enable chaining of methods + */ + public function setCenter($center) + { + $this->center = $center; + + return $this; + } + + /** + * Set the type of the gmap. Also takes into account legacy settings. + * + * FIXME - allow other valid settings in config for map type + * + * @param string $mapType Can be one of road,satellite,hybrid or terrain. Defaults to road + * + * @return MapAPI This same object, in order to enable chaining of methods + */ + public function setMapType($mapType) + { + $this->mapType = $mapType; + + // deal with legacy values for backwards compatbility + switch ($mapType) { + case 'google.maps.MapTypeId.SATELLITE': + $this->mapType = 'satellite'; + break; + case 'google.maps.MapTypeId.G_HYBRID_MAP': + $this->mapType = 'hybrid'; + break; + case 'google.maps.MapTypeId.G_PHYSICAL_MAP': + $this->mapType = 'terrain'; + break; + case 'google.maps.MapTypeId.ROADMAP': + $this->mapType = 'road'; + break; + } + + return $this; + } + + /* + Set whether or not to allow the full screen tools + @return MapAPI This same object, in order to enable chaining of methods + */ + public function setAllowFullScreen($allowed) + { + $this->allowFullScreen = $allowed; + + return $this; + } + + /** + * Set the center of the gmap. + * + * @return MapAPI This same object, in order to enable chaining of methods + **/ + public function setLatLongCenter($center) + { + // error check, we want an associative array with lat,lng keys numeric + + if (!is_array($center)) { + throw new InvalidArgumentException('Center must be an associative array containing lat,lng'); + } + + $keys = array_keys($center); + sort($keys); + if (implode(',', $keys) != 'lat,lng') { + throw new InvalidArgumentException('Keys provided must be lat, lng'); + } + + $this->latLongCenter = $center; + + return $this; + } + + /** + * Set the defaultHideMarker. + * + * @param bool $defaultHideMarker hide all the markers on the map by default + * + * @return MapAPI + */ + public function setDefaultHideMarker($defaultHideMarker) + { + $this->defaultHideMarker = $defaultHideMarker; + + return $this; + } + + /** + * Get the google map content. + * + * @return string the google map html code + */ + public function getGoogleMap() + { + return $this->content; + } + + /** + * Get URL content using cURL. + * + * @param string $url the url + * + * @return string the html code + * + * @todo add proxy settings + */ + public function getContent($url) + { + $curl = curl_init(); + curl_setopt($curl, CURLOPT_TIMEOUT, 10); + curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_URL, $url); + $data = curl_exec($curl); + curl_close($curl); + + return $data; + } + + /** + * Geocoding an address (address -> lat,lng). + * + * @param string $address an address + * + * @return string array with precision, lat & lng + */ + public function geocoding($address) + { + $geocoder = new MappableGoogleGeocoder(); + $locations = $geocoder->getLocations($address); + $result = null; + if (!empty($locations)) { + $place = $locations[0]; + $location = $place['geometry']['location']; + $result = array( + 'lat' => $location['lat'], + 'lon' => $location['lng'], + 'geocoded' => true, + ); + } else { + $result = array(); // no results + } + + return $result; + } + + /** + * Add marker by his coord. + * + * @param string $lat lat + * @param string $lng lngs + * @param string $html html code display in the info window + * @param string $category marker category + * @param string $icon an icon url + * + * @return MapAPI + */ + public function addMarkerByCoords($lat, $lng, $html = '', $category = '', $icon = '') + { + $m = array( + 'latitude' => $lat, + 'longitude' => $lng, + 'html' => $html, + 'category' => $category, + 'icon' => $icon, + ); + array_push($this->markers, $m); + + return $this; + } + + /** + * Add marker by his address. + * + * @param string $address an ddress + * @param string $content html code display in the info window + * @param string $category marker category + * @param string $icon an icon url + * + * @return MapAPI + */ + public function addMarkerByAddress($address, $content = '', $category = '', $icon = '') + { + $point = $this->geocoding($address); + if ($point !== null) { + $this->addMarkerByCoords($point['lat'], $point['lon'], $content, $category, $icon); + } + + return $this; + } + + /** + * Add marker by an array of coord. + * + * @param array $coordtab an array of lat,lng,content + * @param string $category marker category + * @param string $icon an icon url + * + * @return MapAPI + */ + public function addArrayMarkerByCoords($coordtab, $category = '', $icon = '') + { + foreach ($coordtab as $coord) { + $this->addMarkerByCoords($coord[0], $coord[1], $coord[2], $category, $icon); + } + + return $this; + } + + /** + * Adds a {@link ViewableData} object that implements {@link Mappable} + * to the map. + * + * @param $infowindowtemplateparams Optional array of extra parameters to pass to the map info window + * @param ViewableData $obj + */ + public function addMarkerAsObject(ViewableData $obj, $infowindowtemplateparams = null) + { + $extensionsImplementMappable = false; + $extensions = Object::get_extensions(get_class($obj)); + if (is_array($extensions)) { + foreach ($extensions as $extension) { + $class = new ReflectionClass($extension); + if ($class->implementsInterface('Mappable')) { + $extensionsImplementMappable = true; + } + } + } + + if ($extensionsImplementMappable || + ($obj instanceof Mappable) || + (Object::has_extension($obj->ClassName, 'MapExtension')) + ) { + $cat = $obj->hasMethod('getMappableMapCategory') ? $obj->getMappableMapCategory() : 'default'; + if ($infowindowtemplateparams !== null) { + foreach ($infowindowtemplateparams as $key => $value) { + $obj->{$key} = $value; + } + } + $this->addMarkerByCoords( + $obj->getMappableLatitude(), + $obj->getMappableLongitude(), + $obj->getMappableMapContent(), + $cat, + $obj->getMappableMapPin() + ); + } + + return $this; + } + + /** + * Draws a line between two {@link ViewableData} objects. + * + * @param ViewableData $one The first point + * @param ViewableData $two The second point + * @param string $color The hexidecimal color of the line + */ + public function connectPoints(ViewableData $one, ViewableData $two, $color = '#FF3300') + { + $this->addLine( + array($one->getMappableLatitude(), $one->getMappableLongitude()), + array($two->getMappableLatitude(), $two->getMappableLongitude()), + $color + ); + } + + public function forTemplate() + { + $this->generate(); + MapUtil::set_map_already_rendered(true); + + return $this->getGoogleMap(); + } + + /** + * Add a KML file which will be rendered on this map. Normally used for likes + * of GPS traces from activities. + * + * @param string $url url of the kml file compatible with gmap and gearth + * + * @return MapAPI + */ + public function addKML($url) + { + array_push($this->kmlFiles, $url); + + return $this; + } + + /* + Add a line to the map + + */ + public function addLine($from = array(), $to = array(), $color = '#FF3300') + { + $line = array( + 'lat1' => $from[0], + 'lon1' => $from[1], + 'lat2' => $to[0], + 'lon2' => $to[1], + 'color' => $color, + ); + + array_push($this->lines, $line); + + return $this; + } + + /* + For php 5.3 + */ + public static function jsonRemoveUnicodeSequences($struct) + { + return preg_replace('/\\\\u([a-f0-9]{4})/e', + "iconv('UCS-4LE','UTF-8',pack('V', hexdec('U$1')))", + json_encode($struct)); + } + + /** + * Generate the gmap. + */ + public function generate() + { + // from http://stackoverflow.com/questions/3586401/cant-decode-json-string-in-php + $jsonMarkers = null; + $linesJson = null; + $kmlJson = null; + + // prior to PHP version 5.4, one needs to use regex + if (PHP_VERSION_ID < 50400) { + $jsonMarkers = stripslashes(self::jsonRemoveUnicodeSequences($this->markers)); + $linesJson = stripslashes(self::jsonRemoveUnicodeSequences($this->lines)); + $kmlJson = stripslashes(self::jsonRemoveUnicodeSequences($this->kmlFiles)); + } else { + $jsonMarkers = stripslashes(json_encode($this->markers, JSON_UNESCAPED_UNICODE)); + $linesJson = stripslashes(json_encode($this->lines, JSON_UNESCAPED_UNICODE)); + $kmlJson = stripslashes(json_encode($this->kmlFiles, JSON_UNESCAPED_UNICODE)); + } + + // Center of the GMap - text centre takes precedence + $geocodeCentre = ($this->latLongCenter) ? + $this->latLongCenter : $this->geocoding($this->center); + + $latlngCentre = null; + // coordinates for centre depending on which method used + if (isset($geocodeCentre['geocoded'])) { + $latlngCentre = array( + 'lat' => $geocodeCentre['lat'], + 'lng' => $geocodeCentre['lon'], + ); + } elseif (is_array($this->latLongCenter)) { + $latlngCentre = $this->latLongCenter; + } + + $this->LatLngCentreJSON = stripslashes(json_encode($latlngCentre)); + + $lenLng = $this->maxLng - $this->minLng; + $lenLat = $this->maxLat - $this->minLat; + $this->minLng -= $lenLng * $this->coordCoef; + $this->maxLng += $lenLng * $this->coordCoef; + $this->minLat -= $lenLat * $this->coordCoef; + $this->maxLat += $lenLat * $this->coordCoef; + + // add the css class mappable as a handle onto the map styling + $this->additional_css_classes .= ' mappable'; + + if (!$this->enableAutomaticCenterZoom) { + $this->enableAutomaticCenterZoom = 'false'; + } + + if (!$this->useClusterer) { + $this->useClusterer = 'false'; + } + + if (!$this->defaultHideMarker) { + $this->defaultHideMarker = 'false'; + } + + // initialise full screen as the config value if not already set + if ($this->allowFullScreen === null) { + $this->allowFullScreen = Config::inst()->get('Mappable', 'allow_full_screen'); + } + + if (!$this->allowFullScreen) { + $this->allowFullScreen = 'false'; + } + + if (!$this->enableWindowZoom) { + $this->enableWindowZoom = 'false'; + } + + $vars = new ArrayData(array( + + 'JsonMapStyles' => $this->jsonMapStyles, + 'AdditionalCssClasses' => $this->additional_css_classes, + 'Width' => $this->width, + 'Height' => $this->height, + 'ShowInlineMapDivStyle' => $this->show_inline_map_div_style, + 'InfoWindowZoom' => $this->infoWindowZoom, + 'EnableWindowZoom' => $this->enableWindowZoom, + 'MapMarkers' => $jsonMarkers, + 'DefaultHideMarker' => $this->defaultHideMarker, + 'LatLngCentre' => $this->LatLngCentreJSON, + 'EnableAutomaticCenterZoom' => $this->enableAutomaticCenterZoom, + 'Zoom' => $this->zoom, + 'MaxZoom' => $this->maxZoom, + 'GridSize' => $this->gridSize, + 'MapType' => $this->mapType, + 'GoogleMapID' => $this->googleMapId, + 'Lang' => $this->lang, + 'UseClusterer' => $this->useClusterer, + 'ClustererLibraryPath' => $this->clustererLibraryPath, + 'ClustererMaxZoom' => $this->maxZoom, + 'ClustererGridSize' => $this->gridSize, + 'Lines' => $linesJson, + 'KmlFiles' => $kmlJson, + 'AllowFullScreen' => $this->allowFullScreen, + 'UseCompressedAssets' => Config::inst()->get('Mappable', 'use_compressed_assets'), + ) + ); + + if (!MapUtil::get_map_already_rendered()) { + $vars->setField('GoogleMapKey', $this->googleMapKey); + $vars->setField('GoogleMapLang', $this->lang); + } + + // HTML component of the map + $this->content = $this->processTemplateHTML('Map', $vars); + } + + /** + * @param string $templateName + * @param ArrayData $templateVariables + */ + public function processTemplateHTML($templateName, $templateVariables = null) + { + if (!$templateVariables) { + $templateVariables = new ArrayList(); + } + $mappingService = Config::inst()->get('Mappable', 'mapping_service'); + $result = $templateVariables->renderWith($templateName.$mappingService.'HTML'); + + return $result; + } } diff --git a/code/MapExtension.php b/code/MapExtension.php index 7f438a9..4920bff 100644 --- a/code/MapExtension.php +++ b/code/MapExtension.php @@ -1,226 +1,230 @@ 'Decimal(18,15)', - 'Lon' => 'Decimal(18,15)', - 'ZoomLevel' => 'Int', - 'MapPinEdited' => 'Boolean' - ); - - static $has_one = array( - 'MapPinIcon' => 'Image' - ); - - static $defaults = array ( - 'Lat' =>0, - 'Lon' => 0, - 'Zoom' => 4, - 'MapPinEdited' => false - ); - - /* - Map editing field - */ - private $mapField = null; - - - /* - Add a Location tab containing the map - */ - public function updateCMSFields(FieldList $fields) { - // These fields need removed, as they may have already been created by the form scaffolding - $fields->removeByName('Lat'); - $fields->removeByName('Lon'); - $fields->removeByName('ZoomLevel'); - $fields->removeByName('MapPinIcon'); - $fields->removeByName('MapPinEdited'); - - $fields->addFieldToTab("Root.Location", - $this->getMapField() - ); - - $fields->addFieldToTab('Root.Location', $uf = new UploadField('MapPinIcon', - _t('Mappable.MAP_PIN', 'Map Pin Icon. Leave this blank for default pin to show'))); - $uf->setFolderName('mapicons'); - } - - - public function getMappableLatitude() { - return $this->owner->Lat; - } - - public function getMappableLongitude() { - return $this->owner->Lon; - } - - - /** - * Renders the map info window for the DataObject. - * - * Be sure to define a template for that, named by the decorated class suffixed with _MapInfoWindow - * e.g. MyPage_MapInfoWindow - * - * You can change the suffix globally by editing the MapExtension.map_info_window_suffix config val - * - * @return string - */ - public function getMappableMapContent() { - $defaultTemplate = 'MapInfoWindow'; - $classTemplate = - SSViewer::get_templates_by_class( - $this->owner->ClassName, - Config::inst()->get('MapExtension', 'map_info_window_suffix') - ); - - $template = count($classTemplate) ? $classTemplate : $defaultTemplate; - return MapUtil::sanitize($this->owner->renderWith($template)); - } - - - /* - If the marker pin is not at position 0,0 mark the pin as edited. This provides the option of - filtering out (0,0) point which is often irrelevant for plots - */ - public function onBeforeWrite() { - $latzero = ($this->owner->Lat == 0); - $lonzero = ($this->owner->Lon == 0); - $latlonzero = $latzero && $lonzero; - - // if both latitude and longitude still default, do not set the map location as edited - if (!$latlonzero) { - $this->owner->MapPinEdited = true; - } - } - - - /* - If a user has uploaded a map pin icon display that, otherwise - */ - public function getMappableMapPin() { - $result = false; - if ($this->owner->MapPinIconID != 0) { - $mapPin = $this->owner->MapPinIcon(); - $result = $mapPin->getAbsoluteURL(); - } else { - // check for a cached map pin already having been provided for the layer - if ($this->owner->CachedMapPinURL) { - $result = $this->owner->CachedMapPinURL; - } - } - return $result; - } - - - /* - Check for non zero coordinates, on the assumption that (0,0) will never be the desired coordinates - */ - public function HasGeo() { - $isOrigin = ($this->owner->Lat == 0) && ($this->owner->Lon == 0); - $result = !$isOrigin; - if ($this->owner->hasExtension('MapLayerExtension')) { - if ($this->owner->MapLayers()->count() > 0) { - $result = true; - } - } - - - $this->owner->extend('updateHasGeo', $result); - /** - * FIXME - move this to PointsOfInterest module - if ($this->owner->hasExtension('PointsOfInterestLayerExtension')) { - if ($this->owner->PointsOfInterestLayers()->count() > 0) { - $result = true; - } - } - */ - - return $result; - } - - - /* - Render a map at the provided lat,lon, zoom from the editing functions, - */ - public function BasicMap() { - $map = $this->owner->getRenderableMap()-> - setZoom($this->owner->ZoomLevel)-> - setAdditionalCSSClasses('fullWidthMap')-> - setShowInlineMapDivStyle(true); - - $autozoom = false; - - // add any KML map layers - if (Object::has_extension($this->owner->ClassName, 'MapLayerExtension')) { - foreach($this->owner->MapLayers() as $layer) { - $map->addKML($layer->KmlFile()->getAbsoluteURL()); - // we have a layer, so turn on autozoom - $autozoom = true; - } - $map->setEnableAutomaticCenterZoom(true); - } - - $this->owner->extend('updateBasicMap', $map); - - /** - FIXME - move to POI module - if (Object::has_extension($this->owner->ClassName, 'PointsOfInterestLayerExtension')) { - foreach($this->owner->PointsOfInterestLayers() as $layer) { - $layericon = $layer->DefaultIcon(); - if ($layericon->ID === 0) { - $layericon = null; - } - foreach ($layer->PointsOfInterest() as $poi) { - if ($poi->MapPinEdited) { - if ($poi->MapPinIconID == 0 && $layericon) { - $poi->CachedMapPinURL = $layericon->getAbsoluteURL(); - } - $map->addMarkerAsObject($poi); - - // we have a point of interest, so turn on auto zoom - $autozoom = true; - } - } - } - $map->setClusterer(true); - } - - **/ - - $map->setEnableAutomaticCenterZoom($autozoom); - $map->setShowInlineMapDivStyle(true); - - return $map; - } - - - /** - * Access the map editing field for the purpose of adding guide points - * @return [LatLongField] instance of location editing field - */ - public function getMapField() { - if (!isset($this->mapField)) { - $this->mapField = new LatLongField(array( - new TextField('Lat', 'Latitude'), - new TextField('Lon', 'Longitude'), - new TextField('ZoomLevel', 'Zoom') - ) - ); - } - return $this->mapField; - } - - - /** - * Template helper, used to decide whether or not to use compressed assets - */ - public function UseCompressedAssets() { - return Config::inst()->get('Mappable', 'use_compressed_assets'); - } +class MapExtension extends DataExtension implements Mappable +{ + /* + * Template suffix for rendering MapInfoWindow aka map bubble + */ + private static $map_info_window_suffix = '_MapInfoWindow'; + + private static $db = array( + 'Lat' => 'Decimal(18,15)', + 'Lon' => 'Decimal(18,15)', + 'ZoomLevel' => 'Int', + 'MapPinEdited' => 'Boolean', + ); + + public static $has_one = array( + 'MapPinIcon' => 'Image', + ); + + public static $defaults = array( + 'Lat' => 0, + 'Lon' => 0, + 'Zoom' => 4, + 'MapPinEdited' => false, + ); + + /* + Map editing field + */ + private $mapField = null; + + /* + Add a Location tab containing the map + */ + public function updateCMSFields(FieldList $fields) + { + // These fields need removed, as they may have already been created by the form scaffolding + $fields->removeByName('Lat'); + $fields->removeByName('Lon'); + $fields->removeByName('ZoomLevel'); + $fields->removeByName('MapPinIcon'); + $fields->removeByName('MapPinEdited'); + + $fields->addFieldToTab('Root.Location', + $this->getMapField() + ); + + $fields->addFieldToTab('Root.Location', $uf = new UploadField('MapPinIcon', + _t('Mappable.MAP_PIN', 'Map Pin Icon. Leave this blank for default pin to show'))); + $uf->setFolderName('mapicons'); + } + + public function getMappableLatitude() + { + return $this->owner->Lat; + } + + public function getMappableLongitude() + { + return $this->owner->Lon; + } + + /** + * Renders the map info window for the DataObject. + * + * Be sure to define a template for that, named by the decorated class suffixed with _MapInfoWindow + * e.g. MyPage_MapInfoWindow + * + * You can change the suffix globally by editing the MapExtension.map_info_window_suffix config val + * + * @return string + */ + public function getMappableMapContent() + { + $defaultTemplate = 'MapInfoWindow'; + $classTemplate = + SSViewer::get_templates_by_class( + $this->owner->ClassName, + Config::inst()->get('MapExtension', 'map_info_window_suffix') + ); + + $template = count($classTemplate) ? $classTemplate : $defaultTemplate; + + return MapUtil::sanitize($this->owner->renderWith($template)); + } + + /* + If the marker pin is not at position 0,0 mark the pin as edited. This provides the option of + filtering out (0,0) point which is often irrelevant for plots + */ + public function onBeforeWrite() + { + $latzero = ($this->owner->Lat == 0); + $lonzero = ($this->owner->Lon == 0); + $latlonzero = $latzero && $lonzero; + + // if both latitude and longitude still default, do not set the map location as edited + if (!$latlonzero) { + $this->owner->MapPinEdited = true; + } + } + + /* + If a user has uploaded a map pin icon display that, otherwise + */ + public function getMappableMapPin() + { + $result = false; + if ($this->owner->MapPinIconID != 0) { + $mapPin = $this->owner->MapPinIcon(); + $result = $mapPin->getAbsoluteURL(); + } else { + // check for a cached map pin already having been provided for the layer + if ($this->owner->CachedMapPinURL) { + $result = $this->owner->CachedMapPinURL; + } + } + + return $result; + } + + /* + Check for non zero coordinates, on the assumption that (0,0) will never be the desired coordinates + */ + public function HasGeo() + { + $isOrigin = ($this->owner->Lat == 0) && ($this->owner->Lon == 0); + $result = !$isOrigin; + if ($this->owner->hasExtension('MapLayerExtension')) { + if ($this->owner->MapLayers()->count() > 0) { + $result = true; + } + } + + $this->owner->extend('updateHasGeo', $result); + /* + * FIXME - move this to PointsOfInterest module + if ($this->owner->hasExtension('PointsOfInterestLayerExtension')) { + if ($this->owner->PointsOfInterestLayers()->count() > 0) { + $result = true; + } + } + */ + + return $result; + } + + /* + Render a map at the provided lat,lon, zoom from the editing functions, + */ + public function BasicMap() + { + $map = $this->owner->getRenderableMap()-> + setZoom($this->owner->ZoomLevel)-> + setAdditionalCSSClasses('fullWidthMap')-> + setShowInlineMapDivStyle(true); + + $autozoom = false; + + // add any KML map layers + if (Object::has_extension($this->owner->ClassName, 'MapLayerExtension')) { + foreach ($this->owner->MapLayers() as $layer) { + $map->addKML($layer->KmlFile()->getAbsoluteURL()); + // we have a layer, so turn on autozoom + $autozoom = true; + } + $map->setEnableAutomaticCenterZoom(true); + } + + $this->owner->extend('updateBasicMap', $map); + + /* + FIXME - move to POI module + if (Object::has_extension($this->owner->ClassName, 'PointsOfInterestLayerExtension')) { + foreach($this->owner->PointsOfInterestLayers() as $layer) { + $layericon = $layer->DefaultIcon(); + if ($layericon->ID === 0) { + $layericon = null; + } + foreach ($layer->PointsOfInterest() as $poi) { + if ($poi->MapPinEdited) { + if ($poi->MapPinIconID == 0 && $layericon) { + $poi->CachedMapPinURL = $layericon->getAbsoluteURL(); + } + $map->addMarkerAsObject($poi); + + // we have a point of interest, so turn on auto zoom + $autozoom = true; + } + } + } + $map->setClusterer(true); + } + + **/ + + $map->setEnableAutomaticCenterZoom($autozoom); + $map->setShowInlineMapDivStyle(true); + + return $map; + } + + /** + * Access the map editing field for the purpose of adding guide points. + * + * @return [LatLongField] instance of location editing field + */ + public function getMapField() + { + if (!isset($this->mapField)) { + $this->mapField = new LatLongField(array( + new TextField('Lat', 'Latitude'), + new TextField('Lon', 'Longitude'), + new TextField('ZoomLevel', 'Zoom'), + ) + ); + } + + return $this->mapField; + } + + /** + * Template helper, used to decide whether or not to use compressed assets. + */ + public function UseCompressedAssets() + { + return Config::inst()->get('Mappable', 'use_compressed_assets'); + } } diff --git a/code/MapField.php b/code/MapField.php index ef24f40..000f6fc 100755 --- a/code/MapField.php +++ b/code/MapField.php @@ -1,55 +1,59 @@ to
HTML tag. Default: 2 + */ + protected $headingLevel = 2; + private $divId; - /** - * @var int $headingLevel The level of the

to

HTML tag. Default: 2 - */ - protected $headingLevel = 2; - private $divId; + /** + * @param string $name + * @param string $title + */ + public function __construct($name, $title = null, $headingLevel = 2, $allowHTML = false, $form = null) + { + $this->divId = $name; + // legacy handling for old parameters: $title, $heading, ... + // instead of new handling: $name, $title, $heading, ... + $args = func_get_args(); + if (!isset($args[1]) || is_numeric($args[1])) { + $title = (isset($args[0])) ? $args[0] : null; + // Use "HeaderField(title)" as the default field name for a HeaderField; + // if it's just set to title then we risk causing accidental duplicate-field creation. - /** - * @param string $name - * @param string $title - */ - function __construct($name, $title = null, $headingLevel = 2, $allowHTML = false, $form = null) { - $this->divId = $name; - // legacy handling for old parameters: $title, $heading, ... - // instead of new handling: $name, $title, $heading, ... - $args = func_get_args(); - if(!isset($args[1]) || is_numeric($args[1])) { - $title = (isset($args[0])) ? $args[0] : null; - // Use "HeaderField(title)" as the default field name for a HeaderField; - // if it's just set to title then we risk causing accidental duplicate-field creation. + // this means i18nized fields won't be easily accessible through fieldByName() + $name = 'MapField'.$title; + $headingLevel = (isset($args[1])) ? $args[1] : null; + $allowHTML = (isset($args[2])) ? $args[2] : null; + $form = (isset($args[3])) ? $args[3] : null; + } - // this means i18nized fields won't be easily accessible through fieldByName() - $name = 'MapField' . $title; - $headingLevel = (isset($args[1])) ? $args[1] : null; - $allowHTML = (isset($args[2])) ? $args[2] : null; - $form = (isset($args[3])) ? $args[3] : null; - } + if ($headingLevel) { + $this->headingLevel = $headingLevel; + } + $this->allowHTML = $allowHTML; + parent::__construct($name, $title, null, $allowHTML, $form); + } - if($headingLevel) $this->headingLevel = $headingLevel; - $this->allowHTML = $allowHTML; - parent::__construct($name, $title, null, $allowHTML, $form); - } + public function Field($properties = array()) + { + Requirements::javascript('framework/thirdparty/jquery/jquery.js'); + Requirements::javascript('framework/thirdparty/jquery-livequery/jquery.livequery.js'); + $attributes = array( + 'class' => 'middleColumn', + 'id' => $this->divId, + 'style' => 'width:100%;height:300px;margin:5px 0px 5px 5px;position:relative;', + ); - function Field($properties = array()) { - Requirements::javascript('framework/thirdparty/jquery/jquery.js'); - Requirements::javascript('framework/thirdparty/jquery-livequery/jquery.livequery.js'); - $attributes = array( - 'class' => 'middleColumn', - 'id' => $this->divId, - 'style' => "width:100%;height:300px;margin:5px 0px 5px 5px;position:relative;" - ); + Requirements::css('mappable/css/mapField.css'); - Requirements::css('mappable/css/mapField.css'); - - return '
' . $this->createTag( - "div", - $attributes - ) . '
'; - } + return '
'.$this->createTag( + 'div', + $attributes + ).'
'; + } } diff --git a/code/MapLayer.php b/code/MapLayer.php index 0d59261..5efa59e 100644 --- a/code/MapLayer.php +++ b/code/MapLayer.php @@ -1,14 +1,12 @@ 'Varchar(255)' - ); - - - static $has_one = array( - 'KmlFile' => 'File' - ); - +class MapLayer extends DataObject +{ + public static $db = array( + 'Title' => 'Varchar(255)', + ); + + public static $has_one = array( + 'KmlFile' => 'File', + ); } diff --git a/code/MapLayerExtension.php b/code/MapLayerExtension.php index 9a365d7..1a3511b 100644 --- a/code/MapLayerExtension.php +++ b/code/MapLayerExtension.php @@ -1,29 +1,30 @@ 'MapLayer', + ); - static $many_many = array( - 'MapLayers' => 'MapLayer' - ); + public static $belongs_many_many_extraFields = array( + 'MapLayers' => array( + 'SortOrder' => 'Int', + ), + ); - static $belongs_many_many_extraFields = array( - 'MapLayers' => array( - 'SortOrder' => "Int" - ) - ); - - public function updateCMSFields(FieldList $fields) { - $gridConfig2 = GridFieldConfig_RelationEditor::create(); - $gridConfig2->getComponentByType( - 'GridFieldAddExistingAutocompleter')-> - setSearchFields(array('Title') - ); - $gridConfig2->getComponentByType('GridFieldPaginator')->setItemsPerPage(100); - $gridField2 = new GridField("Map Layers", - "Map Layers:", - $this->owner->MapLayers(), - $gridConfig2 - ); - $fields->addFieldToTab("Root.MapLayers", $gridField2); - } + public function updateCMSFields(FieldList $fields) + { + $gridConfig2 = GridFieldConfig_RelationEditor::create(); + $gridConfig2->getComponentByType( + 'GridFieldAddExistingAutocompleter')-> + setSearchFields(array('Title') + ); + $gridConfig2->getComponentByType('GridFieldPaginator')->setItemsPerPage(100); + $gridField2 = new GridField('Map Layers', + 'Map Layers:', + $this->owner->MapLayers(), + $gridConfig2 + ); + $fields->addFieldToTab('Root.MapLayers', $gridField2); + } } diff --git a/code/MapMarkerSetsExtension.php b/code/MapMarkerSetsExtension.php index 4044edb..8a9b827 100644 --- a/code/MapMarkerSetsExtension.php +++ b/code/MapMarkerSetsExtension.php @@ -1,32 +1,30 @@ 'MapMarkerSet' - ); - - - static $belongs_many_many_extraFields = array( - 'MapMarkerSets' => array( - 'SortOrder' => "Int" - ) - ); - - - public function updateCMSFields(FieldList $fields) { - $gridConfig2 = GridFieldConfig_RelationEditor::create(); - $gridConfig2->getComponentByType( - 'GridFieldAddExistingAutocompleter')->setSearchFields(array('Title') - ); - $gridConfig2->getComponentByType('GridFieldPaginator')->setItemsPerPage(100); - - $gridField2 = new GridField("MapMarkerSets", - "MapMarker Sets", - $this->owner->MapMarkerSets(), - $gridConfig2 - ); - $fields->addFieldToTab("Root.MapMarkerSets", $gridField2); - } - +class MapMarkerSetsExtension extends DataExtension +{ + public static $many_many = array( + 'MapMarkerSets' => 'MapMarkerSet', + ); + + public static $belongs_many_many_extraFields = array( + 'MapMarkerSets' => array( + 'SortOrder' => 'Int', + ), + ); + + public function updateCMSFields(FieldList $fields) + { + $gridConfig2 = GridFieldConfig_RelationEditor::create(); + $gridConfig2->getComponentByType( + 'GridFieldAddExistingAutocompleter')->setSearchFields(array('Title') + ); + $gridConfig2->getComponentByType('GridFieldPaginator')->setItemsPerPage(100); + + $gridField2 = new GridField('MapMarkerSets', + 'MapMarker Sets', + $this->owner->MapMarkerSets(), + $gridConfig2 + ); + $fields->addFieldToTab('Root.MapMarkerSets', $gridField2); + } } diff --git a/code/MapUtil.php b/code/MapUtil.php index 51214a9..6beeb2d 100644 --- a/code/MapUtil.php +++ b/code/MapUtil.php @@ -2,222 +2,228 @@ class MapUtil { - - /** - * @var string The Google Maps API key - */ - protected static $api_key; - - /** - * @var int Number of active {@see GoogleMapsAPI} instances (for the HTML ID) - */ - protected static $instances = 0; - - /** - * @var int The default width of a Google Map - */ - public static $map_width = '100%'; - - /** - * @var int The default height of a Google Map - */ - public static $map_height = '400px'; - - /** - * @var int Prefix for the div ID of the map - */ - public static $div_id = "google_map"; - - /** - * @var boolean Automatic center/zoom for the map - */ - public static $automatic_center = true; - - /** - * @var boolean Show the marker fields on the map - */ - public static $hide_marker = false; - - /** - * @var boolean Show the marker fields on the map - */ - public static $map_type = 'google.maps.MapTypeId.ROADMAP'; - - /** - * @var string $center Center of map (adress) - */ - public static $center = 'Paris, France'; - - /* Signals whether at least one map has already been rendered */ - private static $map_already_rendered = false; - - /* Whether or not to allow full screen */ - private static $allow_full_screen = null; - - - public static function reset() { - self::$api_key = null; - self::$instances = 0; - self::$map_width = '100%'; - self::$map_height = '400px'; - self::$div_id = "google_map"; - self::$automatic_center = true; - self::$hide_marker = false; - self::$map_type = 'google.maps.MapTypeId.ROADMAP'; - self::$center = 'Paris, France'; - self::$map_already_rendered = false; - self::$allow_full_screen = null; - Config::inst()->update('Mappable', 'language', 'en'); - } - - /** - * Set the API key for Google Maps - * - * @param string $key - */ - public static function set_api_key($key) { - self::$api_key = $key; - } - - /** - * @param boolean $new_map_already_rendered - */ - public static function set_map_already_rendered($new_map_already_rendered) { - self::$map_already_rendered = $new_map_already_rendered; - } - - public static function get_map_already_rendered() { - return self::$map_already_rendered; - } - - /** - * Set the default size of the map - * - * @param int $width - * @param int $height - */ - public static function set_map_size($width, $height) { - self:: $map_width = $width; - self::$map_height = $height; - } - - /** - * FIXME - NOT USED? - * Set the type of the gmap - * - * @param string $mapType (can be 'google.maps.MapTypeId.ROADMAP', 'G_SATELLITE_MAP', - * 'G_HYBRID_MAP', 'G_PHYSICAL_MAP') - * - * @return void - */ - public static function set_map_type($mapType) { - self::$map_type = $mapType; - } - - /** - * Set the center of the gmap (an address, using text geocoder query) - * - * @param string $center GoogleMap center (an address) - * - * @return void - */ - public static function set_center($center) - { - self::$center = $center; - } - - /** - * Get a new GoogleMapAPI object and load it with the default settings - * - * @return MapAPI - */ - public static function instance() - { - self::$instances++; - - if (self::$allow_full_screen == null) { - self::$allow_full_screen = Config::inst()->get('Mappable', 'allow_full_screen'); - } - - $url = Director::absoluteBaseURL(); - - // remove http and https - $url = str_replace('http://', '', $url); - $url = str_replace('https://', '', $url); - $parts = explode('/', $url); - $host = $parts[0]; - - $key = self::$api_key; - - // if an array, get the key by an array keyed by host - if (is_array($key)) { - $key = $key[$host]; - } - - $gmap = new MapAPI($key); - $gmap->setDivId(self::$div_id."_".self::$instances); - $gmap->setEnableAutomaticCenterZoom(self::$automatic_center); - $gmap->setSize(self::$map_width, self::$map_height); - $gmap->setDefaultHideMarker(self::$hide_marker); - $gmap->setMapType(self::$map_type); - $gmap->setCenter(self::$center); - $gmap->setAllowFullScreen(self::$allow_full_screen); - $language = Config::inst()->get('Mappable', 'language'); - $gmap->setLang($language); - return $gmap; - } - - - /** - * Sanitize a string of HTML content for safe inclusion in the JavaScript - * for a Google Map - * - * @return string - */ - public static function sanitize($content) { - return addslashes(str_replace(array("\n", "\r", "\t"), '', $content)); - } - - - /** - * Creates a new {@link GoogleMapsAPI} object loaded with the default settings - * and places all of the items in a {@link SS_List} - * e.g. {@link DataList} or {@link ArrayList} on the map - * - * @param SS_List list of objects to display on a map - * @param array $infowindowtemplateparams Optional array of extra parameters to pass to the map info window - * @return MapAPI - */ - public static function get_map(SS_List $list, $infowindowtemplateparams) { - $gmap = self::instance(); - if ($list) { - foreach ($list as $mappable) { - if (self::ChooseToAddDataobject($mappable)) - $gmap->addMarkerAsObject($mappable, $infowindowtemplateparams); - } - } - return $gmap; - } - - /** - * Determines if the current DataObject should be included to the map - * Checks if it has Mappable interface implemented - * If it has MapExtension included, the value of MapPinEdited is also checked - * - * @param DataObject $do - * @return bool - */ - private static function ChooseToAddDataobject(DataObject $do) { - $isMappable = $do->is_a('Mappable'); - - foreach ($do->getExtensionInstances() as $extension) { - $isMappable = $isMappable || $extension instanceof Mappable; - } - - $filterMapPinEdited = $do->hasExtension('MapExtension') - ? $do->MapPinEdited - : true; - - return $isMappable && $filterMapPinEdited; - } + /** + * @var string The Google Maps API key + */ + protected static $api_key; + + /** + * @var int Number of active {@see GoogleMapsAPI} instances (for the HTML ID) + */ + protected static $instances = 0; + + /** + * @var int The default width of a Google Map + */ + public static $map_width = '100%'; + + /** + * @var int The default height of a Google Map + */ + public static $map_height = '400px'; + + /** + * @var int Prefix for the div ID of the map + */ + public static $div_id = 'google_map'; + + /** + * @var bool Automatic center/zoom for the map + */ + public static $automatic_center = true; + + /** + * @var bool Show the marker fields on the map + */ + public static $hide_marker = false; + + /** + * @var bool Show the marker fields on the map + */ + public static $map_type = 'google.maps.MapTypeId.ROADMAP'; + + /** + * @var string Center of map (adress) + */ + public static $center = 'Paris, France'; + + /* Signals whether at least one map has already been rendered */ + private static $map_already_rendered = false; + + /* Whether or not to allow full screen */ + private static $allow_full_screen = null; + + public static function reset() + { + self::$api_key = null; + self::$instances = 0; + self::$map_width = '100%'; + self::$map_height = '400px'; + self::$div_id = 'google_map'; + self::$automatic_center = true; + self::$hide_marker = false; + self::$map_type = 'google.maps.MapTypeId.ROADMAP'; + self::$center = 'Paris, France'; + self::$map_already_rendered = false; + self::$allow_full_screen = null; + Config::inst()->update('Mappable', 'language', 'en'); + } + + /** + * Set the API key for Google Maps. + * + * @param string $key + */ + public static function set_api_key($key) + { + self::$api_key = $key; + } + + /** + * @param bool $new_map_already_rendered + */ + public static function set_map_already_rendered($new_map_already_rendered) + { + self::$map_already_rendered = $new_map_already_rendered; + } + + public static function get_map_already_rendered() + { + return self::$map_already_rendered; + } + + /** + * Set the default size of the map. + * + * @param int $width + * @param int $height + */ + public static function set_map_size($width, $height) + { + self:: $map_width = $width; + self::$map_height = $height; + } + + /** + * FIXME - NOT USED? + * Set the type of the gmap. + * + * @param string $mapType (can be 'google.maps.MapTypeId.ROADMAP', 'G_SATELLITE_MAP', + * 'G_HYBRID_MAP', 'G_PHYSICAL_MAP') + */ + public static function set_map_type($mapType) + { + self::$map_type = $mapType; + } + + /** + * Set the center of the gmap (an address, using text geocoder query). + * + * @param string $center GoogleMap center (an address) + */ + public static function set_center($center) + { + self::$center = $center; + } + + /** + * Get a new GoogleMapAPI object and load it with the default settings. + * + * @return MapAPI + */ + public static function instance() + { + ++self::$instances; + + if (self::$allow_full_screen == null) { + self::$allow_full_screen = Config::inst()->get('Mappable', 'allow_full_screen'); + } + + $url = Director::absoluteBaseURL(); + + // remove http and https + $url = str_replace('http://', '', $url); + $url = str_replace('https://', '', $url); + $parts = explode('/', $url); + $host = $parts[0]; + + $key = self::$api_key; + + // if an array, get the key by an array keyed by host + if (is_array($key)) { + $key = $key[$host]; + } + + $gmap = new MapAPI($key); + $gmap->setDivId(self::$div_id.'_'.self::$instances); + $gmap->setEnableAutomaticCenterZoom(self::$automatic_center); + $gmap->setSize(self::$map_width, self::$map_height); + $gmap->setDefaultHideMarker(self::$hide_marker); + $gmap->setMapType(self::$map_type); + $gmap->setCenter(self::$center); + $gmap->setAllowFullScreen(self::$allow_full_screen); + $language = Config::inst()->get('Mappable', 'language'); + $gmap->setLang($language); + + return $gmap; + } + + /** + * Sanitize a string of HTML content for safe inclusion in the JavaScript + * for a Google Map. + * + * @return string + */ + public static function sanitize($content) + { + return addslashes(str_replace(array("\n", "\r", "\t"), '', $content)); + } + + /** + * Creates a new {@link GoogleMapsAPI} object loaded with the default settings + * and places all of the items in a {@link SS_List} + * e.g. {@link DataList} or {@link ArrayList} on the map. + * + * @param SS_List list of objects to display on a map + * @param array $infowindowtemplateparams Optional array of extra parameters to pass to the map info window + * + * @return MapAPI + */ + public static function get_map(SS_List $list, $infowindowtemplateparams) + { + $gmap = self::instance(); + if ($list) { + foreach ($list as $mappable) { + if (self::ChooseToAddDataobject($mappable)) { + $gmap->addMarkerAsObject($mappable, $infowindowtemplateparams); + } + } + } + + return $gmap; + } + + /** + * Determines if the current DataObject should be included to the map + * Checks if it has Mappable interface implemented + * If it has MapExtension included, the value of MapPinEdited is also checked. + * + * @param DataObject $do + * + * @return bool + */ + private static function ChooseToAddDataobject(DataObject $do) + { + $isMappable = $do->is_a('Mappable'); + + foreach ($do->getExtensionInstances() as $extension) { + $isMappable = $isMappable || $extension instanceof Mappable; + } + + $filterMapPinEdited = $do->hasExtension('MapExtension') + ? $do->MapPinEdited + : true; + + return $isMappable && $filterMapPinEdited; + } } diff --git a/code/Mappable.php b/code/Mappable.php index ae34d4f..67d7b78 100755 --- a/code/Mappable.php +++ b/code/Mappable.php @@ -5,62 +5,60 @@ * helper class. * * @author Uncle Cheese - * @package mappable */ -interface Mappable { +interface Mappable +{ + /** + * An accessor method for the latitude field. + * + * @example + * + * return $this->Lat; + * + * + * @return string + */ + public function getMappableLatitude(); - /** - * An accessor method for the latitude field. - * @example - * - * return $this->Lat; - * - * - * @return string - */ - public function getMappableLatitude(); + /** + * An accessor method for the longitude field. + * + * @example + * + * return $this->Long; + * + * + * @return string + */ + public function getMappableLongitude(); + /** + * An accessor method for the path to the marker pin for this point on the map. + * If null or false, use the default Google Maps icon. + * + * @example + * + * return "mysite/images/map_icon.png"; + * + * + * @return string + */ + public function getMappableMapPin(); - /** - * An accessor method for the longitude field. - * @example - * - * return $this->Long; - * - * - * @return string - */ - public function getMappableLongitude(); - - - /** - * An accessor method for the path to the marker pin for this point on the map. - * If null or false, use the default Google Maps icon. - * @example - * - * return "mysite/images/map_icon.png"; - * - * - * @return string - */ - public function getMappableMapPin(); - - - /** - * An accessor method that returns the content for the map bubble popup. - * It is best to use the {@see ViewableData::renderWith()} method to take advantaging - * of templating syntax when rendering the object's content. - * - * Note: it is critical that the content be sanitized for safe inclusino in the rendered - * JavaScript code for the map. {@see GoogleMapsUtil::sanitize()} - * - * @example - * - * return GoogleMapsUtil::sanitize($this->renderWith('MapBubble')); - * - * - * @return string - */ - public function getMappableMapContent(); - + /** + * An accessor method that returns the content for the map bubble popup. + * It is best to use the {@see ViewableData::renderWith()} method to take advantaging + * of templating syntax when rendering the object's content. + * + * Note: it is critical that the content be sanitized for safe inclusino in the rendered + * JavaScript code for the map. {@see GoogleMapsUtil::sanitize()} + * + * @example + * + * return GoogleMapsUtil::sanitize($this->renderWith('MapBubble')); + * + * + * @return string + */ + public function getMappableMapContent(); } diff --git a/code/MappableData.php b/code/MappableData.php index 715c02c..f903138 100644 --- a/code/MappableData.php +++ b/code/MappableData.php @@ -6,95 +6,99 @@ * @author Uncle Cheese * @package mappable */ -class MappableData extends Extension { - - /** - * Optional template values for the map info windows - */ - private $MarkerTemplateValues = null; - - /** - * URL of static maps api - * @var string - */ - private static $staticmap_api_url = '//maps.googleapis.com/maps/api/staticmap'; - - /** - * Default zoom for static map - * @var int - */ - private static $staticmap_default_zoom = 13; - - /** - * Pass through values to the markers so that when rendering the map info windows, these - * parameters are available to the template. This is of course optional - * - * @param array $values hash array of template key to template value - */ - public function setMarkerTemplateValues($values) { - $this->MarkerTemplateValues = $values; - } - - - public function getRenderableMap($width = null, $height = null, $zoom = 9) { - $gmap = MapUtil::get_map(new ArrayList(array($this->owner)), $this->MarkerTemplateValues); - $w = $width ? $width : MapUtil::$map_width; - $h = $height ? $height : MapUtil::$map_height; - $gmap->setSize($w,$h); - $gmap->setZoom($zoom); - $gmap->setEnableAutomaticCenterZoom(false); - if ($this->owner->MapPinEdited) { - $gmap->setLatLongCenter(array( - 'lat' => $this->owner->getMappableLatitude(), - 'lng' => $this->owner->getMappableLongitude() - )); - } - return $gmap; - } - - - /** - * returns an with a src set to a static map picture - * - * You can use MappableData.staticmap_api_url config var to set the domain of the static map. - * You can use MappableData.staticmap_default_zoom config var to set the default zoom for the static map. - * - * @uses Mappable::getMappableMapPin() to draw a special marker, be sure this image is publicly available - * - * @param int $width - * @param int $height - * @return string - */ - public function StaticMap($width, $height, $zoom = null, $mapType = 'roadmap') { - $lat = $this->owner->getMappableLatitude(); - $lng = $this->owner->getMappableLongitude(); - $pin = $this->owner->getMappableMapPin(); - - // use provided zoom or set a default - if ($zoom == null) { - $zoom = Config::inst()->get('MappableData', 'staticmap_default_zoom'); - } - - //https://maps.googleapis.com/maps/api/staticmap?center=Berkeley,CA&zoom=14&size=400x400&key=YOUR_API_KEY - //maps.googleapis.com/maps/api/staticmap'; - - $apiurl = Config::inst()->get('MappableData', 'staticmap_api_url'); - - $urlparts = array( - 'center' => "$lat,$lng", - 'markers' => "$lat,$lng", - 'zoom' => $zoom, - 'size' => "{$width}x{$height}", - 'sensor' => 'false', //@todo: make sensor param configurable - 'maptype' => $mapType - ); - if ($pin) { - $urlparts['markers'] = "icon:$pin|$lat,$lng"; - } - - $src = htmlentities($apiurl . '?' . http_build_query($urlparts)); - - return ''.$this->owner->Title.''; - } - +class MappableData extends Extension +{ + /** + * Optional template values for the map info windows. + */ + private $MarkerTemplateValues = null; + + /** + * URL of static maps api. + * + * @var string + */ + private static $staticmap_api_url = '//maps.googleapis.com/maps/api/staticmap'; + + /** + * Default zoom for static map. + * + * @var int + */ + private static $staticmap_default_zoom = 13; + + /** + * Pass through values to the markers so that when rendering the map info windows, these + * parameters are available to the template. This is of course optional. + * + * @param array $values hash array of template key to template value + */ + public function setMarkerTemplateValues($values) + { + $this->MarkerTemplateValues = $values; + } + + public function getRenderableMap($width = null, $height = null, $zoom = 9) + { + $gmap = MapUtil::get_map(new ArrayList(array($this->owner)), $this->MarkerTemplateValues); + $w = $width ? $width : MapUtil::$map_width; + $h = $height ? $height : MapUtil::$map_height; + $gmap->setSize($w, $h); + $gmap->setZoom($zoom); + $gmap->setEnableAutomaticCenterZoom(false); + if ($this->owner->MapPinEdited) { + $gmap->setLatLongCenter(array( + 'lat' => $this->owner->getMappableLatitude(), + 'lng' => $this->owner->getMappableLongitude(), + )); + } + + return $gmap; + } + + /** + * returns an with a src set to a static map picture. + * + * You can use MappableData.staticmap_api_url config var to set the domain of the static map. + * You can use MappableData.staticmap_default_zoom config var to set the default zoom for the static map. + * + * @uses Mappable::getMappableMapPin() to draw a special marker, be sure this image is publicly available + * + * @param int $width + * @param int $height + * + * @return string + */ + public function StaticMap($width, $height, $zoom = null, $mapType = 'roadmap') + { + $lat = $this->owner->getMappableLatitude(); + $lng = $this->owner->getMappableLongitude(); + $pin = $this->owner->getMappableMapPin(); + + // use provided zoom or set a default + if ($zoom == null) { + $zoom = Config::inst()->get('MappableData', 'staticmap_default_zoom'); + } + + //https://maps.googleapis.com/maps/api/staticmap?center=Berkeley,CA&zoom=14&size=400x400&key=YOUR_API_KEY + //maps.googleapis.com/maps/api/staticmap'; + + $apiurl = Config::inst()->get('MappableData', 'staticmap_api_url'); + + $urlparts = array( + 'center' => "$lat,$lng", + 'markers' => "$lat,$lng", + 'zoom' => $zoom, + 'size' => "{$width}x{$height}", + 'sensor' => 'false', //@todo: make sensor param configurable + 'maptype' => $mapType, + ); + if ($pin) { + $urlparts['markers'] = "icon:$pin|$lat,$lng"; + } + + $src = htmlentities($apiurl.'?'.http_build_query($urlparts)); + + return ''.$this->owner->Title.''; + } } diff --git a/code/MappableDataObjectSet.php b/code/MappableDataObjectSet.php index 3a537e1..9a28439 100755 --- a/code/MappableDataObjectSet.php +++ b/code/MappableDataObjectSet.php @@ -6,29 +6,31 @@ * @author Uncle Cheese * @package mappable */ -class MappableDataObjectSet extends Extension { +class MappableDataObjectSet extends Extension +{ + /** + * Optional template values for the map info windows. + */ + private $MarkerTemplateValues = null; - /** - * Optional template values for the map info windows - */ - private $MarkerTemplateValues = null; + /** + * Pass through values to the markers so that when rendering the map info windows, these + * parameters are available to the template. This is of course optional. + * + * @param array $values hash array of template key to template value + */ + public function setMarkerTemplateValues($values) + { + $this->MarkerTemplateValues = $values; + } - /** - * Pass through values to the markers so that when rendering the map info windows, these - * parameters are available to the template. This is of course optional - * - * @param array $values hash array of template key to template value - */ - public function setMarkerTemplateValues($values) { - $this->MarkerTemplateValues = $values; - } - - public function getRenderableMap($width = null, $height = null) { - $gmap = MapUtil::get_map($this->owner, $this->MarkerTemplateValues); - $w = $width ? $width : MapUtil::$map_width; - $h = $height ? $height : MapUtil::$map_height; - $gmap->setSize($w,$h); - return $gmap; - } + public function getRenderableMap($width = null, $height = null) + { + $gmap = MapUtil::get_map($this->owner, $this->MarkerTemplateValues); + $w = $width ? $width : MapUtil::$map_width; + $h = $height ? $height : MapUtil::$map_height; + $gmap->setSize($w, $h); + return $gmap; + } } diff --git a/code/shortcodes/GoogleMapShortCodeHandler.php b/code/shortcodes/GoogleMapShortCodeHandler.php index b54b679..f2536a1 100644 --- a/code/shortcodes/GoogleMapShortCodeHandler.php +++ b/code/shortcodes/GoogleMapShortCodeHandler.php @@ -1,74 +1,76 @@ 5, - 'MapType' => 'road' - ); - - // ensure JavaScript for the map service is only downloaded once - $arguments['DownloadJS'] = !MapUtil::get_map_already_rendered(); - MapUtil::set_map_already_rendered(true); - - // convert parameters to CamelCase as per standard template conventions - $arguments['Latitude'] = $arguments['latitude']; - $arguments['Longitude'] = $arguments['longitude']; - - // optional parameter caption - if (isset($arguments['caption'])) { - $arguments['Caption'] = $arguments['caption']; - } - - if (isset($arguments['maptype'])) { - $arguments['MapType'] = $arguments['maptype']; - } - - // optional parameter zoom - if (isset($arguments['zoom'])) { - $arguments['Zoom'] = $arguments['zoom']; - } - - // the id of the dom element to be used to render the street view - $arguments['DomID'] = 'google_sc_map_'.self::$gsv_ctr; - - // fullscreen - $arguments['AllowFullScreen'] = Config::inst()->get('Mappable', 'allow_full_screen'); - - // incrememt the counter to ensure a unique id for each map canvas - self::$gsv_ctr++; - - // merge defaults and arguments - $customised = array_merge($defaults, $arguments); - - // include JavaScript to be appended at the end of the page, namely params for map rendering - //Requirements::javascriptTemplate("mappable/javascript/google/map.google.template.js", $customised); - - //get map view template and render the HTML - $template = new SSViewer('GoogleMapShortCode'); - - //return the template customised with the parmameters - return $template->process(new ArrayData($customised)); - } - - /** - * This is only used for testing, otherwise the sequence of tests change the number returned - */ - public static function resetCounter() { - self::$gsv_ctr = 1; - } +class GoogleMapShortCodeHandler +{ + /* Counter used to ensure unique div ids to allow for multiple maps on on page */ + private static $gsv_ctr = 1; + + public static function parse_googlemap($arguments, $caption = null, $parser = null) + { + // each of latitude and longitude are required at a bare minimum + if (!isset($arguments['latitude'])) { + return ''; + } + + if (!isset($arguments['longitude'])) { + return ''; + } + + // defaults - can be overriden by using zoom and FIXME in the shortcode + $defaults = array( + 'Zoom' => 5, + 'MapType' => 'road', + ); + + // ensure JavaScript for the map service is only downloaded once + $arguments['DownloadJS'] = !MapUtil::get_map_already_rendered(); + MapUtil::set_map_already_rendered(true); + + // convert parameters to CamelCase as per standard template conventions + $arguments['Latitude'] = $arguments['latitude']; + $arguments['Longitude'] = $arguments['longitude']; + + // optional parameter caption + if (isset($arguments['caption'])) { + $arguments['Caption'] = $arguments['caption']; + } + + if (isset($arguments['maptype'])) { + $arguments['MapType'] = $arguments['maptype']; + } + + // optional parameter zoom + if (isset($arguments['zoom'])) { + $arguments['Zoom'] = $arguments['zoom']; + } + + // the id of the dom element to be used to render the street view + $arguments['DomID'] = 'google_sc_map_'.self::$gsv_ctr; + + // fullscreen + $arguments['AllowFullScreen'] = Config::inst()->get('Mappable', 'allow_full_screen'); + + // incrememt the counter to ensure a unique id for each map canvas + ++self::$gsv_ctr; + + // merge defaults and arguments + $customised = array_merge($defaults, $arguments); + + // include JavaScript to be appended at the end of the page, namely params for map rendering + //Requirements::javascriptTemplate("mappable/javascript/google/map.google.template.js", $customised); + + //get map view template and render the HTML + $template = new SSViewer('GoogleMapShortCode'); + + //return the template customised with the parmameters + return $template->process(new ArrayData($customised)); + } + + /** + * This is only used for testing, otherwise the sequence of tests change the number returned. + */ + public static function resetCounter() + { + self::$gsv_ctr = 1; + } } diff --git a/code/shortcodes/GoogleStreetViewShortCodeHandler.php b/code/shortcodes/GoogleStreetViewShortCodeHandler.php index 2c2aba0..0bff756 100644 --- a/code/shortcodes/GoogleStreetViewShortCodeHandler.php +++ b/code/shortcodes/GoogleStreetViewShortCodeHandler.php @@ -1,79 +1,79 @@ 1, - 'Pitch' => 0 - ); - - // ensure JavaScript for the map service is only downloaded once - $arguments['DownloadJS'] = !MapUtil::get_map_already_rendered(); - MapUtil::set_map_already_rendered(true); - - // convert parameters to CamelCase as per standard template conventions - $arguments['Latitude'] = $arguments['latitude']; - $arguments['Longitude'] = $arguments['longitude']; - $arguments['Heading'] = $arguments['heading']; - - // optional parameter caption - if (isset($arguments['caption'])) { - $arguments['Caption'] = $arguments['caption']; - } - - // optional parameter pitch - if (isset($arguments['pitch'])) { - $arguments['Pitch'] = $arguments['pitch']; - } - - // optional parameter zoom - if (isset($arguments['zoom'])) { - $arguments['Zoom'] = $arguments['zoom']; - } - - // the id of the dom element to be used to render the street view - $arguments['DomID'] = 'google_streetview_'.self::$gsv_ctr; - - // incrememt the counter to ensure a unique id for each map canvas - self::$gsv_ctr++; - - // merge defaults and arguments - $customised = array_merge($defaults, $arguments); - - // Include google maps JS at the end of the page - //Requirements::javascriptTemplate("mappable/javascript/google/streetview.google.template.js", $customised); - - - //get streetview template template - $template = new SSViewer('GoogleStreetView'); - - //return the template customised with the parmameters - return $template->process(new ArrayData($customised)); - } - - /** - * This is only used for testing, otherwise the sequence of tests change the number returned - */ - public static function resetCounter() { - self::$gsv_ctr = 1; - } +class GoogleStreetViewShortCodeHandler +{ + /* Counter used to ensure unique div ids to allow for multiple StreetViews on on page */ + private static $gsv_ctr = 1; + + public static function parse_googlestreetview($arguments, $caption = null, $parser = null) + { + // each of latitude, longitude and heading are required at a bare minimum + if (!isset($arguments['latitude'])) { + return ''; + } + + if (!isset($arguments['longitude'])) { + return ''; + } + + if (!isset($arguments['heading'])) { + return ''; + } + + // defaults - these can be overriden by using zoom and pitch in the shortcode + $defaults = array( + 'Zoom' => 1, + 'Pitch' => 0, + ); + + // ensure JavaScript for the map service is only downloaded once + $arguments['DownloadJS'] = !MapUtil::get_map_already_rendered(); + MapUtil::set_map_already_rendered(true); + + // convert parameters to CamelCase as per standard template conventions + $arguments['Latitude'] = $arguments['latitude']; + $arguments['Longitude'] = $arguments['longitude']; + $arguments['Heading'] = $arguments['heading']; + + // optional parameter caption + if (isset($arguments['caption'])) { + $arguments['Caption'] = $arguments['caption']; + } + + // optional parameter pitch + if (isset($arguments['pitch'])) { + $arguments['Pitch'] = $arguments['pitch']; + } + + // optional parameter zoom + if (isset($arguments['zoom'])) { + $arguments['Zoom'] = $arguments['zoom']; + } + + // the id of the dom element to be used to render the street view + $arguments['DomID'] = 'google_streetview_'.self::$gsv_ctr; + + // incrememt the counter to ensure a unique id for each map canvas + ++self::$gsv_ctr; + + // merge defaults and arguments + $customised = array_merge($defaults, $arguments); + + // Include google maps JS at the end of the page + //Requirements::javascriptTemplate("mappable/javascript/google/streetview.google.template.js", $customised); + + //get streetview template template + $template = new SSViewer('GoogleStreetView'); + + //return the template customised with the parmameters + return $template->process(new ArrayData($customised)); + } + + /** + * This is only used for testing, otherwise the sequence of tests change the number returned. + */ + public static function resetCounter() + { + self::$gsv_ctr = 1; + } } diff --git a/tests/GoogleMapShortCodeTest.php b/tests/GoogleMapShortCodeTest.php index ff853ca..bd8371b 100644 --- a/tests/GoogleMapShortCodeTest.php +++ b/tests/GoogleMapShortCodeTest.php @@ -1,17 +1,18 @@ objFromFixture('Page', 'RoadMap'); - $html = ShortcodeParser::get_active()->parse($page->Content); - $expected = <<objFromFixture('Page', 'RoadMap'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $expected = << @@ -20,15 +21,15 @@ public function testRoadMap() {
TEXT; - $this->assertEquals($expected, $html); - } - - - public function testAerialMap() { - GoogleMapShortCodeHandler::resetCounter(); - $page = $this->objFromFixture('Page', 'AerialMap'); - $html = ShortcodeParser::get_active()->parse($page->Content); - $expected = <<assertEquals($expected, $html); + } + + public function testAerialMap() + { + GoogleMapShortCodeHandler::resetCounter(); + $page = $this->objFromFixture('Page', 'AerialMap'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $expected = << @@ -37,15 +38,15 @@ public function testAerialMap() { TEXT; - $this->assertEquals($expected, $html); - } - - - public function testHybridMap() { - GoogleMapShortCodeHandler::resetCounter(); - $page = $this->objFromFixture('Page', 'HybridMap'); - $html = ShortcodeParser::get_active()->parse($page->Content); - $expected = <<assertEquals($expected, $html); + } + + public function testHybridMap() + { + GoogleMapShortCodeHandler::resetCounter(); + $page = $this->objFromFixture('Page', 'HybridMap'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $expected = << @@ -54,15 +55,15 @@ public function testHybridMap() { TEXT; - $this->assertEquals($expected, $html); - } - - - public function testTerrainmap() { - GoogleMapShortCodeHandler::resetCounter(); - $page = $this->objFromFixture('Page', 'TerrainMap'); - $html = ShortcodeParser::get_active()->parse($page->Content); - $expected = <<assertEquals($expected, $html); + } + + public function testTerrainmap() + { + GoogleMapShortCodeHandler::resetCounter(); + $page = $this->objFromFixture('Page', 'TerrainMap'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $expected = << @@ -71,22 +72,20 @@ public function testTerrainmap() { TEXT; - $this->assertEquals($expected, $html); - } - - - public function testNoLongitude() { - $page = $this->objFromFixture('Page', 'MapWithNoLongitude'); - $html = ShortcodeParser::get_active()->parse($page->Content); - $this->assertEquals('Some text', $html); - } - - - public function testNoLatitude() { - $page = $this->objFromFixture('Page', 'MapWithNoLatitude'); - $html = ShortcodeParser::get_active()->parse($page->Content); - $this->assertEquals('Some text', $html); - } - - + $this->assertEquals($expected, $html); + } + + public function testNoLongitude() + { + $page = $this->objFromFixture('Page', 'MapWithNoLongitude'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $this->assertEquals('Some text', $html); + } + + public function testNoLatitude() + { + $page = $this->objFromFixture('Page', 'MapWithNoLatitude'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $this->assertEquals('Some text', $html); + } } diff --git a/tests/GoogleStreetViewShortCodeTest.php b/tests/GoogleStreetViewShortCodeTest.php index d116acd..b73059e 100644 --- a/tests/GoogleStreetViewShortCodeTest.php +++ b/tests/GoogleStreetViewShortCodeTest.php @@ -2,13 +2,14 @@ class GoogleStreetViewShortCodeTest extends SapphireTest { - protected static $fixture_file = 'mappable/tests/shortcodes.yml'; + protected static $fixture_file = 'mappable/tests/shortcodes.yml'; - public function testRoadMap() { - GoogleStreetViewShortCodeHandler::resetCounter(); - $page = $this->objFromFixture('Page', 'StreetView'); - $html = ShortcodeParser::get_active()->parse($page->Content); - $expected = <<objFromFixture('Page', 'StreetView'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $expected = << @@ -17,35 +18,39 @@ public function testRoadMap() { TEXT; - $this->assertEquals($expected, $html); - } - - public function testNoLongitude() { - GoogleStreetViewShortCodeHandler::resetCounter(); - $page = $this->objFromFixture('Page', 'StreetViewNoLongitude'); - $html = ShortcodeParser::get_active()->parse($page->Content); - $this->assertEquals('Some text', $html); - } - - public function testNoLatitude() { - GoogleStreetViewShortCodeHandler::resetCounter(); - $page = $this->objFromFixture('Page', 'StreetViewNoLatitude'); - $html = ShortcodeParser::get_active()->parse($page->Content); - $this->assertEquals('Some text', $html); - } - - public function testNoHeading() { - GoogleStreetViewShortCodeHandler::resetCounter(); - $page = $this->objFromFixture('Page', 'StreetViewNoHeading'); - $html = ShortcodeParser::get_active()->parse($page->Content); - $this->assertEquals('Some text', $html); - } - - public function testZoom() { - GoogleStreetViewShortCodeHandler::resetCounter(); - $page = $this->objFromFixture('Page', 'StreetViewWithZoom'); - $html = ShortcodeParser::get_active()->parse($page->Content); - $expected = <<< TEXT + $this->assertEquals($expected, $html); + } + + public function testNoLongitude() + { + GoogleStreetViewShortCodeHandler::resetCounter(); + $page = $this->objFromFixture('Page', 'StreetViewNoLongitude'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $this->assertEquals('Some text', $html); + } + + public function testNoLatitude() + { + GoogleStreetViewShortCodeHandler::resetCounter(); + $page = $this->objFromFixture('Page', 'StreetViewNoLatitude'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $this->assertEquals('Some text', $html); + } + + public function testNoHeading() + { + GoogleStreetViewShortCodeHandler::resetCounter(); + $page = $this->objFromFixture('Page', 'StreetViewNoHeading'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $this->assertEquals('Some text', $html); + } + + public function testZoom() + { + GoogleStreetViewShortCodeHandler::resetCounter(); + $page = $this->objFromFixture('Page', 'StreetViewWithZoom'); + $html = ShortcodeParser::get_active()->parse($page->Content); + $expected = <<< TEXT Some text
@@ -54,6 +59,6 @@ public function testZoom() {
TEXT; - $this->assertEquals($expected, $html); - } + $this->assertEquals($expected, $html); + } } diff --git a/tests/LatLongFieldTest.php b/tests/LatLongFieldTest.php index 0101f2b..6361255 100644 --- a/tests/LatLongFieldTest.php +++ b/tests/LatLongFieldTest.php @@ -1,127 +1,126 @@ fail('Creation of lat long field should have failed'); - } catch (Exception $e) { - $expected = 'LatLongField argument 1 must be an array containing at' - . ' least two FormField objects for Lat/Long values, resp' - . 'ectively.'; - $this->assertEquals($expected, $e->getMessage()); - } - } - - - public function testConstructTwoFieldsValid() { - $mapField = new LatLongField( - array( - new TextField('Lat', 'Latitude'), - new TextField('Lon', 'Longitude') - ) - ); - - $html = $mapField->FieldHolder(); - $this->assertContains( - '', - $html - ); - $this->assertContains( - '', - $html - ); - $this->assertContains( - '', - $html - ); - $this->assertContains( - '', - $html - ); - - } - - - public function testConstructThreeFieldsValid() { - $mapField = new LatLongField( - array( - new TextField('Lat', 'Latitude'), - new TextField('Lon', 'Longitude'), - new TextField('ZoomLevel', 'Zoom') - ) - ); - - $html = $mapField->FieldHolder(); - $this->assertContains( - '', - $html - ); - $this->assertContains( - '', - $html - ); - $this->assertContains( - '', - $html - ); - $this->assertContains( - '', - $html - ); - $this->assertContains( - '', - $html - ); - $this->assertContains( - '', - $html - ); - } - - - public function testGeocode() { - $this->markTestSkipped('TODO'); - } - - - public function testSetGuidePoints() { - $mapField = new LatLongField( - array( - new TextField('Lat', 'Latitude'), - new TextField('Lon', 'Longitude'), - new TextField('ZoomLevel', 'Zoom') - ) - ); - $guidePoints = array( - array('latitude' => 42, 'longitude' => '113.1'), - array('latitude' => 14.9, 'longitude' => '113.2'), - array('latitude' => 42.3, 'longitude' => '113.4'), - ); - $mapField->setGuidePoints($guidePoints); - - $html = $mapField->FieldHolder(); - $expected = 'data-GuidePoints="[{"latitude":42,"longitude":"113.1&' - . 'quot;},{"latitude":14.9,"longitude":"113.2"},{&q' - . 'uot;latitude":42.3,"longitude":"113.4"}]"'; - - $this->assertContains($expected, $html); - } - +class LatLongFieldTest extends SapphireTest +{ + public function testConstructValid() + { + $mapField = new LatLongField(array( + new TextField('Lat', 'Latitude'), + new TextField('Lon', 'Longitude'), + new TextField('ZoomLevel', 'Zoom'), + ), + array('Address') + ); + } + + public function testConstructOneFieldInvalid() + { + try { + $mapField = new LatLongField( + array( + new TextField('Lat', 'Latitude'), + ) + ); + $this->fail('Creation of lat long field should have failed'); + } catch (Exception $e) { + $expected = 'LatLongField argument 1 must be an array containing at' + .' least two FormField objects for Lat/Long values, resp' + .'ectively.'; + $this->assertEquals($expected, $e->getMessage()); + } + } + + public function testConstructTwoFieldsValid() + { + $mapField = new LatLongField( + array( + new TextField('Lat', 'Latitude'), + new TextField('Lon', 'Longitude'), + ) + ); + + $html = $mapField->FieldHolder(); + $this->assertContains( + '', + $html + ); + $this->assertContains( + '', + $html + ); + $this->assertContains( + '', + $html + ); + $this->assertContains( + '', + $html + ); + } + + public function testConstructThreeFieldsValid() + { + $mapField = new LatLongField( + array( + new TextField('Lat', 'Latitude'), + new TextField('Lon', 'Longitude'), + new TextField('ZoomLevel', 'Zoom'), + ) + ); + + $html = $mapField->FieldHolder(); + $this->assertContains( + '', + $html + ); + $this->assertContains( + '', + $html + ); + $this->assertContains( + '', + $html + ); + $this->assertContains( + '', + $html + ); + $this->assertContains( + '', + $html + ); + $this->assertContains( + '', + $html + ); + } + + public function testGeocode() + { + $this->markTestSkipped('TODO'); + } + + public function testSetGuidePoints() + { + $mapField = new LatLongField( + array( + new TextField('Lat', 'Latitude'), + new TextField('Lon', 'Longitude'), + new TextField('ZoomLevel', 'Zoom'), + ) + ); + $guidePoints = array( + array('latitude' => 42, 'longitude' => '113.1'), + array('latitude' => 14.9, 'longitude' => '113.2'), + array('latitude' => 42.3, 'longitude' => '113.4'), + ); + $mapField->setGuidePoints($guidePoints); + + $html = $mapField->FieldHolder(); + $expected = 'data-GuidePoints="[{"latitude":42,"longitude":"113.1&' + .'quot;},{"latitude":14.9,"longitude":"113.2"},{&q' + .'uot;latitude":42.3,"longitude":"113.4"}]"'; + + $this->assertContains($expected, $html); + } } diff --git a/tests/MapAPITest.php b/tests/MapAPITest.php index 1c80fae..264e602 100644 --- a/tests/MapAPITest.php +++ b/tests/MapAPITest.php @@ -1,69 +1,74 @@ requiredExtensions = array( - 'Member' => array('MapExtension') - ); - parent::setupOnce(); - } - - public function setUp() { - MapUtil::reset(); - parent::setUp(); - } - - public function testSetClusterer() { - $map = $this->getMap(); - $map->setClusterer(true); - $html = $map->forTemplate(); - $this->assertContains('data-clusterergridsize=50', $html); - $this->assertContains('data-clusterermaxzoom=17', $html); - $this->assertContains('data-useclusterer=1', $html); - - $map = $this->getMap(); - $map->setClusterer(true, 60, 14); - $html = $map->forTemplate(); - $this->assertContains('data-clusterergridsize=60', $html); - $this->assertContains('data-clusterermaxzoom=14', $html); - $this->assertContains('data-useclusterer=1', $html); - - $map = $this->getMap(); - $map->setClusterer(false); - $html = $map->forTemplate(); - $this->assertContains('data-useclusterer=false', $html); - $this->assertContains('data-clusterergridsize=50', $html); - $this->assertContains('data-clusterermaxzoom=17', $html); - } - - /* - Toggle as to whether or not to include a style= attribute with width/height - */ - public function testSetShowInlineMapDivStyle() { - $map = $this->getMap(); - $map->setShowInlineMapDivStyle(true); - $html = $map->forTemplate(); - $expected = 'style="width:100%; height: 400px;"'; - $this->assertContains($expected, $html); - - $map->setShowInlineMapDivStyle(false); - $html = $map->forTemplate(); - $this->assertNotContains($expected, $html); - } - - public function testSetAdditionalCSSClasses() { - $map = $this->getMap(); - $map->setAdditionalCSSClasses('bigMap shadowMap'); - $html = $map->forTemplate(); - $expected = 'class="bigMap shadowMap mappable"'; - $this->assertContains($expected, $html); - $map->setAdditionalCSSClasses('bigMap shadowMap'); - } - - - public function testSetMapStyle() { - $style = <<