Eliminar o modulo do Google Maps non usado.
This commit is contained in:
parent
0b9c73d8f3
commit
02db83f5ba
16 changed files with 0 additions and 3165 deletions
|
@ -1,276 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ProcessWire Map Marker Fieldtype
|
|
||||||
*
|
|
||||||
* Holds an address and geocodes it to latitude and longitude via Google Maps
|
|
||||||
*
|
|
||||||
* For documentation about the fields used in this class, please see:
|
|
||||||
* /wire/core/Fieldtype.php
|
|
||||||
*
|
|
||||||
* ProcessWire 2.x
|
|
||||||
* Copyright (C) 2016 by Ryan Cramer
|
|
||||||
* Licensed under MPL 2.0
|
|
||||||
*
|
|
||||||
* https://processwire.com
|
|
||||||
*
|
|
||||||
* @todo implement a getMatchQuery method and support LIKE with address.
|
|
||||||
*
|
|
||||||
* @property string $googleApiKey
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
class FieldtypeMapMarker extends Fieldtype implements ConfigurableModule {
|
|
||||||
|
|
||||||
public static function getModuleInfo() {
|
|
||||||
return array(
|
|
||||||
'title' => 'Map Marker',
|
|
||||||
'version' => 209,
|
|
||||||
'summary' => 'Field that stores an address with latitude and longitude coordinates and has built-in geocoding capability with Google Maps API.',
|
|
||||||
'installs' => 'InputfieldMapMarker',
|
|
||||||
'icon' => 'map-marker',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Include our MapMarker class, which serves as the value for fields of type FieldtypeMapMarker
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function __construct() {
|
|
||||||
require_once(dirname(__FILE__) . '/MapMarker.php');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the Inputfield required by this Fieldtype
|
|
||||||
*
|
|
||||||
* @param Page $page
|
|
||||||
* @param Field $field
|
|
||||||
* @return InputfieldMapMarker
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getInputfield(Page $page, Field $field) {
|
|
||||||
/** @var InputfieldMapMarker $inputfield */
|
|
||||||
$inputfield = $this->modules->get('InputfieldMapMarker');
|
|
||||||
$inputfield->set('googleApiKey', $this->get('googleApiKey'));
|
|
||||||
return $inputfield;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all compatible Fieldtypes
|
|
||||||
*
|
|
||||||
* @param Field $field
|
|
||||||
* @return null
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function ___getCompatibleFieldtypes(Field $field) {
|
|
||||||
// there are no other fieldtypes compatible with this one
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sanitize value for runtime
|
|
||||||
*
|
|
||||||
* @param Page $page
|
|
||||||
* @param Field $field
|
|
||||||
* @param MapMarker $value
|
|
||||||
* @return MapMarker
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function sanitizeValue(Page $page, Field $field, $value) {
|
|
||||||
|
|
||||||
// if it's not a MapMarker, then just return a blank MapMarker
|
|
||||||
if(!$value instanceof MapMarker) $value = $this->getBlankValue($page, $field);
|
|
||||||
|
|
||||||
// if the address changed, tell the $page that this field changed
|
|
||||||
if($value->isChanged('address')) $page->trackChange($field->name);
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a blank value used by this fieldtype
|
|
||||||
*
|
|
||||||
* @param Page $page
|
|
||||||
* @param Field $field
|
|
||||||
* @return MapMarker
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getBlankValue(Page $page, Field $field) {
|
|
||||||
return new MapMarker();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a raw value (value as stored in DB), return the value as it would appear in a Page object
|
|
||||||
*
|
|
||||||
* @param Page $page
|
|
||||||
* @param Field $field
|
|
||||||
* @param string|int|array $value
|
|
||||||
* @return string|int|array|object $value
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function ___wakeupValue(Page $page, Field $field, $value) {
|
|
||||||
|
|
||||||
// get a blank MapMarker instance
|
|
||||||
$marker = $this->getBlankValue($page, $field);
|
|
||||||
|
|
||||||
if("$value[lat]" === "0") $value['lat'] = '';
|
|
||||||
if("$value[lng]" === "0") $value['lng'] = '';
|
|
||||||
|
|
||||||
// populate the marker
|
|
||||||
$marker->address = $value['data'];
|
|
||||||
$marker->lat = $value['lat'];
|
|
||||||
$marker->lng = $value['lng'];
|
|
||||||
$marker->status = $value['status'];
|
|
||||||
$marker->zoom = $value['zoom'];
|
|
||||||
$marker->setTrackChanges(true);
|
|
||||||
|
|
||||||
return $marker;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given an 'awake' value, as set by wakeupValue, convert the value back to a basic type for storage in DB.
|
|
||||||
*
|
|
||||||
* @param Page $page
|
|
||||||
* @param Field $field
|
|
||||||
* @param string|int|array|object $value
|
|
||||||
* @return string|int
|
|
||||||
* @throws WireException
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function ___sleepValue(Page $page, Field $field, $value) {
|
|
||||||
|
|
||||||
$marker = $value;
|
|
||||||
|
|
||||||
if(!$marker instanceof MapMarker)
|
|
||||||
throw new WireException("Expecting an instance of MapMarker");
|
|
||||||
|
|
||||||
// if the address was changed, then force it to geocode the new address
|
|
||||||
if($marker->isChanged('address') && $marker->address && $marker->status != MapMarker::statusNoGeocode) $marker->geocode();
|
|
||||||
|
|
||||||
$sleepValue = array(
|
|
||||||
'data' => $marker->address,
|
|
||||||
'lat' => strlen($marker->lat) ? $marker->lat : 0,
|
|
||||||
'lng' => strlen($marker->lng) ? $marker->lng : 0,
|
|
||||||
'status' => $marker->status,
|
|
||||||
'zoom' => $marker->zoom
|
|
||||||
);
|
|
||||||
|
|
||||||
return $sleepValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the database schema in specified format
|
|
||||||
*
|
|
||||||
* @param Field $field
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getDatabaseSchema(Field $field) {
|
|
||||||
|
|
||||||
// get the default schema
|
|
||||||
$schema = parent::getDatabaseSchema($field);
|
|
||||||
|
|
||||||
$schema['data'] = "VARCHAR(255) NOT NULL DEFAULT ''"; // address (reusing the 'data' field from default schema)
|
|
||||||
$schema['lat'] = "FLOAT(10,6) NOT NULL DEFAULT 0"; // latitude
|
|
||||||
$schema['lng'] = "FLOAT(10,6) NOT NULL DEFAULT 0"; // longitude
|
|
||||||
$schema['status'] = "TINYINT NOT NULL DEFAULT 0"; // geocode status
|
|
||||||
$schema['zoom'] = "TINYINT NOT NULL DEFAULT 0"; // zoom level (schema v1)
|
|
||||||
|
|
||||||
$schema['keys']['latlng'] = "KEY latlng (lat, lng)"; // keep an index of lat/lng
|
|
||||||
$schema['keys']['data'] = 'FULLTEXT KEY `data` (`data`)';
|
|
||||||
$schema['keys']['zoom'] = "KEY zoom (zoom)";
|
|
||||||
|
|
||||||
if($field->id) $this->updateDatabaseSchema($field, $schema);
|
|
||||||
|
|
||||||
return $schema;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the DB schema, if necessary
|
|
||||||
*
|
|
||||||
* @param Field $field
|
|
||||||
* @param array $schema
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
protected function updateDatabaseSchema(Field $field, array $schema) {
|
|
||||||
|
|
||||||
$requiredVersion = 1;
|
|
||||||
$schemaVersion = (int) $field->get('schemaVersion');
|
|
||||||
|
|
||||||
if($schemaVersion >= $requiredVersion) {
|
|
||||||
// already up-to-date
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($schemaVersion == 0) {
|
|
||||||
// update schema to v1: add 'zoom' column
|
|
||||||
$schemaVersion = 1;
|
|
||||||
$database = $this->wire('database');
|
|
||||||
$table = $database->escapeTable($field->getTable());
|
|
||||||
$query = $database->prepare("SHOW TABLES LIKE '$table'");
|
|
||||||
$query->execute();
|
|
||||||
$row = $query->fetch(\PDO::FETCH_NUM);
|
|
||||||
$query->closeCursor();
|
|
||||||
if(!empty($row)) {
|
|
||||||
$query = $database->prepare("SHOW COLUMNS FROM `$table` WHERE field='zoom'");
|
|
||||||
$query->execute();
|
|
||||||
if(!$query->rowCount()) try {
|
|
||||||
$database->exec("ALTER TABLE `$table` ADD zoom $schema[zoom] AFTER status");
|
|
||||||
$this->message("Added 'zoom' column to '$field->table'");
|
|
||||||
} catch(Exception $e) {
|
|
||||||
$this->error($e->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$field->set('schemaVersion', $schemaVersion);
|
|
||||||
$field->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Match values for PageFinder
|
|
||||||
*
|
|
||||||
* @param DatabaseQuerySelect $query
|
|
||||||
* @param string $table
|
|
||||||
* @param string $subfield
|
|
||||||
* @param string $operator
|
|
||||||
* @param string $value
|
|
||||||
* @return DatabaseQuerySelect
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getMatchQuery($query, $table, $subfield, $operator, $value) {
|
|
||||||
if(!$subfield || $subfield == 'address') $subfield = 'data';
|
|
||||||
if($subfield != 'data' || $this->wire('database')->isOperator($operator)) {
|
|
||||||
// if dealing with something other than address, or operator is native to SQL,
|
|
||||||
// then let Fieldtype::getMatchQuery handle it instead
|
|
||||||
return parent::getMatchQuery($query, $table, $subfield, $operator, $value);
|
|
||||||
}
|
|
||||||
// if we get here, then we're performing either %= (LIKE and variations) or *= (FULLTEXT and variations)
|
|
||||||
$ft = new DatabaseQuerySelectFulltext($query);
|
|
||||||
$ft->match($table, $subfield, $operator, $value);
|
|
||||||
return $query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module configuration
|
|
||||||
*
|
|
||||||
* @param array $data
|
|
||||||
* @return InputfieldWrapper
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public static function getModuleConfigInputfields(array $data) {
|
|
||||||
$inputfields = new InputfieldWrapper();
|
|
||||||
/** @var InputfieldText $f */
|
|
||||||
$f = wire('modules')->get('InputfieldText');
|
|
||||||
$f->attr('name', 'googleApiKey');
|
|
||||||
$f->label = __('Google Maps API Key');
|
|
||||||
$f->icon = 'map';
|
|
||||||
$f->description = sprintf(__('[Click here](%s) for instructions from Google on how to obtain an API key.'),
|
|
||||||
'https://developers.google.com/maps/documentation/javascript/get-api-key');
|
|
||||||
$f->attr('value', isset($data['googleApiKey']) ? $data['googleApiKey'] : '');
|
|
||||||
$inputfields->add($f);
|
|
||||||
return $inputfields;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
.InputfieldMapMarker input[type=number],
|
|
||||||
.InputfieldMapMarker input[type=text] {
|
|
||||||
width: 99.5%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerToggle span {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerAddress {
|
|
||||||
float: left;
|
|
||||||
width: 70%;
|
|
||||||
padding-right: 2%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerToggle {
|
|
||||||
float: left;
|
|
||||||
width: 28%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerLat,
|
|
||||||
.InputfieldMapMarkerLng {
|
|
||||||
width: 42%;
|
|
||||||
float: left;
|
|
||||||
padding-right: 2%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerZoom {
|
|
||||||
float: left;
|
|
||||||
width: 10%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarker .notes {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerMap {
|
|
||||||
width: 100%;
|
|
||||||
height: 300px;
|
|
||||||
clear: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (min-width: 768px) {
|
|
||||||
|
|
||||||
.InputfieldMapMarkerAddress {
|
|
||||||
width: 38%;
|
|
||||||
padding-right: 1%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerToggle {
|
|
||||||
width: 2%;
|
|
||||||
padding-right: 0.5%;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerToggle strong {
|
|
||||||
/* hide geocode label */
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerLat,
|
|
||||||
.InputfieldMapMarkerLng {
|
|
||||||
width: 23%;
|
|
||||||
padding-right: 1%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerZoom {
|
|
||||||
float: left;
|
|
||||||
width: 9.5%;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,130 +0,0 @@
|
||||||
/**
|
|
||||||
* Display a Google Map and pinpoint a location for InputfieldMapMarker
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
var InputfieldMapMarker = {
|
|
||||||
|
|
||||||
options: {
|
|
||||||
zoom: 12, // mats, previously 5
|
|
||||||
draggable: true, // +mats
|
|
||||||
center: null,
|
|
||||||
//key: config.InputfieldMapMarker.googleApiKey,
|
|
||||||
mapTypeId: google.maps.MapTypeId.HYBRID,
|
|
||||||
scrollwheel: false,
|
|
||||||
mapTypeControlOptions: {
|
|
||||||
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
|
|
||||||
},
|
|
||||||
scaleControl: false
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function(mapId, lat, lng, zoom, mapType) {
|
|
||||||
|
|
||||||
var options = InputfieldMapMarker.options;
|
|
||||||
|
|
||||||
if(zoom < 1) zoom = 12;
|
|
||||||
options.center = new google.maps.LatLng(lat, lng);
|
|
||||||
options.zoom = parseInt(zoom);
|
|
||||||
|
|
||||||
if(mapType == 'SATELLITE') options.mapTypeId = google.maps.MapTypeId.SATELLITE;
|
|
||||||
else if(mapType == 'ROADMAP') options.mapTypeId = google.maps.MapTypeId.ROADMAP;
|
|
||||||
|
|
||||||
var map = new google.maps.Map(document.getElementById(mapId), options);
|
|
||||||
|
|
||||||
var marker = new google.maps.Marker({
|
|
||||||
position: options.center,
|
|
||||||
map: map,
|
|
||||||
draggable: options.draggable
|
|
||||||
});
|
|
||||||
|
|
||||||
var $map = $('#' + mapId);
|
|
||||||
var $lat = $map.siblings(".InputfieldMapMarkerLat").find("input[type=text]");
|
|
||||||
var $lng = $map.siblings(".InputfieldMapMarkerLng").find("input[type=text]");
|
|
||||||
var $addr = $map.siblings(".InputfieldMapMarkerAddress").find("input[type=text]");
|
|
||||||
var $addrJS = $map.siblings(".InputfieldMapMarkerAddress").find("input[type=hidden]");
|
|
||||||
var $toggle = $map.siblings(".InputfieldMapMarkerToggle").find("input[type=checkbox]");
|
|
||||||
var $zoom = $map.siblings(".InputfieldMapMarkerZoom").find("input[type=number]");
|
|
||||||
var $notes = $map.siblings(".notes");
|
|
||||||
|
|
||||||
$lat.val(marker.getPosition().lat());
|
|
||||||
$lng.val(marker.getPosition().lng());
|
|
||||||
$zoom.val(map.getZoom());
|
|
||||||
|
|
||||||
google.maps.event.addListener(marker, 'dragend', function(event) {
|
|
||||||
var geocoder = new google.maps.Geocoder();
|
|
||||||
var position = this.getPosition();
|
|
||||||
$lat.val(position.lat());
|
|
||||||
$lng.val(position.lng());
|
|
||||||
if($toggle.is(":checked")) {
|
|
||||||
geocoder.geocode({ 'latLng': position }, function(results, status) {
|
|
||||||
if(status == google.maps.GeocoderStatus.OK && results[0]) {
|
|
||||||
$addr.val(results[0].formatted_address);
|
|
||||||
$addrJS.val($addr.val());
|
|
||||||
}
|
|
||||||
$notes.text(status);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
google.maps.event.addListener(map, 'zoom_changed', function() {
|
|
||||||
$zoom.val(map.getZoom());
|
|
||||||
});
|
|
||||||
|
|
||||||
$addr.blur(function() {
|
|
||||||
if(!$toggle.is(":checked")) return true;
|
|
||||||
var geocoder = new google.maps.Geocoder();
|
|
||||||
geocoder.geocode({ 'address': $(this).val()}, function(results, status) {
|
|
||||||
if(status == google.maps.GeocoderStatus.OK && results[0]) {
|
|
||||||
var position = results[0].geometry.location;
|
|
||||||
map.setCenter(position);
|
|
||||||
marker.setPosition(position);
|
|
||||||
$lat.val(position.lat());
|
|
||||||
$lng.val(position.lng());
|
|
||||||
$addrJS.val($addr.val());
|
|
||||||
}
|
|
||||||
$notes.text(status);
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
$zoom.change(function() {
|
|
||||||
map.setZoom(parseInt($(this).val()));
|
|
||||||
});
|
|
||||||
|
|
||||||
$toggle.click(function() {
|
|
||||||
if($(this).is(":checked")) {
|
|
||||||
$notes.text('Geocode ON');
|
|
||||||
// google.maps.event.trigger(marker, 'dragend');
|
|
||||||
$addr.trigger('blur');
|
|
||||||
} else {
|
|
||||||
$notes.text('Geocode OFF');
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
// added by diogo to solve the problem of maps not rendering correctly in hidden elements
|
|
||||||
// trigger a resize on the map when either the tab button or the toggle field bar are pressed
|
|
||||||
|
|
||||||
// get the tab element where this map is integrated
|
|
||||||
var $map = $('#' + mapId);
|
|
||||||
var $tab = $('#_' + $map.closest('.InputfieldFieldsetTabOpen').attr('id'));
|
|
||||||
// get the inputfield where this map is integrated and add the tab to the stack
|
|
||||||
var $inputFields = $map.closest('.Inputfield').find('.InputfieldStateToggle').add($tab);
|
|
||||||
|
|
||||||
$inputFields.on('click',function(){
|
|
||||||
// give it time to open
|
|
||||||
window.setTimeout(function(){
|
|
||||||
google.maps.event.trigger(map,'resize');
|
|
||||||
map.setCenter(options.center);
|
|
||||||
}, 200);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
|
||||||
$(".InputfieldMapMarkerMap").each(function() {
|
|
||||||
var $t = $(this);
|
|
||||||
InputfieldMapMarker.init($t.attr('id'), $t.attr('data-lat'), $t.attr('data-lng'), $t.attr('data-zoom'), $t.attr('data-type'));
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,329 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ProcessWire Map Marker Inputfield
|
|
||||||
*
|
|
||||||
* Provides the admin control panel inputs for FieldtypeMapMarker
|
|
||||||
*
|
|
||||||
* ProcessWire 2.x
|
|
||||||
* Copyright (C) 2016 by Ryan Cramer
|
|
||||||
* Licensed under MPL 2.0
|
|
||||||
*
|
|
||||||
* https://processwire.com
|
|
||||||
*
|
|
||||||
* @property string $defaultAddr
|
|
||||||
* @property int $defaultZoom
|
|
||||||
* @property string $defaultType
|
|
||||||
* @property string $defaultLat
|
|
||||||
* @property string $defaultLng
|
|
||||||
* @property int $height
|
|
||||||
* @property string $googleApiKey
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
class InputfieldMapMarker extends Inputfield {
|
|
||||||
|
|
||||||
public static function getModuleInfo() {
|
|
||||||
return array(
|
|
||||||
'title' => 'Map Marker',
|
|
||||||
'version' => 209,
|
|
||||||
'summary' => "Provides input for the MapMarker Fieldtype",
|
|
||||||
'requires' => 'FieldtypeMapMarker',
|
|
||||||
'icon' => 'map-marker',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultAddr = 'Castaway Cay';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Just in case this Inputfield is being used separately from FieldtypeMapmarker, we include the MapMarker class
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function __construct() {
|
|
||||||
require_once(dirname(__FILE__) . '/MapMarker.php');
|
|
||||||
$this->set('defaultAddr', self::defaultAddr);
|
|
||||||
$this->set('defaultZoom', 12);
|
|
||||||
$this->set('defaultType', 'HYBRID');
|
|
||||||
$this->set('defaultLat', '');
|
|
||||||
$this->set('defaultLng', '');
|
|
||||||
$this->set('height', 300);
|
|
||||||
$this->set('googleApiKey', '');
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set an attribute to this Inputfield
|
|
||||||
*
|
|
||||||
* In this case, we just capture the 'value' attribute and make sure it's something valid
|
|
||||||
*
|
|
||||||
* @param string $key
|
|
||||||
* @param mixed $value
|
|
||||||
* @return $this
|
|
||||||
* @throws WireException
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function setAttribute($key, $value) {
|
|
||||||
|
|
||||||
if($key == 'value' && !$value instanceof MapMarker && !is_null($value)) {
|
|
||||||
throw new WireException("This input only accepts a MapMarker for it's value");
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::setAttribute($key, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isEmpty() {
|
|
||||||
return (!$this->value || ((float) $this->value->lat) === 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderReady(Inputfield $parent = null, $renderValueMode = false) {
|
|
||||||
$url = 'https://maps.google.com/maps/api/js';
|
|
||||||
$key = $this->get('googleApiKey');
|
|
||||||
if($key) $url .= "?key=$key";
|
|
||||||
$this->config->scripts->add($url);
|
|
||||||
return parent::renderReady($parent, $renderValueMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the markup needed to draw the Inputfield
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function ___render() {
|
|
||||||
|
|
||||||
$name = $this->attr('name');
|
|
||||||
$id = $this->attr('id');
|
|
||||||
$marker = $this->attr('value');
|
|
||||||
if($marker->lat == 0.0) $marker->lat = $this->defaultLat;
|
|
||||||
if($marker->lng == 0.0) $marker->lng = $this->defaultLng;
|
|
||||||
if(!$marker->zoom) $marker->zoom = $this->defaultZoom;
|
|
||||||
$address = htmlentities($marker->address, ENT_QUOTES, "UTF-8");
|
|
||||||
$toggleChecked = $marker->status != MapMarker::statusNoGeocode ? " checked='checked'" : '';
|
|
||||||
$status = $marker->status == MapMarker::statusNoGeocode ? 0 : $marker->status;
|
|
||||||
$mapType = $this->defaultType;
|
|
||||||
$height = $this->height ? (int) $this->height : 300;
|
|
||||||
|
|
||||||
$labels = array(
|
|
||||||
'addr' => $this->_('Address'),
|
|
||||||
'lat' => $this->_('Latitude'),
|
|
||||||
'lng' => $this->_('Longitude'),
|
|
||||||
'geo' => $this->_('Geocode?'),
|
|
||||||
'zoom' => $this->_('Zoom')
|
|
||||||
);
|
|
||||||
|
|
||||||
$out = <<< _OUT
|
|
||||||
|
|
||||||
<span></span>
|
|
||||||
|
|
||||||
<p class='InputfieldMapMarkerAddress'>
|
|
||||||
<label>
|
|
||||||
<strong>$labels[addr]</strong>
|
|
||||||
<br />
|
|
||||||
<input type='text' id='{$id}' name='{$name}' value='{$address}' /><br />
|
|
||||||
</label>
|
|
||||||
<input type='hidden' id='_{$name}_js_geocode_address' name='_{$name}_js_geocode_address' value='' />
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class='InputfieldMapMarkerToggle'>
|
|
||||||
<label>
|
|
||||||
<br />
|
|
||||||
<input title='Geocode ON/OFF' type='checkbox' name='_{$name}_status' id='_{$name}_toggle' value='$status'$toggleChecked />
|
|
||||||
<strong>$labels[geo]</strong>
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class='InputfieldMapMarkerLat'>
|
|
||||||
<label>
|
|
||||||
<strong>$labels[lat]</strong><br />
|
|
||||||
<input type='text' id='_{$id}_lat' name='_{$name}_lat' value='{$marker->lat}' />
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class='InputfieldMapMarkerLng'>
|
|
||||||
<label>
|
|
||||||
<strong>$labels[lng]</strong><br />
|
|
||||||
<input type='text' id='_{$id}_lng' name='_{$name}_lng' value='{$marker->lng}' />
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class='InputfieldMapMarkerZoom'>
|
|
||||||
<label>
|
|
||||||
<strong>$labels[zoom]</strong><br />
|
|
||||||
<input type='number' min='0' id='_{$id}_zoom' name='_{$name}_zoom' value='{$marker->zoom}' />
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
|
||||||
_OUT;
|
|
||||||
|
|
||||||
$out .= "<div class='InputfieldMapMarkerMap' " .
|
|
||||||
"id='_{$id}_map' " .
|
|
||||||
"style='height: {$height}px' " .
|
|
||||||
"data-lat='$marker->lat' " .
|
|
||||||
"data-lng='$marker->lng' " .
|
|
||||||
"data-zoom='$marker->zoom' " .
|
|
||||||
"data-type='$mapType'>" .
|
|
||||||
"</div>";
|
|
||||||
|
|
||||||
$this->notes = $marker->statusString;
|
|
||||||
|
|
||||||
if(!$this->get('googleApiKey')) {
|
|
||||||
$msg = $this->_('Please setup a Google Maps API key in the FieldtypeMapMarker module settings');
|
|
||||||
if($this->wire('user')->isSuperuser()) {
|
|
||||||
$link = "<a href='{$this->config->urls->admin}module/edit?name=FieldtypeMapMarker'>";
|
|
||||||
$msg = "$link$msg</a>";
|
|
||||||
$this->warning($msg, Notice::allowMarkup);
|
|
||||||
} else {
|
|
||||||
$this->warning($msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process the input after a form submission
|
|
||||||
*
|
|
||||||
* @param WireInputData $input
|
|
||||||
* @return $this
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function ___processInput(WireInputData $input) {
|
|
||||||
|
|
||||||
$name = $this->attr('name');
|
|
||||||
$marker = $this->attr('value');
|
|
||||||
|
|
||||||
if(!isset($input->$name)) return $this;
|
|
||||||
|
|
||||||
if($input->$name == $this->defaultAddr) {
|
|
||||||
$marker->set('address', '');
|
|
||||||
} else {
|
|
||||||
$marker->set('address', $input->$name);
|
|
||||||
}
|
|
||||||
|
|
||||||
$lat = $input["_{$name}_lat"];
|
|
||||||
$lng = $input["_{$name}_lng"];
|
|
||||||
|
|
||||||
$precision = 4;
|
|
||||||
if( ((string) round($lat, $precision)) != ((string) round($this->defaultLat, $precision)) ||
|
|
||||||
((string) round($lng, $precision)) != ((string) round($this->defaultLng, $precision))) {
|
|
||||||
|
|
||||||
$marker->set('lat', $lat);
|
|
||||||
$marker->set('lng', $lng);
|
|
||||||
} else {
|
|
||||||
// $this->message("Kept lat/lng at unset value", Notice::debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
$zoom = $input["_{$name}_zoom"];
|
|
||||||
if($zoom > -1 && $zoom < 30) $marker->zoom = (int) $zoom;
|
|
||||||
|
|
||||||
$status = $input["_{$name}_status"];
|
|
||||||
if(is_null($status)) $marker->set('status', MapMarker::statusNoGeocode); // disable geocode
|
|
||||||
else $marker->set('status', (int) $status);
|
|
||||||
|
|
||||||
// if the address changed, then redo the geocoding.
|
|
||||||
// while we do this in the Fieldtype, we also do it here in case this Inputfield is used on it's own.
|
|
||||||
// the MapMarker class checks to make sure it doesn't do the same geocode twice.
|
|
||||||
if($marker->isChanged('address') && $marker->address && $marker->status != MapMarker::statusNoGeocode) {
|
|
||||||
// double check that the address wasn't already populated by the JS geocoder
|
|
||||||
// this prevents user-dragged markers that don't geocode to an exact location from getting
|
|
||||||
// unintentionally moved by the PHP-side geocoder
|
|
||||||
if($input["_{$name}_js_geocode_address"] == $marker->address) {
|
|
||||||
// prevent the geocoder from running in the fieldtype
|
|
||||||
$marker->skipGeocode = true;
|
|
||||||
$this->message('Skipping geocode (already done by JS geocoder)', Notice::debug);
|
|
||||||
} else {
|
|
||||||
$marker->geocode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function ___getConfigInputfields() {
|
|
||||||
$inputfields = parent::___getConfigInputfields();
|
|
||||||
|
|
||||||
/** @var InputfieldText $field */
|
|
||||||
$field = $this->modules->get('InputfieldText');
|
|
||||||
$field->attr('name', 'defaultAddr');
|
|
||||||
$field->label = $this->_('Default Address');
|
|
||||||
$field->description = $this->_('This will be geocoded to become the starting point of the map.');
|
|
||||||
$field->attr('value', $this->defaultAddr);
|
|
||||||
$field->notes = $this->_('When modifying the default address, please make the Latitude and Longitude fields below blank, which will force the system to geocode your new address.');
|
|
||||||
$inputfields->add($field);
|
|
||||||
|
|
||||||
if(!$this->defaultLat && !$this->defaultLng) {
|
|
||||||
$m = new MapMarker();
|
|
||||||
$m->address = $this->defaultAddr;
|
|
||||||
$status = $m->geocode();
|
|
||||||
if($status > 0) {
|
|
||||||
$this->defaultLat = $m->lat;
|
|
||||||
$this->defaultLng = $m->lng;
|
|
||||||
$this->message($this->_('Geocoded your default address. Please hit save once again to commit the new default latitude and longitude.'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var InputfieldText $field */
|
|
||||||
$field = $this->modules->get('InputfieldText');
|
|
||||||
$field->attr('name', 'defaultLat');
|
|
||||||
$field->label = $this->_('Default Latitude');
|
|
||||||
$field->attr('value', $this->defaultLat);
|
|
||||||
$field->columnWidth = 50;
|
|
||||||
$inputfields->add($field);
|
|
||||||
|
|
||||||
/** @var InputfieldText $field */
|
|
||||||
$field = $this->modules->get('InputfieldText');
|
|
||||||
$field->attr('name', 'defaultLng');
|
|
||||||
$field->label = $this->_('Default Longitude');
|
|
||||||
$field->attr('value', $this->defaultLng);
|
|
||||||
$field->columnWidth = 50;
|
|
||||||
$inputfields->add($field);
|
|
||||||
|
|
||||||
/** @var InputfieldRadios $field */
|
|
||||||
$field = $this->modules->get('InputfieldRadios');
|
|
||||||
$field->attr('name', 'defaultType');
|
|
||||||
$field->label = $this->_('Default Map Type');
|
|
||||||
$field->addOption('HYBRID', $this->_('Hybrid'));
|
|
||||||
$field->addOption('ROADMAP', $this->_('Road Map'));
|
|
||||||
$field->addOption('SATELLITE', $this->_('Satellite'));
|
|
||||||
$field->attr('value', $this->defaultType);
|
|
||||||
$field->optionColumns = 1;
|
|
||||||
$field->columnWidth = 50;
|
|
||||||
$inputfields->add($field);
|
|
||||||
|
|
||||||
/** @var InputfieldInteger $field */
|
|
||||||
$field = $this->modules->get('InputfieldInteger');
|
|
||||||
$field->attr('name', 'height');
|
|
||||||
$field->label = $this->_('Map Height (in pixels)');
|
|
||||||
$field->attr('value', $this->height);
|
|
||||||
$field->attr('type', 'number');
|
|
||||||
$field->columnWidth = 50;
|
|
||||||
$inputfields->add($field);
|
|
||||||
|
|
||||||
/** @var InputfieldInteger $field */
|
|
||||||
$field = $this->modules->get('InputfieldInteger');
|
|
||||||
$field->attr('name', 'defaultZoom');
|
|
||||||
$field->label = $this->_('Default Zoom');
|
|
||||||
$field->description = $this->_('Enter a value between 1 and 23. The highest zoom level is typically somewhere between 19-23, depending on the location being zoomed and how much data Google has for the location.'); // Zoom level description
|
|
||||||
$field->attr('value', $this->defaultZoom);
|
|
||||||
$field->attr('type', 'number');
|
|
||||||
$inputfields->add($field);
|
|
||||||
|
|
||||||
/** @var InputfieldMarkup $field */
|
|
||||||
$field = $this->modules->get('InputfieldMarkup');
|
|
||||||
$field->label = $this->_('API Notes');
|
|
||||||
$field->description = $this->_('You can access individual values from this field using the following from your template files:');
|
|
||||||
$field->value =
|
|
||||||
"<pre>" .
|
|
||||||
"\$page->{$this->name}->address\n" .
|
|
||||||
"\$page->{$this->name}->lat\n" .
|
|
||||||
"\$page->{$this->name}->lng\n" .
|
|
||||||
"\$page->{$this->name}->zoom" .
|
|
||||||
"</pre>";
|
|
||||||
|
|
||||||
$inputfields->add($field);
|
|
||||||
|
|
||||||
return $inputfields;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,126 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class to hold an address and geocode it to latitude/longitude
|
|
||||||
*
|
|
||||||
* @property string $lat
|
|
||||||
* @property string $lng
|
|
||||||
* @property string $address
|
|
||||||
* @property int $status
|
|
||||||
* @property int $zoom
|
|
||||||
* @property bool $skipGeocode
|
|
||||||
* @property string $statusString
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class MapMarker extends WireData {
|
|
||||||
|
|
||||||
const statusNoGeocode = -100;
|
|
||||||
|
|
||||||
protected $geocodeStatuses = array(
|
|
||||||
|
|
||||||
0 => 'N/A',
|
|
||||||
1 => 'OK',
|
|
||||||
2 => 'OK_ROOFTOP',
|
|
||||||
3 => 'OK_RANGE_INTERPOLATED',
|
|
||||||
4 => 'OK_GEOMETRIC_CENTER',
|
|
||||||
5 => 'OK_APPROXIMATE',
|
|
||||||
|
|
||||||
-1 => 'UNKNOWN',
|
|
||||||
-2 => 'ZERO_RESULTS',
|
|
||||||
-3 => 'OVER_QUERY_LIMIT',
|
|
||||||
-4 => 'REQUEST_DENIED',
|
|
||||||
-5 => 'INVALID_REQUEST',
|
|
||||||
|
|
||||||
-100 => 'Geocode OFF', // RCD
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
protected $geocodedAddress = '';
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
$this->set('lat', '');
|
|
||||||
$this->set('lng', '');
|
|
||||||
$this->set('address', '');
|
|
||||||
$this->set('status', 0);
|
|
||||||
$this->set('zoom', 0);
|
|
||||||
// temporary runtime property to indicate the geocode should be skipped
|
|
||||||
$this->set('skipGeocode', false);
|
|
||||||
$this->set('statusString', '');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function set($key, $value) {
|
|
||||||
|
|
||||||
if($key == 'lat' || $key == 'lng') {
|
|
||||||
// if value isn't numeric, then it's not valid: make it blank
|
|
||||||
if(strpos($value, ',') !== false) $value = str_replace(',', '.', $value); // convert 123,456 to 123.456
|
|
||||||
if(!is_numeric($value)) $value = '';
|
|
||||||
|
|
||||||
} else if($key == 'address') {
|
|
||||||
$value = wire('sanitizer')->text($value);
|
|
||||||
|
|
||||||
} else if($key == 'status') {
|
|
||||||
$value = (int) $value;
|
|
||||||
if(!isset($this->geocodeStatuses[$value])) $value = -1; // -1 = unknown
|
|
||||||
} else if($key == 'zoom') {
|
|
||||||
$value = (int) $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::set($key, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get($key) {
|
|
||||||
if($key == 'statusString') return str_replace('_', ' ', $this->geocodeStatuses[$this->status]);
|
|
||||||
return parent::get($key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function geocode() {
|
|
||||||
if($this->skipGeocode) return -100;
|
|
||||||
|
|
||||||
// check if address was already geocoded
|
|
||||||
if($this->geocodedAddress == $this->address) return $this->status;
|
|
||||||
$this->geocodedAddress = $this->address;
|
|
||||||
|
|
||||||
$url = "https://maps.googleapis.com/maps/api/geocode/json?address=" . urlencode($this->address);
|
|
||||||
$apiKey = $this->modules->get('FieldtypeMapMarker')->get('googleApiKey');
|
|
||||||
if($apiKey) $url .= "&key=$apiKey";
|
|
||||||
$http = new WireHttp();
|
|
||||||
$json = $http->getJSON($url, true);
|
|
||||||
|
|
||||||
if(empty($json['status']) || $json['status'] != 'OK') {
|
|
||||||
$this->error("Error geocoding address");
|
|
||||||
if(isset($json['status'])) $this->status = (int) array_search($json['status'], $this->geocodeStatuses);
|
|
||||||
else $this->status = -1;
|
|
||||||
$this->lat = 0;
|
|
||||||
$this->lng = 0;
|
|
||||||
return $this->status;
|
|
||||||
}
|
|
||||||
|
|
||||||
$geometry = $json['results'][0]['geometry'];
|
|
||||||
$location = $geometry['location'];
|
|
||||||
$locationType = $geometry['location_type'];
|
|
||||||
|
|
||||||
$this->lat = $location['lat'];
|
|
||||||
$this->lng = $location['lng'];
|
|
||||||
|
|
||||||
$statusString = $json['status'] . '_' . $locationType;
|
|
||||||
$status = array_search($statusString, $this->geocodeStatuses);
|
|
||||||
if($status === false) $status = 1; // OK
|
|
||||||
|
|
||||||
$this->status = $status;
|
|
||||||
$this->message("Geocode {$this->statusString}: '{$this->address}'");
|
|
||||||
|
|
||||||
return $this->status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If accessed as a string, then just output the lat, lng coordinates
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function __toString() {
|
|
||||||
return "$this->address ($this->lat, $this->lng, $this->zoom) [$this->statusString]";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,214 +0,0 @@
|
||||||
/**
|
|
||||||
* ProcessWire Map Markup (JS)
|
|
||||||
*
|
|
||||||
* Renders maps for the FieldtypeMapMarker module
|
|
||||||
*
|
|
||||||
* ProcessWire 2.x
|
|
||||||
* Copyright (C) 2016 by Ryan Cramer
|
|
||||||
* Licensed under MPL 2.0
|
|
||||||
*
|
|
||||||
* http://processwire.com
|
|
||||||
*
|
|
||||||
* Javascript Usage:
|
|
||||||
* =================
|
|
||||||
* var map = new MarkupGoogleMap();
|
|
||||||
* map.setOption('any-google-maps-option', 'value');
|
|
||||||
* map.setOption('zoom', 12); // example
|
|
||||||
*
|
|
||||||
* // init(container ID, latitude, longitude):
|
|
||||||
* map.init('#map-div', 26.0936823, -77.5332796);
|
|
||||||
*
|
|
||||||
* // addMarker(latitude, longitude, optional URL, optional URL to icon file):
|
|
||||||
* map.addMarker(26.0936823, -77.5332796, 'en.wikipedia.org/wiki/Castaway_Cay', '');
|
|
||||||
* map.addMarker(...you may have as many of these as you want...);
|
|
||||||
*
|
|
||||||
* // optionally fit the map to the bounds of the markers you added
|
|
||||||
* map.fitToMarkers();
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
function MarkupGoogleMap() {
|
|
||||||
|
|
||||||
this.map = null;
|
|
||||||
this.markers = [];
|
|
||||||
this.numMarkers = 0;
|
|
||||||
this.icon = '';
|
|
||||||
this.iconHover = '';
|
|
||||||
this.shadow = '';
|
|
||||||
|
|
||||||
this.hoverBox = null;
|
|
||||||
this.hoverBoxOffsetTop = 0;
|
|
||||||
this.hoverBoxOffsetLeft = 0;
|
|
||||||
|
|
||||||
this.options = {
|
|
||||||
zoom: 10,
|
|
||||||
center: null,
|
|
||||||
mapTypeId: google.maps.MapTypeId.ROADMAP,
|
|
||||||
scrollwheel: false,
|
|
||||||
mapTypeControlOptions: {
|
|
||||||
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
|
|
||||||
},
|
|
||||||
scaleControl: false,
|
|
||||||
|
|
||||||
// disable points of interest
|
|
||||||
styles: [{
|
|
||||||
featureType: "poi",
|
|
||||||
stylers: [ { visibility: "off" } ]
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
this._currentURL = '';
|
|
||||||
|
|
||||||
this.init = function(mapID, lat, lng) {
|
|
||||||
if(lat != 0) this.options.center = new google.maps.LatLng(lat, lng);
|
|
||||||
this.map = new google.maps.Map(document.getElementById(mapID), this.options);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setOption = function(key, value) {
|
|
||||||
this.options[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setIcon = function(url) {
|
|
||||||
this.icon = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setIconHover = function(url) {
|
|
||||||
this.iconHover = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setShadow = function(url) {
|
|
||||||
this.shadow = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setHoverBox = function(markup) {
|
|
||||||
|
|
||||||
if(!markup.length) {
|
|
||||||
this.hoverBox = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hoverBox = $(markup);
|
|
||||||
var $hoverBox = this.hoverBox;
|
|
||||||
|
|
||||||
this.hoverBoxOffsetTop = parseInt($hoverBox.attr('data-top'));
|
|
||||||
this.hoverBoxOffsetLeft = parseInt($hoverBox.attr('data-left'));
|
|
||||||
|
|
||||||
$("body").append($hoverBox);
|
|
||||||
|
|
||||||
// keep it hidden/out of the way until needed
|
|
||||||
$hoverBox.css({
|
|
||||||
position: 'absolute',
|
|
||||||
left: 0,
|
|
||||||
top: '-100px'
|
|
||||||
});
|
|
||||||
|
|
||||||
$hoverBox.mouseout(function() {
|
|
||||||
$hoverBox.hide();
|
|
||||||
}).click(function() {
|
|
||||||
if(this._currentURL.length > 0) window.location.href = this._currentURL;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.addMarker = function(lat, lng, url, title, icon, shadow) {
|
|
||||||
if(lat == 0.0) return;
|
|
||||||
|
|
||||||
var latLng = new google.maps.LatLng(lat, lng);
|
|
||||||
var zIndex = 99999 + this.numMarkers;
|
|
||||||
|
|
||||||
var markerOptions = {
|
|
||||||
position: latLng,
|
|
||||||
map: this.map,
|
|
||||||
linkURL: '',
|
|
||||||
zIndex: zIndex
|
|
||||||
};
|
|
||||||
|
|
||||||
if(typeof icon !== "undefined" && icon.length > 0) markerOptions.icon = icon;
|
|
||||||
else if(this.icon) markerOptions.icon = this.icon;
|
|
||||||
|
|
||||||
// console.log(markerOptions);
|
|
||||||
|
|
||||||
if(typeof shadow !== "undefined" && shadow.length > 0) markerOptions.shadow = shadow;
|
|
||||||
else if(this.shadow.length > 0) markerOptions.shadow = this.shadow;
|
|
||||||
|
|
||||||
var marker = new google.maps.Marker(markerOptions);
|
|
||||||
|
|
||||||
if(url.length > 0) marker.linkURL = url;
|
|
||||||
if(this.hoverBox) marker.hoverBoxTitle = title;
|
|
||||||
else marker.setTitle(title);
|
|
||||||
|
|
||||||
this.markers[this.numMarkers] = marker;
|
|
||||||
this.numMarkers++;
|
|
||||||
|
|
||||||
if(marker.linkURL.length > 0) {
|
|
||||||
google.maps.event.addListener(marker, 'click', function(e) {
|
|
||||||
window.location.href = marker.linkURL;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if(markerOptions.icon !== "undefined" && this.iconHover) {
|
|
||||||
var iconHover = this.iconHover;
|
|
||||||
google.maps.event.addListener(marker, 'mouseover', function(e) {
|
|
||||||
marker.setIcon(iconHover);
|
|
||||||
});
|
|
||||||
google.maps.event.addListener(marker, 'mouseout', function(e) {
|
|
||||||
marker.setIcon(markerOptions.icon);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.hoverBox) {
|
|
||||||
|
|
||||||
var $hoverBox = this.hoverBox;
|
|
||||||
var offsetTop = this.hoverBoxOffsetTop;
|
|
||||||
var offsetLeft = this.hoverBoxOffsetLeft;
|
|
||||||
|
|
||||||
var mouseMove = function(e) {
|
|
||||||
$hoverBox.css({
|
|
||||||
'top': e.pageY + offsetTop,
|
|
||||||
'left': e.pageX + offsetLeft
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// console.log($hoverBox);
|
|
||||||
|
|
||||||
google.maps.event.addListener(marker, 'mouseover', function(e) {
|
|
||||||
this._currentURL = url;
|
|
||||||
$hoverBox.html("<span>" + marker.hoverBoxTitle + "</span>")
|
|
||||||
.css('top', '0px')
|
|
||||||
.css('left', '0px')
|
|
||||||
.css('display', 'block')
|
|
||||||
.css('width', 'auto')
|
|
||||||
.css('z-index', 9999);
|
|
||||||
$hoverBox.show();
|
|
||||||
|
|
||||||
$(document).mousemove(mouseMove);
|
|
||||||
});
|
|
||||||
|
|
||||||
google.maps.event.addListener(marker, 'mouseout', function(e) {
|
|
||||||
$hoverBox.hide();
|
|
||||||
$(document).unbind("mousemove", mouseMove);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.fitToMarkers = function() {
|
|
||||||
|
|
||||||
var bounds = new google.maps.LatLngBounds();
|
|
||||||
var map = this.map;
|
|
||||||
|
|
||||||
for(var i = 0; i < this.numMarkers; i++) {
|
|
||||||
var latLng = this.markers[i].position;
|
|
||||||
bounds.extend(latLng);
|
|
||||||
}
|
|
||||||
|
|
||||||
map.fitBounds(bounds);
|
|
||||||
|
|
||||||
|
|
||||||
var listener = google.maps.event.addListener(map, "idle", function() {
|
|
||||||
if(map.getZoom() < 2) map.setZoom(2);
|
|
||||||
google.maps.event.removeListener(listener);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,251 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ProcessWire Map Markup
|
|
||||||
*
|
|
||||||
* Renders maps for the FieldtypeMapMarker module
|
|
||||||
*
|
|
||||||
* ProcessWire 2.x
|
|
||||||
* Copyright (C) 2016 by Ryan Cramer
|
|
||||||
* Licensed under MPL 2.0
|
|
||||||
*
|
|
||||||
* https://processwire.com
|
|
||||||
*
|
|
||||||
* USAGE:
|
|
||||||
* ======
|
|
||||||
*
|
|
||||||
* Add this somewhere before your closing </head> tag:
|
|
||||||
*
|
|
||||||
* <script type='text/javascript' src='https://maps.googleapis.com/maps/api/js'></script>
|
|
||||||
*
|
|
||||||
* In the location where you want to output your map, do the following in your template file:
|
|
||||||
*
|
|
||||||
* $map = $modules->get('MarkupGoogleMap');
|
|
||||||
* echo $map->render($page, 'map'); // replace 'map' with the name of your FieldtypeMap field
|
|
||||||
*
|
|
||||||
* To render a map with multiple markers on it, specify a PageArray rather than a single $page:
|
|
||||||
*
|
|
||||||
* $items = $pages->find("template=something, map!='', sort=title");
|
|
||||||
* $map = $modules->get('MarkupGoogleMap');
|
|
||||||
* echo $map->render($items, 'map');
|
|
||||||
*
|
|
||||||
* To specify options, provide a 3rd argument with an options array:
|
|
||||||
*
|
|
||||||
* $map = $modules->get('MarkupGoogleMap');
|
|
||||||
* echo $map->render($items, 'map', array('height' => '500px'));
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* OPTIONS
|
|
||||||
* =======
|
|
||||||
* Here is a list of all possible options (with defaults shown):
|
|
||||||
*
|
|
||||||
* // default width of the map
|
|
||||||
* 'width' => '100%'
|
|
||||||
*
|
|
||||||
* // default height of the map
|
|
||||||
* 'height' => '300px'
|
|
||||||
*
|
|
||||||
* // zoom level
|
|
||||||
* 'zoom' => 12 (or $field->defaultZoom)
|
|
||||||
*
|
|
||||||
* // map type: ROADMAP, HYBRID or SATELLITE
|
|
||||||
* 'type' => 'HYBRID' or $field->defaultType
|
|
||||||
*
|
|
||||||
* // map ID attribute
|
|
||||||
* 'id' => "mgmap"
|
|
||||||
*
|
|
||||||
* // map class attribute
|
|
||||||
* 'class' => "MarkupGoogleMap"
|
|
||||||
*
|
|
||||||
* // center latitude
|
|
||||||
* 'lat' => $field->defaultLat
|
|
||||||
*
|
|
||||||
* // center longitude
|
|
||||||
* 'lng' => $field->defaultLng
|
|
||||||
*
|
|
||||||
* // set to false only if you will style the map <div> yourself
|
|
||||||
* 'useStyles' => true
|
|
||||||
*
|
|
||||||
* // allows single-marker map to use marker settings rather than map settings
|
|
||||||
* 'useMarkerSettings' => true
|
|
||||||
*
|
|
||||||
* // field to use for the marker link, or blank to not link
|
|
||||||
* 'markerLinkField' => 'url'
|
|
||||||
*
|
|
||||||
* // field to use for the marker title
|
|
||||||
* 'markerTitleField' => 'title'
|
|
||||||
*
|
|
||||||
* // map will automatically adjust to fit to the given markers (when multiple markers)
|
|
||||||
* 'fitToMarkers' => true
|
|
||||||
*
|
|
||||||
* // use hover box? When true, shows a tooltip-type box when you hover the marker, populated with the markerTitleField
|
|
||||||
* // this is often more useful than the default presentation google maps uses
|
|
||||||
* 'useHoverBox' => false
|
|
||||||
*
|
|
||||||
* // when useHoverBox is true, you can specify the markup used for it. Use the following (which is the default) as your starting point:
|
|
||||||
* 'hoverBoxMarkup' => "<div data-top='-10' data-left='15' style='background: #000; color: #fff; padding: 0.25em 0.5em; border-radius: 3px;'></div>",
|
|
||||||
*
|
|
||||||
* // FUll URL to icon file to use for markers. Blank=use default Google marker icon.
|
|
||||||
* 'icon' => '',
|
|
||||||
*
|
|
||||||
* // Any extra javascript initialization code you want to occur before the map itself is drawn
|
|
||||||
* 'init' => '',
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
class MarkupGoogleMap extends WireData implements Module {
|
|
||||||
|
|
||||||
public static function getModuleInfo() {
|
|
||||||
return array(
|
|
||||||
'title' => 'Map Markup (Google Maps)',
|
|
||||||
'version' => 101,
|
|
||||||
'summary' => 'Renders Google Maps for the MapMarker Fieldtype',
|
|
||||||
'requires' => 'FieldtypeMapMarker',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Include our MapMarker class, which serves as the value for fields of type FieldtypeMapMarker
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function init() {
|
|
||||||
require_once(dirname(__FILE__) . '/MapMarker.php');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get associative array of map options
|
|
||||||
*
|
|
||||||
* @param string $fieldName
|
|
||||||
* @return array
|
|
||||||
* @throws WireException
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getOptions($fieldName) {
|
|
||||||
|
|
||||||
static $n = 0;
|
|
||||||
$field = $this->fields->get($fieldName);
|
|
||||||
if(!$field) throw new WireException("Unknown field: $fieldName");
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'useStyles' => true,
|
|
||||||
'fitToMarkers' => true,
|
|
||||||
'useMarkerSettings' => true,
|
|
||||||
'useHoverBox' => false,
|
|
||||||
'hoverBoxMarkup' => "<div data-top='-10' data-left='15' style='background: #000; color: #fff; padding: 0.25em 0.5em; border-radius: 3px;'></div>",
|
|
||||||
'markerLinkField' => 'url',
|
|
||||||
'markerTitleField' => 'title',
|
|
||||||
'width' => '100%',
|
|
||||||
'height' => $field->height,
|
|
||||||
'zoom' => $field->defaultZoom ? (int) $field->defaultZoom : 12,
|
|
||||||
'type' => $field->defaultType ? $field->defaultType : 'HYBRID',
|
|
||||||
'id' => "mgmap" . (++$n),
|
|
||||||
'class' => "MarkupGoogleMap",
|
|
||||||
'lat' => $field->defaultLat,
|
|
||||||
'lng' => $field->defaultLng,
|
|
||||||
'icon' => '', // url to icon (blank=use default)
|
|
||||||
'iconHover' => '', // url to icon when hovered (default=none)
|
|
||||||
'shadow' => '', // url to icon shadow (blank=use default)
|
|
||||||
'init' => '', // additional javascript code to insert in map initialization
|
|
||||||
'n' => $n,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the script tag for loading Google Maps
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
* @throws WireException
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getGMapScript() {
|
|
||||||
$url = 'https://maps.google.com/maps/api/js';
|
|
||||||
$key = $this->wire('modules')->get('FieldtypeMapMarker')->get('googleApiKey');
|
|
||||||
if($key) $url .= "?key=$key";
|
|
||||||
return "<script type='text/javascript' src='$url'></script>";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render map markup
|
|
||||||
*
|
|
||||||
* @param PageArray|Page $pageArray Page (or multiple pages in PageArray) containing map field
|
|
||||||
* @param $fieldName Name of the map field
|
|
||||||
* @param array $options Options to adjust behavior
|
|
||||||
* @return string
|
|
||||||
* @throws WireException
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function render($pageArray, $fieldName, array $options = array()) {
|
|
||||||
|
|
||||||
static $n = 0;
|
|
||||||
$n++;
|
|
||||||
|
|
||||||
$defaultOptions = $this->getOptions($fieldName);
|
|
||||||
$options = array_merge($defaultOptions, $options);
|
|
||||||
|
|
||||||
if($pageArray instanceof Page) {
|
|
||||||
$page = $pageArray;
|
|
||||||
$pageArray = new PageArray();
|
|
||||||
$pageArray->add($page);
|
|
||||||
}
|
|
||||||
|
|
||||||
$height = $options['height'];
|
|
||||||
$width = $options['width'];
|
|
||||||
if(empty($height)) $height = 300;
|
|
||||||
if(ctype_digit("$height")) $height .= "px";
|
|
||||||
if(ctype_digit("$width")) $width .= "px";
|
|
||||||
|
|
||||||
$style = '';
|
|
||||||
if($options['useStyles'] && !empty($height) && !empty($width)) {
|
|
||||||
$style = " style='width: $width; height: $height;'";
|
|
||||||
}
|
|
||||||
|
|
||||||
$lat = $options['lat'];
|
|
||||||
$lng = $options['lng'];
|
|
||||||
$zoom = $options['zoom'] > 0 ? (int) $options['zoom'] : $defaultOptions['zoom'];
|
|
||||||
$type = in_array($options['type'], array('ROADMAP', 'SATELLITE', 'HYBRID')) ? $options['type'] : 'HYBRID';
|
|
||||||
|
|
||||||
if($options['useMarkerSettings'] && (count($pageArray) == 1 || !$lat)) {
|
|
||||||
// single marker overrides lat, lng and zoom settings
|
|
||||||
$marker = $pageArray->first()->get($fieldName);
|
|
||||||
$lat = $marker->lat;
|
|
||||||
$lng = $marker->lng;
|
|
||||||
if($marker->zoom > 0) $zoom = (int) $marker->zoom;
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = $options['id'];
|
|
||||||
$out = '';
|
|
||||||
|
|
||||||
if($n === 1) $out .= "<script type='text/javascript' src='{$this->config->urls->MarkupGoogleMap}MarkupGoogleMap.js'></script>";
|
|
||||||
|
|
||||||
$out .= "<div id='$id' class='$options[class]'$style></div>";
|
|
||||||
|
|
||||||
$out .= "<script type='text/javascript'>" .
|
|
||||||
"if(typeof google === 'undefined' || typeof google.maps === 'undefined') { " .
|
|
||||||
"alert('MarkupGoogleMap Error: Please add the maps.google.com script in your document head.'); " .
|
|
||||||
"} else { " .
|
|
||||||
"var $id = new MarkupGoogleMap(); " .
|
|
||||||
"$id.setOption('zoom', $zoom); " .
|
|
||||||
"$id.setOption('mapTypeId', google.maps.MapTypeId.$type); " .
|
|
||||||
($options['icon'] ? "$id.setIcon('$options[icon]'); " : "") .
|
|
||||||
($options['iconHover'] ? "$id.setIconHover('$options[iconHover]'); " : "") .
|
|
||||||
($options['shadow'] ? "$id.setShadow('$options[shadow]'); " : "") .
|
|
||||||
($options['useHoverBox'] ? "$id.setHoverBox('" . str_replace("'", '"', $options['hoverBoxMarkup']) . "');" : "") .
|
|
||||||
$options['init'] .
|
|
||||||
"$id.init('$id', $lat, $lng); ";
|
|
||||||
|
|
||||||
foreach($pageArray as $page) {
|
|
||||||
$marker = $page->get($fieldName);
|
|
||||||
if(!$marker instanceof MapMarker) continue;
|
|
||||||
if(!$marker->lat) continue;
|
|
||||||
$url = $options['markerLinkField'] ? $page->get($options['markerLinkField']) : '';
|
|
||||||
$title = $options['markerTitleField'] ? $page->get($options['markerTitleField']) : '';
|
|
||||||
$out .= "$id.addMarker($marker->lat, $marker->lng, '$url', '$title', ''); ";
|
|
||||||
}
|
|
||||||
|
|
||||||
if(count($pageArray) > 1 && $options['fitToMarkers']) $out .= "$id.fitToMarkers(); ";
|
|
||||||
$out .= "}</script>";
|
|
||||||
|
|
||||||
return $out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,147 +0,0 @@
|
||||||
# FieldtypeMapMarker Module for ProcessWire
|
|
||||||
|
|
||||||
This Fieldtype for ProcessWire holds an address or location name, and automatically
|
|
||||||
geocodes the address to latitude/longitude using Google Maps API. The resulting
|
|
||||||
values may be used to populate any kind of map (whether Google Maps or another).
|
|
||||||
|
|
||||||
This Fieldtype was created to serve as an example of creating a custom Fieldtype and
|
|
||||||
Inputfield that contains multiple pieces of data. Though the Fieldtype has now gone
|
|
||||||
far beyond that and is relatively full featured. As a result, it may no longer be
|
|
||||||
the simplest example of how to implement a Fieldtype/Inputfield, though it is very
|
|
||||||
effective and useful.
|
|
||||||
|
|
||||||
MapMarker also has a corresponding Inputfield and Markup module, named
|
|
||||||
InputfieldMapMarker and MarkupGoogleMap. When you install FieldtypeMapMarker, the
|
|
||||||
Inputfield will also be installed and used for input on the admin side. Installation
|
|
||||||
of MarkupGoogleMap is optional. It provides a simple way to render Google maps with
|
|
||||||
the data managed by FieldtypeMapMarker.
|
|
||||||
|
|
||||||
This Fieldtype has a [support forum](http://processwire.com/talk/index.php/topic,752.0.html)
|
|
||||||
|
|
||||||
## Using Map Marker
|
|
||||||
|
|
||||||
### How to install
|
|
||||||
|
|
||||||
1. Copy all of the files for this module into /site/modules/FieldtypeMapMarker/
|
|
||||||
|
|
||||||
2. In your admin, go to the Modules screen and "check for new modules." Click *install*
|
|
||||||
for the Map Marker Fieldtype.
|
|
||||||
|
|
||||||
3. In your admin, go to Setup > Fields > Add New Field. Choose MapMarker as the type.
|
|
||||||
If you are not sure what to name your field, simply "map" is a good one! Once created,
|
|
||||||
configure the settings on the *input* tab.
|
|
||||||
|
|
||||||
4. Add your new "map" field to one or more templates, as you would any other field.
|
|
||||||
|
|
||||||
### How to use from the page editor
|
|
||||||
|
|
||||||
1. Create or edit a page using one of the templates you added the "map" field to.
|
|
||||||
|
|
||||||
2. Type in a location or address into the "address" box for the map field. Then click
|
|
||||||
outside of the address, and the Javascript geocoder should automatically populate the
|
|
||||||
latitude, longitude and map location. The Google geocoder will accept full addresses
|
|
||||||
or known location names. For instance, you could type in "Disney Land" and it knows
|
|
||||||
how to find locations like that.
|
|
||||||
|
|
||||||
3. The geocoding also works in reverse. You may drag the map marker wherever you want
|
|
||||||
and it will populate the address field for you. You may also populate the latitude,
|
|
||||||
longitude and zoom fields manually if you like. Unchecking the box between address
|
|
||||||
and latitude disables the geocoder.
|
|
||||||
|
|
||||||
### How to use from the API, in your template files
|
|
||||||
|
|
||||||
In your template files, you can utilize this data for your own Google Maps (or anything
|
|
||||||
else that you might need latitude/longitude for).
|
|
||||||
|
|
||||||
Lets assume that your field is called 'map'. Here is how you would access the
|
|
||||||
components of it from the API:
|
|
||||||
```````````
|
|
||||||
echo $page->map->address; // outputs the address you entered
|
|
||||||
echo $page->map->lat; // outputs the latitude
|
|
||||||
echo $page->map->lng; // outputs the longitude
|
|
||||||
echo $page->map->zoom; // outputs the zoom level
|
|
||||||
`````````
|
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
## Markup Google Map
|
|
||||||
|
|
||||||
This package also comes with a module called MarkupGoogleMap. It provides a simple means
|
|
||||||
of outputting a Google Map based on the data managed by FieldtypeMapMarker. To install,
|
|
||||||
simply click "install" for the Google Maps (Markup) module. This is a Markup module,
|
|
||||||
meaning it exists primarily to generate markup for output on the front-end of your site.
|
|
||||||
|
|
||||||
### How to use
|
|
||||||
|
|
||||||
Add this somewhere before your closing `</head>` tag:
|
|
||||||
`````````
|
|
||||||
<script type='text/javascript' src='https://maps.googleapis.com/maps/api/js?sensor=false'></script>
|
|
||||||
`````````
|
|
||||||
|
|
||||||
In the location where you want to output your map, place the following in your template file:
|
|
||||||
`````````
|
|
||||||
$map = $modules->get('MarkupGoogleMap');
|
|
||||||
echo $map->render($page, 'map');
|
|
||||||
`````````
|
|
||||||
In the above, $page is the Page object that has the 'map' field. Rreplace 'map' with the name of
|
|
||||||
your FieldtypeMap field
|
|
||||||
|
|
||||||
To render a map with multiple markers on it, specify a PageArray rather than a single $page:
|
|
||||||
`````````
|
|
||||||
$items = $pages->find("template=something, map!='', sort=title");
|
|
||||||
$map = $modules->get('MarkupGoogleMap');
|
|
||||||
echo $map->render($items, 'map');
|
|
||||||
`````````
|
|
||||||
|
|
||||||
To specify options, provide a 3rd argument with an options array:
|
|
||||||
`````````
|
|
||||||
$map = $modules->get('MarkupGoogleMap');
|
|
||||||
echo $map->render($items, 'map', array('height' => '500px'));
|
|
||||||
`````````
|
|
||||||
|
|
||||||
### Options
|
|
||||||
|
|
||||||
Here is a list of all possible options (with defaults shown):
|
|
||||||
|
|
||||||
`width`
|
|
||||||
Width of the map (type: string; default: 100%).
|
|
||||||
|
|
||||||
`height`
|
|
||||||
Height of the map (type: string; default: 300px)
|
|
||||||
|
|
||||||
`zoom`
|
|
||||||
Zoom level 1-25 (type: integer; default: from your field settings)
|
|
||||||
|
|
||||||
`type`
|
|
||||||
Map type: ROADMAP, HYBRID or SATELLITE (type: string; default: from your field settings)
|
|
||||||
|
|
||||||
`id`
|
|
||||||
Map ID attribute (type: string; default: mgmap)
|
|
||||||
|
|
||||||
`class`
|
|
||||||
Map class attribute (type: string; default: MarkupGoogleMap)
|
|
||||||
|
|
||||||
`lat`
|
|
||||||
Map center latitude (type: string|float; default: from your field settings)
|
|
||||||
|
|
||||||
`lng`
|
|
||||||
Map center longitude (type: string|float; default: from your field settings)
|
|
||||||
|
|
||||||
`useStyles`
|
|
||||||
Whether to populate inline styles to the map div for width/height (type: boolean; default: true).
|
|
||||||
Set to false only if you will style the map div yourself.
|
|
||||||
|
|
||||||
`useMarkerSettings`
|
|
||||||
Makes single-marker map use marker settings rather than map settings (type: boolean; default: true).
|
|
||||||
|
|
||||||
`markerLinkField`
|
|
||||||
Page field to use for the marker link, or blank to not link (type: string; default: url).
|
|
||||||
|
|
||||||
`markerTitleField`
|
|
||||||
Page field to use for the marker title, or blank not to use a marker title (type: string; default: title).
|
|
||||||
|
|
||||||
`fitToMarkers`
|
|
||||||
When multiple markers are present, set map to automatically adjust to fit to the given markers (type: boolean; default: true).
|
|
||||||
|
|
||||||
---------
|
|
||||||
|
|
|
@ -1,299 +0,0 @@
|
||||||
<?php namespace ProcessWire;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ProcessWire Map Marker Fieldtype
|
|
||||||
*
|
|
||||||
* Holds an address and geocodes it to latitude and longitude via Google Maps
|
|
||||||
*
|
|
||||||
* For documentation about the fields used in this class, please see:
|
|
||||||
* /wire/core/Fieldtype.php
|
|
||||||
*
|
|
||||||
* ProcessWire 3.x
|
|
||||||
* Copyright (C) 2023 by Ryan Cramer
|
|
||||||
* Licensed under MPL 2.0
|
|
||||||
*
|
|
||||||
* https://processwire.com
|
|
||||||
*
|
|
||||||
* @todo implement a getMatchQuery method and support LIKE with address.
|
|
||||||
*
|
|
||||||
* @property string $googleApiKey
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
class FieldtypeMapMarker extends Fieldtype implements ConfigurableModule {
|
|
||||||
|
|
||||||
public static function getModuleInfo() {
|
|
||||||
return array(
|
|
||||||
'title' => 'Map Marker',
|
|
||||||
'version' => 300,
|
|
||||||
'summary' => 'Field that stores an address with latitude and longitude coordinates and has built-in geocoding capability with Google Maps API.',
|
|
||||||
'installs' => 'InputfieldMapMarker',
|
|
||||||
'icon' => 'map-marker',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Include our MapMarker class, which serves as the value for fields of type FieldtypeMapMarker
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
require_once(dirname(__FILE__) . '/MapMarker.php');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function set($key, $value) {
|
|
||||||
/*
|
|
||||||
if($key === 'googleApiKey' && strpos($this->wire('config')->httpHost, 'localhost') !== false) {
|
|
||||||
// disable API key when in localhost environment
|
|
||||||
if($this->wire('page')->template == 'admin') $value = '';
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
return parent::set($key, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getGoogleMapsURL() {
|
|
||||||
$url = 'https://maps.google.com/maps/api/js';
|
|
||||||
$key = $this->get('googleApiKey');
|
|
||||||
if($key) $url .= "?key=$key";
|
|
||||||
return $url;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the Inputfield required by this Fieldtype
|
|
||||||
*
|
|
||||||
* @param Page $page
|
|
||||||
* @param Field $field
|
|
||||||
* @return InputfieldMapMarker
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getInputfield(Page $page, Field $field) {
|
|
||||||
/** @var InputfieldMapMarker $inputfield */
|
|
||||||
$inputfield = $this->wire()->modules->get('InputfieldMapMarker');
|
|
||||||
$inputfield->set('googleApiKey', $this->get('googleApiKey'));
|
|
||||||
return $inputfield;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all compatible Fieldtypes
|
|
||||||
*
|
|
||||||
* @param Field $field
|
|
||||||
* @return null
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function ___getCompatibleFieldtypes(Field $field) {
|
|
||||||
// there are no other fieldtypes compatible with this one
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sanitize value for runtime
|
|
||||||
*
|
|
||||||
* @param Page $page
|
|
||||||
* @param Field $field
|
|
||||||
* @param MapMarker $value
|
|
||||||
* @return MapMarker
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function sanitizeValue(Page $page, Field $field, $value) {
|
|
||||||
|
|
||||||
// if it's not a MapMarker, then just return a blank MapMarker
|
|
||||||
if(!$value instanceof MapMarker) $value = $this->getBlankValue($page, $field);
|
|
||||||
|
|
||||||
// if the address changed, tell the $page that this field changed
|
|
||||||
if($value->isChanged('address')) $page->trackChange($field->name);
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a blank value used by this fieldtype
|
|
||||||
*
|
|
||||||
* @param Page $page
|
|
||||||
* @param Field $field
|
|
||||||
* @return MapMarker
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getBlankValue(Page $page, Field $field) {
|
|
||||||
$value = new MapMarker();
|
|
||||||
$this->wire($value);
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a raw value (value as stored in DB), return the value as it would appear in a Page object
|
|
||||||
*
|
|
||||||
* @param Page $page
|
|
||||||
* @param Field $field
|
|
||||||
* @param string|int|array $value
|
|
||||||
* @return MapMarker
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function ___wakeupValue(Page $page, Field $field, $value) {
|
|
||||||
|
|
||||||
// get a blank MapMarker instance
|
|
||||||
$marker = $this->getBlankValue($page, $field);
|
|
||||||
|
|
||||||
if("$value[lat]" === "0") $value['lat'] = '';
|
|
||||||
if("$value[lng]" === "0") $value['lng'] = '';
|
|
||||||
|
|
||||||
// populate the marker
|
|
||||||
$marker->address = $value['data'];
|
|
||||||
$marker->lat = $value['lat'];
|
|
||||||
$marker->lng = $value['lng'];
|
|
||||||
$marker->status = $value['status'];
|
|
||||||
$marker->zoom = $value['zoom'];
|
|
||||||
$marker->setTrackChanges(true);
|
|
||||||
|
|
||||||
return $marker;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given an 'awake' value, as set by wakeupValue, convert the value back to a basic type for storage in DB.
|
|
||||||
*
|
|
||||||
* @param Page $page
|
|
||||||
* @param Field $field
|
|
||||||
* @param string|int|array|object $value
|
|
||||||
* @return array
|
|
||||||
* @throws WireException
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function ___sleepValue(Page $page, Field $field, $value) {
|
|
||||||
|
|
||||||
$marker = $value;
|
|
||||||
|
|
||||||
if(!$marker instanceof MapMarker) {
|
|
||||||
throw new WireException("Expecting an instance of MapMarker");
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the address was changed, then force it to geocode the new address
|
|
||||||
if($marker->isChanged('address') && $marker->address && $marker->status != MapMarker::statusNoGeocode) {
|
|
||||||
$marker->geocode();
|
|
||||||
}
|
|
||||||
|
|
||||||
$sleepValue = array(
|
|
||||||
'data' => $marker->address,
|
|
||||||
'lat' => strlen($marker->lat) ? $marker->lat : 0,
|
|
||||||
'lng' => strlen($marker->lng) ? $marker->lng : 0,
|
|
||||||
'status' => $marker->status,
|
|
||||||
'zoom' => $marker->zoom
|
|
||||||
);
|
|
||||||
|
|
||||||
return $sleepValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the database schema in specified format
|
|
||||||
*
|
|
||||||
* @param Field $field
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getDatabaseSchema(Field $field) {
|
|
||||||
|
|
||||||
// get the default schema
|
|
||||||
$schema = parent::getDatabaseSchema($field);
|
|
||||||
|
|
||||||
$schema['data'] = "VARCHAR(255) NOT NULL DEFAULT ''"; // address (reusing the 'data' field from default schema)
|
|
||||||
$schema['lat'] = "FLOAT(10,6) NOT NULL DEFAULT 0"; // latitude
|
|
||||||
$schema['lng'] = "FLOAT(10,6) NOT NULL DEFAULT 0"; // longitude
|
|
||||||
$schema['status'] = "TINYINT NOT NULL DEFAULT 0"; // geocode status
|
|
||||||
$schema['zoom'] = "TINYINT NOT NULL DEFAULT 0"; // zoom level (schema v1)
|
|
||||||
|
|
||||||
$schema['keys']['latlng'] = "KEY latlng (lat, lng)"; // keep an index of lat/lng
|
|
||||||
$schema['keys']['data'] = 'FULLTEXT KEY `data` (`data`)';
|
|
||||||
$schema['keys']['zoom'] = "KEY zoom (zoom)";
|
|
||||||
|
|
||||||
if($field->id) $this->updateDatabaseSchema($field, $schema);
|
|
||||||
|
|
||||||
return $schema;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the DB schema, if necessary
|
|
||||||
*
|
|
||||||
* @param Field $field
|
|
||||||
* @param array $schema
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
protected function updateDatabaseSchema(Field $field, array $schema) {
|
|
||||||
|
|
||||||
$requiredVersion = 1;
|
|
||||||
$schemaVersion = (int) $field->get('schemaVersion');
|
|
||||||
|
|
||||||
if($schemaVersion >= $requiredVersion) {
|
|
||||||
// already up-to-date
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($schemaVersion == 0) {
|
|
||||||
// update schema to v1: add 'zoom' column
|
|
||||||
$schemaVersion = 1;
|
|
||||||
$database = $this->wire()->database;
|
|
||||||
$table = $database->escapeTable($field->getTable());
|
|
||||||
$query = $database->prepare("SHOW TABLES LIKE '$table'");
|
|
||||||
$query->execute();
|
|
||||||
$row = $query->fetch(\PDO::FETCH_NUM);
|
|
||||||
$query->closeCursor();
|
|
||||||
if(!empty($row)) {
|
|
||||||
$query = $database->prepare("SHOW COLUMNS FROM `$table` WHERE field='zoom'");
|
|
||||||
$query->execute();
|
|
||||||
if(!$query->rowCount()) try {
|
|
||||||
$database->exec("ALTER TABLE `$table` ADD zoom $schema[zoom] AFTER status");
|
|
||||||
$this->message("Added 'zoom' column to '$field->table'");
|
|
||||||
} catch(\Exception $e) {
|
|
||||||
$this->error($e->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$field->set('schemaVersion', $schemaVersion);
|
|
||||||
$field->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Match values for PageFinder
|
|
||||||
*
|
|
||||||
* @param PageFinderDatabaseQuerySelect|DatabaseQuerySelect $query
|
|
||||||
* @param string $table
|
|
||||||
* @param string $subfield
|
|
||||||
* @param string $operator
|
|
||||||
* @param string $value
|
|
||||||
* @return DatabaseQuerySelect
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getMatchQuery($query, $table, $subfield, $operator, $value) {
|
|
||||||
if(!$subfield || $subfield == 'address') $subfield = 'data';
|
|
||||||
if($subfield != 'data' || $this->wire()->database->isOperator($operator)) {
|
|
||||||
// if dealing with something other than address, or operator is native to SQL,
|
|
||||||
// then let Fieldtype::getMatchQuery handle it instead
|
|
||||||
return parent::getMatchQuery($query, $table, $subfield, $operator, $value);
|
|
||||||
}
|
|
||||||
// if we get here, then we're performing either %= (LIKE and variations) or *= (FULLTEXT and variations)
|
|
||||||
$ft = new DatabaseQuerySelectFulltext($query);
|
|
||||||
$ft->match($table, $subfield, $operator, $value);
|
|
||||||
return $query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module configuration
|
|
||||||
*
|
|
||||||
* @param array $data
|
|
||||||
* @return InputfieldWrapper
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public static function getModuleConfigInputfields(array $data) {
|
|
||||||
$inputfields = new InputfieldWrapper();
|
|
||||||
if(wire()->config->demo) $data['googleApiKey'] = 'Not shown in demo mode';
|
|
||||||
/** @var InputfieldText $f */
|
|
||||||
$f = wire()->modules->get('InputfieldText');
|
|
||||||
$f->attr('name', 'googleApiKey');
|
|
||||||
$f->label = __('Google Maps API Key');
|
|
||||||
$f->icon = 'map';
|
|
||||||
$f->description = sprintf(__('[Click here](%s) for instructions from Google on how to obtain an API key.'),
|
|
||||||
'https://developers.google.com/maps/documentation/javascript/get-api-key');
|
|
||||||
$f->attr('value', isset($data['googleApiKey']) ? $data['googleApiKey'] : '');
|
|
||||||
$inputfields->add($f);
|
|
||||||
return $inputfields;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
.InputfieldMapMarker input[type=number],
|
|
||||||
.InputfieldMapMarker input[type=text] {
|
|
||||||
width: 99.5%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerToggle span {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerAddress {
|
|
||||||
float: left;
|
|
||||||
width: 70%;
|
|
||||||
padding-right: 2%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerToggle {
|
|
||||||
float: left;
|
|
||||||
width: 28%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerLat,
|
|
||||||
.InputfieldMapMarkerLng {
|
|
||||||
width: 42%;
|
|
||||||
float: left;
|
|
||||||
padding-right: 2%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerZoom {
|
|
||||||
float: left;
|
|
||||||
width: 10%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarker .notes {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerMap {
|
|
||||||
width: 100%;
|
|
||||||
height: 300px;
|
|
||||||
clear: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (min-width: 768px) {
|
|
||||||
|
|
||||||
.InputfieldMapMarkerAddress {
|
|
||||||
width: 38%;
|
|
||||||
padding-right: 1%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerToggle {
|
|
||||||
width: 2%;
|
|
||||||
padding-right: 0.5%;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerToggle strong {
|
|
||||||
/* hide geocode label */
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerLat,
|
|
||||||
.InputfieldMapMarkerLng {
|
|
||||||
width: 23%;
|
|
||||||
padding-right: 1%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.InputfieldMapMarkerZoom {
|
|
||||||
float: left;
|
|
||||||
width: 9.5%;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,130 +0,0 @@
|
||||||
/**
|
|
||||||
* Display a Google Map and pinpoint a location for InputfieldMapMarker
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
var InputfieldMapMarker = {
|
|
||||||
|
|
||||||
options: {
|
|
||||||
zoom: 12, // mats, previously 5
|
|
||||||
draggable: true, // +mats
|
|
||||||
center: null,
|
|
||||||
//key: config.InputfieldMapMarker.googleApiKey,
|
|
||||||
mapTypeId: google.maps.MapTypeId.HYBRID,
|
|
||||||
scrollwheel: false,
|
|
||||||
mapTypeControlOptions: {
|
|
||||||
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
|
|
||||||
},
|
|
||||||
scaleControl: false
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function(mapId, lat, lng, zoom, mapType) {
|
|
||||||
|
|
||||||
var options = InputfieldMapMarker.options;
|
|
||||||
|
|
||||||
if(zoom < 1) zoom = 12;
|
|
||||||
options.center = new google.maps.LatLng(lat, lng);
|
|
||||||
options.zoom = parseInt(zoom);
|
|
||||||
|
|
||||||
if(mapType == 'SATELLITE') options.mapTypeId = google.maps.MapTypeId.SATELLITE;
|
|
||||||
else if(mapType == 'ROADMAP') options.mapTypeId = google.maps.MapTypeId.ROADMAP;
|
|
||||||
|
|
||||||
var map = new google.maps.Map(document.getElementById(mapId), options);
|
|
||||||
|
|
||||||
var marker = new google.maps.Marker({
|
|
||||||
position: options.center,
|
|
||||||
map: map,
|
|
||||||
draggable: options.draggable
|
|
||||||
});
|
|
||||||
|
|
||||||
var $map = $('#' + mapId);
|
|
||||||
var $lat = $map.siblings(".InputfieldMapMarkerLat").find("input[type=text]");
|
|
||||||
var $lng = $map.siblings(".InputfieldMapMarkerLng").find("input[type=text]");
|
|
||||||
var $addr = $map.siblings(".InputfieldMapMarkerAddress").find("input[type=text]");
|
|
||||||
var $addrJS = $map.siblings(".InputfieldMapMarkerAddress").find("input[type=hidden]");
|
|
||||||
var $toggle = $map.siblings(".InputfieldMapMarkerToggle").find("input[type=checkbox]");
|
|
||||||
var $zoom = $map.siblings(".InputfieldMapMarkerZoom").find("input[type=number]");
|
|
||||||
var $notes = $map.siblings(".notes");
|
|
||||||
|
|
||||||
$lat.val(marker.getPosition().lat());
|
|
||||||
$lng.val(marker.getPosition().lng());
|
|
||||||
$zoom.val(map.getZoom());
|
|
||||||
|
|
||||||
google.maps.event.addListener(marker, 'dragend', function(event) {
|
|
||||||
var geocoder = new google.maps.Geocoder();
|
|
||||||
var position = this.getPosition();
|
|
||||||
$lat.val(position.lat());
|
|
||||||
$lng.val(position.lng());
|
|
||||||
if($toggle.is(":checked")) {
|
|
||||||
geocoder.geocode({ 'latLng': position }, function(results, status) {
|
|
||||||
if(status == google.maps.GeocoderStatus.OK && results[0]) {
|
|
||||||
$addr.val(results[0].formatted_address);
|
|
||||||
$addrJS.val($addr.val());
|
|
||||||
}
|
|
||||||
$notes.text(status);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
google.maps.event.addListener(map, 'zoom_changed', function() {
|
|
||||||
$zoom.val(map.getZoom());
|
|
||||||
});
|
|
||||||
|
|
||||||
$addr.blur(function() {
|
|
||||||
if(!$toggle.is(":checked")) return true;
|
|
||||||
var geocoder = new google.maps.Geocoder();
|
|
||||||
geocoder.geocode({ 'address': $(this).val()}, function(results, status) {
|
|
||||||
if(status == google.maps.GeocoderStatus.OK && results[0]) {
|
|
||||||
var position = results[0].geometry.location;
|
|
||||||
map.setCenter(position);
|
|
||||||
marker.setPosition(position);
|
|
||||||
$lat.val(position.lat());
|
|
||||||
$lng.val(position.lng());
|
|
||||||
$addrJS.val($addr.val());
|
|
||||||
}
|
|
||||||
$notes.text(status);
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
$zoom.change(function() {
|
|
||||||
map.setZoom(parseInt($(this).val()));
|
|
||||||
});
|
|
||||||
|
|
||||||
$toggle.click(function() {
|
|
||||||
if($(this).is(":checked")) {
|
|
||||||
$notes.text('Geocode ON');
|
|
||||||
// google.maps.event.trigger(marker, 'dragend');
|
|
||||||
$addr.trigger('blur');
|
|
||||||
} else {
|
|
||||||
$notes.text('Geocode OFF');
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
// added by diogo to solve the problem of maps not rendering correctly in hidden elements
|
|
||||||
// trigger a resize on the map when either the tab button or the toggle field bar are pressed
|
|
||||||
|
|
||||||
// get the tab element where this map is integrated
|
|
||||||
var $map = $('#' + mapId);
|
|
||||||
var $tab = $('#_' + $map.closest('.InputfieldFieldsetTabOpen').attr('id'));
|
|
||||||
// get the inputfield where this map is integrated and add the tab to the stack
|
|
||||||
var $inputFields = $map.closest('.Inputfield').find('.InputfieldStateToggle').add($tab);
|
|
||||||
|
|
||||||
$inputFields.on('click',function(){
|
|
||||||
// give it time to open
|
|
||||||
window.setTimeout(function(){
|
|
||||||
google.maps.event.trigger(map,'resize');
|
|
||||||
map.setCenter(options.center);
|
|
||||||
}, 200);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
|
||||||
$(".InputfieldMapMarkerMap").each(function() {
|
|
||||||
var $t = $(this);
|
|
||||||
InputfieldMapMarker.init($t.attr('id'), $t.attr('data-lat'), $t.attr('data-lng'), $t.attr('data-zoom'), $t.attr('data-type'));
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,377 +0,0 @@
|
||||||
<?php namespace ProcessWire;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ProcessWire Map Marker Inputfield
|
|
||||||
*
|
|
||||||
* Provides the admin control panel inputs for FieldtypeMapMarker
|
|
||||||
*
|
|
||||||
* ProcessWire 3.x
|
|
||||||
* Copyright (C) 2023 by Ryan Cramer
|
|
||||||
* Licensed under MPL 2.0
|
|
||||||
*
|
|
||||||
* https://processwire.com
|
|
||||||
*
|
|
||||||
* @property string $defaultAddr
|
|
||||||
* @property int $defaultZoom
|
|
||||||
* @property string $defaultType
|
|
||||||
* @property string $defaultLat
|
|
||||||
* @property string $defaultLng
|
|
||||||
* @property int $height
|
|
||||||
* @property string $googleApiKey
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
class InputfieldMapMarker extends Inputfield {
|
|
||||||
|
|
||||||
public static function getModuleInfo() {
|
|
||||||
return array(
|
|
||||||
'title' => 'Map Marker',
|
|
||||||
'version' => 300,
|
|
||||||
'summary' => "Provides input for the MapMarker Fieldtype",
|
|
||||||
'requires' => 'FieldtypeMapMarker',
|
|
||||||
'icon' => 'map-marker',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultAddr = 'Castaway Cay';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Just in case this Inputfield is being used separately from FieldtypeMapmarker, we include the MapMarker class
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function __construct() {
|
|
||||||
require_once(dirname(__FILE__) . '/MapMarker.php');
|
|
||||||
$this->set('defaultAddr', self::defaultAddr);
|
|
||||||
$this->set('defaultZoom', 12);
|
|
||||||
$this->set('defaultType', 'HYBRID');
|
|
||||||
$this->set('defaultLat', '');
|
|
||||||
$this->set('defaultLng', '');
|
|
||||||
$this->set('height', 300);
|
|
||||||
$this->set('googleApiKey', '');
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set an attribute to this Inputfield
|
|
||||||
*
|
|
||||||
* In this case, we just capture the 'value' attribute and make sure it's something valid
|
|
||||||
*
|
|
||||||
* @param string $key
|
|
||||||
* @param mixed $value
|
|
||||||
* @return $this
|
|
||||||
* @throws WireException
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function setAttribute($key, $value) {
|
|
||||||
|
|
||||||
if($key == 'value' && !$value instanceof MapMarker && !is_null($value)) {
|
|
||||||
throw new WireException("This input only accepts a MapMarker for its value");
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::setAttribute($key, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is the value empty?
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function isEmpty() {
|
|
||||||
return (!$this->value || ((float) $this->value->lat) === 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return FieldtypeMapMarker
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function fieldtype() {
|
|
||||||
/** @var FieldtypeMapMarker $fieldtype */
|
|
||||||
$fieldtype = $this->wire()->modules->get('FieldtypeMapMarker');
|
|
||||||
return $fieldtype;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderReady(Inputfield $parent = null, $renderValueMode = false) {
|
|
||||||
/*
|
|
||||||
$url = 'https://maps.google.com/maps/api/js';
|
|
||||||
$key = $this->get('googleApiKey');
|
|
||||||
if($key) $url .= "?key=$key";
|
|
||||||
*/
|
|
||||||
$url = $this->fieldtype()->getGoogleMapsURL();
|
|
||||||
$this->wire()->config->scripts->add($url);
|
|
||||||
return parent::renderReady($parent, $renderValueMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the markup needed to draw the Inputfield
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function ___render() {
|
|
||||||
|
|
||||||
$sanitizer = $this->wire()->sanitizer;
|
|
||||||
$adminTheme = $this->wire()->adminTheme;
|
|
||||||
|
|
||||||
$name = $this->attr('name');
|
|
||||||
$id = $this->attr('id');
|
|
||||||
$marker = $this->attr('value');
|
|
||||||
|
|
||||||
if($marker->lat == 0.0) $marker->lat = $this->defaultLat;
|
|
||||||
if($marker->lng == 0.0) $marker->lng = $this->defaultLng;
|
|
||||||
if(!$marker->zoom) $marker->zoom = $this->defaultZoom;
|
|
||||||
|
|
||||||
$address = $sanitizer->entities($marker->address);
|
|
||||||
$toggleChecked = $marker->status != MapMarker::statusNoGeocode ? " checked='checked'" : '';
|
|
||||||
$status = $marker->status == MapMarker::statusNoGeocode ? 0 : $marker->status;
|
|
||||||
$mapType = $this->defaultType;
|
|
||||||
$height = $this->height ? (int) $this->height : 300;
|
|
||||||
$classes = array('input' => '', 'checkbox' => '');
|
|
||||||
|
|
||||||
if($adminTheme && method_exists($adminTheme, 'getClass')) {
|
|
||||||
foreach(array_keys($classes) as $key) {
|
|
||||||
$classes[$key] = $adminTheme->getClass($key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$labels = array(
|
|
||||||
'addr' => $this->_('Address'),
|
|
||||||
'lat' => $this->_('Latitude'),
|
|
||||||
'lng' => $this->_('Longitude'),
|
|
||||||
'geo' => $this->_('Geocode?'),
|
|
||||||
'zoom' => $this->_('Zoom')
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach($labels as $key => $label) {
|
|
||||||
$labels[$key] = $sanitizer->entities1($label);
|
|
||||||
}
|
|
||||||
|
|
||||||
$out = <<< _OUT
|
|
||||||
|
|
||||||
<span></span>
|
|
||||||
|
|
||||||
<p class='InputfieldMapMarkerAddress'>
|
|
||||||
<label>
|
|
||||||
<strong>$labels[addr]</strong>
|
|
||||||
<br />
|
|
||||||
<input type='text' id='{$id}' name='{$name}' value='{$address}' class='$classes[input]' /><br />
|
|
||||||
</label>
|
|
||||||
<input type='hidden' id='_{$name}_js_geocode_address' name='_{$name}_js_geocode_address' value='' />
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class='InputfieldMapMarkerToggle'>
|
|
||||||
<label>
|
|
||||||
<br />
|
|
||||||
<input title='Geocode ON/OFF' type='checkbox' class='$classes[checkbox]' name='_{$name}_status' id='_{$name}_toggle' value='$status'$toggleChecked />
|
|
||||||
<strong>$labels[geo]</strong>
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class='InputfieldMapMarkerLat'>
|
|
||||||
<label>
|
|
||||||
<strong>$labels[lat]</strong><br />
|
|
||||||
<input type='text' id='_{$id}_lat' name='_{$name}_lat' value='{$marker->lat}' class='$classes[input]' />
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class='InputfieldMapMarkerLng'>
|
|
||||||
<label>
|
|
||||||
<strong>$labels[lng]</strong><br />
|
|
||||||
<input type='text' id='_{$id}_lng' name='_{$name}_lng' value='{$marker->lng}' class='$classes[input]' />
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class='InputfieldMapMarkerZoom'>
|
|
||||||
<label>
|
|
||||||
<strong>$labels[zoom]</strong><br />
|
|
||||||
<input type='number' min='0' id='_{$id}_zoom' name='_{$name}_zoom' value='{$marker->zoom}' class='$classes[input]' />
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
|
||||||
_OUT;
|
|
||||||
|
|
||||||
$out .=
|
|
||||||
"<div class='InputfieldMapMarkerMap' " .
|
|
||||||
"id='_{$id}_map' " .
|
|
||||||
"style='height: {$height}px' " .
|
|
||||||
"data-lat='$marker->lat' " .
|
|
||||||
"data-lng='$marker->lng' " .
|
|
||||||
"data-zoom='$marker->zoom' " .
|
|
||||||
"data-type='$mapType'>" .
|
|
||||||
"</div>";
|
|
||||||
|
|
||||||
$this->notes = $marker->statusString;
|
|
||||||
|
|
||||||
if(!$this->get('googleApiKey')) {
|
|
||||||
$config = $this->wire()->config;
|
|
||||||
$msg = $sanitizer->entities1($this->_('Please setup a Google Maps API key in the FieldtypeMapMarker module settings'));
|
|
||||||
if($this->wire()->user->isSuperuser()) {
|
|
||||||
$link = "<a href='{$config->urls->admin}module/edit?name=FieldtypeMapMarker'>";
|
|
||||||
$msg = "$link$msg</a>";
|
|
||||||
$this->warning($msg, Notice::allowMarkup);
|
|
||||||
} else {
|
|
||||||
$this->warning($msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process the input after a form submission
|
|
||||||
*
|
|
||||||
* @param WireInputData $input
|
|
||||||
* @return $this
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function ___processInput(WireInputData $input) {
|
|
||||||
|
|
||||||
$name = $this->attr('name');
|
|
||||||
$marker = $this->attr('value');
|
|
||||||
|
|
||||||
if(!isset($input->$name)) {
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($input->$name == $this->defaultAddr) {
|
|
||||||
$marker->set('address', '');
|
|
||||||
} else {
|
|
||||||
$marker->set('address', $input->$name);
|
|
||||||
}
|
|
||||||
|
|
||||||
$lat = (float) $input["_{$name}_lat"];
|
|
||||||
$lng = (float) $input["_{$name}_lng"];
|
|
||||||
$defaultLat = (float) $this->defaultLat;
|
|
||||||
$defaultLng = (float) $this->defaultLng;
|
|
||||||
$precision = 4;
|
|
||||||
|
|
||||||
if( ((string) round($lat, $precision)) != ((string) round($defaultLat, $precision)) ||
|
|
||||||
((string) round($lng, $precision)) != ((string) round($defaultLng, $precision))) {
|
|
||||||
$marker->set('lat', $lat);
|
|
||||||
$marker->set('lng', $lng);
|
|
||||||
} else {
|
|
||||||
// $this->message("Kept lat/lng at unset value", Notice::debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
$zoom = $input["_{$name}_zoom"];
|
|
||||||
if($zoom > -1 && $zoom < 30) $marker->zoom = (int) $zoom;
|
|
||||||
|
|
||||||
$status = $input["_{$name}_status"];
|
|
||||||
if(is_null($status)) {
|
|
||||||
$marker->set('status', MapMarker::statusNoGeocode); // disable geocode
|
|
||||||
} else {
|
|
||||||
$marker->set('status', (int) $status);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the address changed, then redo the geocoding.
|
|
||||||
// while we do this in the Fieldtype, we also do it here in case this Inputfield is used on it's own.
|
|
||||||
// the MapMarker class checks to make sure it doesn't do the same geocode twice.
|
|
||||||
if($marker->isChanged('address') && $marker->address && $marker->status != MapMarker::statusNoGeocode) {
|
|
||||||
// double check that the address wasn't already populated by the JS geocoder
|
|
||||||
// this prevents user-dragged markers that don't geocode to an exact location from getting
|
|
||||||
// unintentionally moved by the PHP-side geocoder
|
|
||||||
if($input["_{$name}_js_geocode_address"] == $marker->address) {
|
|
||||||
// prevent the geocoder from running in the fieldtype
|
|
||||||
$marker->skipGeocode = true;
|
|
||||||
$this->message('Skipping geocode (already done by JS geocoder)', Notice::debug);
|
|
||||||
} else {
|
|
||||||
$marker->geocode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return InputfieldWrapper
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function ___getConfigInputfields() {
|
|
||||||
$modules = $this->wire()->modules;
|
|
||||||
$inputfields = parent::___getConfigInputfields();
|
|
||||||
|
|
||||||
/** @var InputfieldText $field */
|
|
||||||
$field = $modules->get('InputfieldText');
|
|
||||||
$field->attr('name', 'defaultAddr');
|
|
||||||
$field->label = $this->_('Default Address');
|
|
||||||
$field->description = $this->_('This will be geocoded to become the starting point of the map.');
|
|
||||||
$field->attr('value', $this->defaultAddr);
|
|
||||||
$field->notes = $this->_('When modifying the default address, please make the Latitude and Longitude fields below blank, which will force the system to geocode your new address.');
|
|
||||||
$inputfields->add($field);
|
|
||||||
|
|
||||||
if(!$this->defaultLat && !$this->defaultLng) {
|
|
||||||
$m = new MapMarker();
|
|
||||||
$m->address = $this->defaultAddr;
|
|
||||||
$status = $m->geocode();
|
|
||||||
if($status > 0) {
|
|
||||||
$this->defaultLat = $m->lat;
|
|
||||||
$this->defaultLng = $m->lng;
|
|
||||||
$this->message($this->_('Geocoded your default address. Please hit save once again to commit the new default latitude and longitude.'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var InputfieldText $field */
|
|
||||||
$field = $modules->get('InputfieldText');
|
|
||||||
$field->attr('name', 'defaultLat');
|
|
||||||
$field->label = $this->_('Default Latitude');
|
|
||||||
$field->attr('value', $this->defaultLat);
|
|
||||||
$field->columnWidth = 50;
|
|
||||||
$inputfields->add($field);
|
|
||||||
|
|
||||||
/** @var InputfieldText $field */
|
|
||||||
$field = $modules->get('InputfieldText');
|
|
||||||
$field->attr('name', 'defaultLng');
|
|
||||||
$field->label = $this->_('Default Longitude');
|
|
||||||
$field->attr('value', $this->defaultLng);
|
|
||||||
$field->columnWidth = 50;
|
|
||||||
$inputfields->add($field);
|
|
||||||
|
|
||||||
/** @var InputfieldRadios $field */
|
|
||||||
$field = $modules->get('InputfieldRadios');
|
|
||||||
$field->attr('name', 'defaultType');
|
|
||||||
$field->label = $this->_('Default Map Type');
|
|
||||||
$field->addOption('HYBRID', $this->_('Hybrid'));
|
|
||||||
$field->addOption('ROADMAP', $this->_('Road Map'));
|
|
||||||
$field->addOption('SATELLITE', $this->_('Satellite'));
|
|
||||||
$field->attr('value', $this->defaultType);
|
|
||||||
$field->optionColumns = 1;
|
|
||||||
$field->columnWidth = 50;
|
|
||||||
$inputfields->add($field);
|
|
||||||
|
|
||||||
/** @var InputfieldInteger $field */
|
|
||||||
$field = $modules->get('InputfieldInteger');
|
|
||||||
$field->attr('name', 'height');
|
|
||||||
$field->label = $this->_('Map Height (in pixels)');
|
|
||||||
$field->attr('value', $this->height);
|
|
||||||
$field->attr('type', 'number');
|
|
||||||
$field->columnWidth = 50;
|
|
||||||
$inputfields->add($field);
|
|
||||||
|
|
||||||
/** @var InputfieldInteger $field */
|
|
||||||
$field = $modules->get('InputfieldInteger');
|
|
||||||
$field->attr('name', 'defaultZoom');
|
|
||||||
$field->label = $this->_('Default Zoom');
|
|
||||||
$field->description = $this->_('Enter a value between 1 and 23. The highest zoom level is typically somewhere between 19-23, depending on the location being zoomed and how much data Google has for the location.'); // Zoom level description
|
|
||||||
$field->attr('value', $this->defaultZoom);
|
|
||||||
$field->attr('type', 'number');
|
|
||||||
$inputfields->add($field);
|
|
||||||
|
|
||||||
/** @var InputfieldMarkup $field */
|
|
||||||
$field = $modules->get('InputfieldMarkup');
|
|
||||||
$field->label = $this->_('API Notes');
|
|
||||||
$field->description = $this->_('You can access individual values from this field using the following from your template files:');
|
|
||||||
$field->value =
|
|
||||||
"<pre>" .
|
|
||||||
"\$page->{$this->name}->address\n" .
|
|
||||||
"\$page->{$this->name}->lat\n" .
|
|
||||||
"\$page->{$this->name}->lng\n" .
|
|
||||||
"\$page->{$this->name}->zoom" .
|
|
||||||
"</pre>";
|
|
||||||
|
|
||||||
$inputfields->add($field);
|
|
||||||
|
|
||||||
return $inputfields;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,126 +0,0 @@
|
||||||
<?php namespace ProcessWire;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class to hold an address and geocode it to latitude/longitude
|
|
||||||
*
|
|
||||||
* @property string $lat
|
|
||||||
* @property string $lng
|
|
||||||
* @property string $address
|
|
||||||
* @property int $status
|
|
||||||
* @property int $zoom
|
|
||||||
* @property bool $skipGeocode
|
|
||||||
* @property string $statusString
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class MapMarker extends WireData {
|
|
||||||
|
|
||||||
const statusNoGeocode = -100;
|
|
||||||
|
|
||||||
protected $geocodeStatuses = array(
|
|
||||||
0 => 'N/A',
|
|
||||||
1 => 'OK',
|
|
||||||
2 => 'OK_ROOFTOP',
|
|
||||||
3 => 'OK_RANGE_INTERPOLATED',
|
|
||||||
4 => 'OK_GEOMETRIC_CENTER',
|
|
||||||
5 => 'OK_APPROXIMATE',
|
|
||||||
|
|
||||||
-1 => 'UNKNOWN',
|
|
||||||
-2 => 'ZERO_RESULTS',
|
|
||||||
-3 => 'OVER_QUERY_LIMIT',
|
|
||||||
-4 => 'REQUEST_DENIED',
|
|
||||||
-5 => 'INVALID_REQUEST',
|
|
||||||
|
|
||||||
-100 => 'Geocode OFF', // RCD
|
|
||||||
);
|
|
||||||
|
|
||||||
protected $geocodedAddress = '';
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
$this->set('lat', '');
|
|
||||||
$this->set('lng', '');
|
|
||||||
$this->set('address', '');
|
|
||||||
$this->set('status', 0);
|
|
||||||
$this->set('zoom', 0);
|
|
||||||
// temporary runtime property to indicate the geocode should be skipped
|
|
||||||
$this->set('skipGeocode', false);
|
|
||||||
$this->set('statusString', '');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function set($key, $value) {
|
|
||||||
|
|
||||||
if($key == 'lat' || $key == 'lng') {
|
|
||||||
// if value isn't numeric, then it's not valid: make it blank
|
|
||||||
if(strpos($value, ',') !== false) $value = str_replace(',', '.', $value); // convert 123,456 to 123.456
|
|
||||||
if(!is_numeric($value)) $value = '';
|
|
||||||
|
|
||||||
} else if($key == 'address') {
|
|
||||||
$value = $this->wire()->sanitizer->text($value);
|
|
||||||
|
|
||||||
} else if($key == 'status') {
|
|
||||||
$value = (int) $value;
|
|
||||||
if(!isset($this->geocodeStatuses[$value])) $value = -1; // -1 = unknown
|
|
||||||
} else if($key == 'zoom') {
|
|
||||||
$value = (int) $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::set($key, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get($key) {
|
|
||||||
if($key == 'statusString') return str_replace('_', ' ', $this->geocodeStatuses[$this->status]);
|
|
||||||
return parent::get($key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function geocode() {
|
|
||||||
if($this->skipGeocode) return -100;
|
|
||||||
|
|
||||||
// check if address was already geocoded
|
|
||||||
if($this->geocodedAddress == $this->address) return $this->status;
|
|
||||||
$this->geocodedAddress = $this->address;
|
|
||||||
|
|
||||||
$url = "https://maps.googleapis.com/maps/api/geocode/json?address=" . urlencode($this->address);
|
|
||||||
|
|
||||||
/** @var FieldtypeMapMarker $fieldtype */
|
|
||||||
$fieldtype = $this->modules->get('FieldtypeMapMarker');
|
|
||||||
$apiKey = $fieldtype->get('googleApiKey');
|
|
||||||
if($apiKey) $url .= "&key=$apiKey";
|
|
||||||
$http = new WireHttp();
|
|
||||||
$json = $http->getJSON($url, true);
|
|
||||||
if(empty($json['status'])) $json['status'] = 'No response';
|
|
||||||
|
|
||||||
if($json['status'] != 'OK') {
|
|
||||||
$this->error("Error geocoding address: $json[status] ($url)");
|
|
||||||
if(isset($json['status'])) $this->status = (int) array_search($json['status'], $this->geocodeStatuses);
|
|
||||||
else $this->status = -1;
|
|
||||||
$this->lat = 0;
|
|
||||||
$this->lng = 0;
|
|
||||||
return $this->status;
|
|
||||||
}
|
|
||||||
|
|
||||||
$geometry = $json['results'][0]['geometry'];
|
|
||||||
$location = $geometry['location'];
|
|
||||||
$locationType = $geometry['location_type'];
|
|
||||||
|
|
||||||
$this->lat = $location['lat'];
|
|
||||||
$this->lng = $location['lng'];
|
|
||||||
|
|
||||||
$statusString = $json['status'] . '_' . $locationType;
|
|
||||||
$status = array_search($statusString, $this->geocodeStatuses);
|
|
||||||
if($status === false) $status = 1; // OK
|
|
||||||
|
|
||||||
$this->status = $status;
|
|
||||||
$this->message("Geocode {$this->statusString}: '{$this->address}'");
|
|
||||||
|
|
||||||
return $this->status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If accessed as a string, then just output the lat, lng coordinates
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function __toString() {
|
|
||||||
return "$this->address ($this->lat, $this->lng, $this->zoom) [$this->statusString]";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,213 +0,0 @@
|
||||||
/**
|
|
||||||
* ProcessWire Map Markup (JS)
|
|
||||||
*
|
|
||||||
* Renders maps for the FieldtypeMapMarker module
|
|
||||||
*
|
|
||||||
* ProcessWire 3.x
|
|
||||||
* Copyright (C) 2023 by Ryan Cramer
|
|
||||||
* Licensed under MPL 2.0
|
|
||||||
*
|
|
||||||
* http://processwire.com
|
|
||||||
*
|
|
||||||
* Javascript Usage:
|
|
||||||
* =================
|
|
||||||
* var map = new MarkupGoogleMap();
|
|
||||||
* map.setOption('any-google-maps-option', 'value');
|
|
||||||
* map.setOption('zoom', 12); // example
|
|
||||||
*
|
|
||||||
* // init(container ID, latitude, longitude):
|
|
||||||
* map.init('#map-div', 26.0936823, -77.5332796);
|
|
||||||
*
|
|
||||||
* // addMarker(latitude, longitude, optional URL, optional URL to icon file):
|
|
||||||
* map.addMarker(26.0936823, -77.5332796, 'en.wikipedia.org/wiki/Castaway_Cay', '');
|
|
||||||
* map.addMarker(...you may have as many of these as you want...);
|
|
||||||
*
|
|
||||||
* // optionally fit the map to the bounds of the markers you added
|
|
||||||
* map.fitToMarkers();
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
function MarkupGoogleMap() {
|
|
||||||
|
|
||||||
this.map = null;
|
|
||||||
this.markers = [];
|
|
||||||
this.numMarkers = 0;
|
|
||||||
this.icon = '';
|
|
||||||
this.iconHover = '';
|
|
||||||
this.shadow = '';
|
|
||||||
|
|
||||||
this.hoverBox = null;
|
|
||||||
this.hoverBoxOffsetTop = 0;
|
|
||||||
this.hoverBoxOffsetLeft = 0;
|
|
||||||
|
|
||||||
this.options = {
|
|
||||||
zoom: 10,
|
|
||||||
center: null,
|
|
||||||
mapTypeId: google.maps.MapTypeId.ROADMAP,
|
|
||||||
scrollwheel: false,
|
|
||||||
mapTypeControlOptions: {
|
|
||||||
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
|
|
||||||
},
|
|
||||||
scaleControl: false,
|
|
||||||
|
|
||||||
// disable points of interest
|
|
||||||
styles: [{
|
|
||||||
featureType: "poi",
|
|
||||||
stylers: [ { visibility: "off" } ]
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
this._currentURL = '';
|
|
||||||
|
|
||||||
this.init = function(mapID, lat, lng) {
|
|
||||||
if(lat != 0) this.options.center = new google.maps.LatLng(lat, lng);
|
|
||||||
this.map = new google.maps.Map(document.getElementById(mapID), this.options);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setOption = function(key, value) {
|
|
||||||
this.options[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setIcon = function(url) {
|
|
||||||
this.icon = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setIconHover = function(url) {
|
|
||||||
this.iconHover = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setShadow = function(url) {
|
|
||||||
this.shadow = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setHoverBox = function(markup) {
|
|
||||||
|
|
||||||
if(!markup.length) {
|
|
||||||
this.hoverBox = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hoverBox = $(markup);
|
|
||||||
var $hoverBox = this.hoverBox;
|
|
||||||
|
|
||||||
this.hoverBoxOffsetTop = parseInt($hoverBox.attr('data-top'));
|
|
||||||
this.hoverBoxOffsetLeft = parseInt($hoverBox.attr('data-left'));
|
|
||||||
|
|
||||||
$("body").append($hoverBox);
|
|
||||||
|
|
||||||
// keep it hidden/out of the way until needed
|
|
||||||
$hoverBox.css({
|
|
||||||
position: 'absolute',
|
|
||||||
left: 0,
|
|
||||||
top: '-100px'
|
|
||||||
});
|
|
||||||
|
|
||||||
$hoverBox.mouseout(function() {
|
|
||||||
$hoverBox.hide();
|
|
||||||
}).click(function() {
|
|
||||||
if(this._currentURL.length > 0) window.location.href = this._currentURL;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.addMarker = function(lat, lng, url, title, icon, shadow) {
|
|
||||||
if(lat == 0.0) return;
|
|
||||||
|
|
||||||
var latLng = new google.maps.LatLng(lat, lng);
|
|
||||||
var zIndex = 99999 + this.numMarkers;
|
|
||||||
|
|
||||||
var markerOptions = {
|
|
||||||
position: latLng,
|
|
||||||
map: this.map,
|
|
||||||
linkURL: '',
|
|
||||||
zIndex: zIndex
|
|
||||||
};
|
|
||||||
|
|
||||||
if(typeof icon !== "undefined" && icon.length > 0) markerOptions.icon = icon;
|
|
||||||
else if(this.icon) markerOptions.icon = this.icon;
|
|
||||||
|
|
||||||
// console.log(markerOptions);
|
|
||||||
|
|
||||||
if(typeof shadow !== "undefined" && shadow.length > 0) markerOptions.shadow = shadow;
|
|
||||||
else if(this.shadow.length > 0) markerOptions.shadow = this.shadow;
|
|
||||||
|
|
||||||
var marker = new google.maps.Marker(markerOptions);
|
|
||||||
|
|
||||||
if(url.length > 0) marker.linkURL = url;
|
|
||||||
if(this.hoverBox) marker.hoverBoxTitle = title;
|
|
||||||
else marker.setTitle(title);
|
|
||||||
|
|
||||||
this.markers[this.numMarkers] = marker;
|
|
||||||
this.numMarkers++;
|
|
||||||
|
|
||||||
if(marker.linkURL.length > 0) {
|
|
||||||
google.maps.event.addListener(marker, 'click', function(e) {
|
|
||||||
window.location.href = marker.linkURL;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if(markerOptions.icon !== "undefined" && this.iconHover) {
|
|
||||||
var iconHover = this.iconHover;
|
|
||||||
google.maps.event.addListener(marker, 'mouseover', function(e) {
|
|
||||||
marker.setIcon(iconHover);
|
|
||||||
});
|
|
||||||
google.maps.event.addListener(marker, 'mouseout', function(e) {
|
|
||||||
marker.setIcon(markerOptions.icon);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.hoverBox) {
|
|
||||||
|
|
||||||
var $hoverBox = this.hoverBox;
|
|
||||||
var offsetTop = this.hoverBoxOffsetTop;
|
|
||||||
var offsetLeft = this.hoverBoxOffsetLeft;
|
|
||||||
|
|
||||||
var mouseMove = function(e) {
|
|
||||||
$hoverBox.css({
|
|
||||||
'top': e.pageY + offsetTop,
|
|
||||||
'left': e.pageX + offsetLeft
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// console.log($hoverBox);
|
|
||||||
|
|
||||||
google.maps.event.addListener(marker, 'mouseover', function(e) {
|
|
||||||
this._currentURL = url;
|
|
||||||
$hoverBox.html("<span>" + marker.hoverBoxTitle + "</span>")
|
|
||||||
.css('top', '0px')
|
|
||||||
.css('left', '0px')
|
|
||||||
.css('display', 'block')
|
|
||||||
.css('width', 'auto')
|
|
||||||
.css('z-index', 9999);
|
|
||||||
$hoverBox.show();
|
|
||||||
|
|
||||||
$(document).mousemove(mouseMove);
|
|
||||||
});
|
|
||||||
|
|
||||||
google.maps.event.addListener(marker, 'mouseout', function(e) {
|
|
||||||
$hoverBox.hide();
|
|
||||||
$(document).unbind("mousemove", mouseMove);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.fitToMarkers = function() {
|
|
||||||
|
|
||||||
var bounds = new google.maps.LatLngBounds();
|
|
||||||
var map = this.map;
|
|
||||||
|
|
||||||
for(var i = 0; i < this.numMarkers; i++) {
|
|
||||||
var latLng = this.markers[i].position;
|
|
||||||
bounds.extend(latLng);
|
|
||||||
}
|
|
||||||
|
|
||||||
map.fitBounds(bounds);
|
|
||||||
|
|
||||||
|
|
||||||
var listener = google.maps.event.addListener(map, "idle", function() {
|
|
||||||
if(map.getZoom() < 2) map.setZoom(2);
|
|
||||||
google.maps.event.removeListener(listener);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,255 +0,0 @@
|
||||||
<?php namespace ProcessWire;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ProcessWire Map Markup
|
|
||||||
*
|
|
||||||
* Renders maps for the FieldtypeMapMarker module
|
|
||||||
*
|
|
||||||
* ProcessWire 3.x
|
|
||||||
* Copyright (C) 2023 by Ryan Cramer
|
|
||||||
* Licensed under MPL 2.0
|
|
||||||
*
|
|
||||||
* https://processwire.com
|
|
||||||
*
|
|
||||||
* USAGE:
|
|
||||||
* ======
|
|
||||||
*
|
|
||||||
* Add this somewhere before your closing </head> tag:
|
|
||||||
*
|
|
||||||
* <script type='text/javascript' src='<?=$modules->FieldtypeMapMarker->getGoogleMapsURL();?>'></script>
|
|
||||||
*
|
|
||||||
* In the location where you want to output your map, do the following in your template file:
|
|
||||||
*
|
|
||||||
* $map = $modules->get('MarkupGoogleMap');
|
|
||||||
* echo $map->render($page, 'map'); // replace 'map' with the name of your FieldtypeMap field
|
|
||||||
*
|
|
||||||
* To render a map with multiple markers on it, specify a PageArray rather than a single $page:
|
|
||||||
*
|
|
||||||
* $items = $pages->find("template=something, map!='', sort=title");
|
|
||||||
* $map = $modules->get('MarkupGoogleMap');
|
|
||||||
* echo $map->render($items, 'map');
|
|
||||||
*
|
|
||||||
* To specify options, provide a 3rd argument with an options array:
|
|
||||||
*
|
|
||||||
* $map = $modules->get('MarkupGoogleMap');
|
|
||||||
* echo $map->render($items, 'map', array('height' => '500px'));
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* OPTIONS
|
|
||||||
* =======
|
|
||||||
* Here is a list of all possible options (with defaults shown):
|
|
||||||
*
|
|
||||||
* // default width of the map
|
|
||||||
* 'width' => '100%'
|
|
||||||
*
|
|
||||||
* // default height of the map
|
|
||||||
* 'height' => '300px'
|
|
||||||
*
|
|
||||||
* // zoom level
|
|
||||||
* 'zoom' => 12 (or $field->defaultZoom)
|
|
||||||
*
|
|
||||||
* // map type: ROADMAP, HYBRID or SATELLITE
|
|
||||||
* 'type' => 'HYBRID' or $field->defaultType
|
|
||||||
*
|
|
||||||
* // map ID attribute
|
|
||||||
* 'id' => "mgmap"
|
|
||||||
*
|
|
||||||
* // map class attribute
|
|
||||||
* 'class' => "MarkupGoogleMap"
|
|
||||||
*
|
|
||||||
* // center latitude
|
|
||||||
* 'lat' => $field->defaultLat
|
|
||||||
*
|
|
||||||
* // center longitude
|
|
||||||
* 'lng' => $field->defaultLng
|
|
||||||
*
|
|
||||||
* // set to false only if you will style the map <div> yourself
|
|
||||||
* 'useStyles' => true
|
|
||||||
*
|
|
||||||
* // allows single-marker map to use marker settings rather than map settings
|
|
||||||
* 'useMarkerSettings' => true
|
|
||||||
*
|
|
||||||
* // field to use for the marker link, or blank to not link
|
|
||||||
* 'markerLinkField' => 'url'
|
|
||||||
*
|
|
||||||
* // field to use for the marker title
|
|
||||||
* 'markerTitleField' => 'title'
|
|
||||||
*
|
|
||||||
* // map will automatically adjust to fit to the given markers (when multiple markers)
|
|
||||||
* 'fitToMarkers' => true
|
|
||||||
*
|
|
||||||
* // use hover box? When true, shows a tooltip-type box when you hover the marker, populated with the markerTitleField
|
|
||||||
* // this is often more useful than the default presentation google maps uses
|
|
||||||
* 'useHoverBox' => false
|
|
||||||
*
|
|
||||||
* // when useHoverBox is true, you can specify the markup used for it. Use the following (which is the default) as your starting point:
|
|
||||||
* 'hoverBoxMarkup' => "<div data-top='-10' data-left='15' style='background: #000; color: #fff; padding: 0.25em 0.5em; border-radius: 3px;'></div>",
|
|
||||||
*
|
|
||||||
* // FUll URL to icon file to use for markers. Blank=use default Google marker icon.
|
|
||||||
* 'icon' => '',
|
|
||||||
*
|
|
||||||
* // Any extra javascript initialization code you want to occur before the map itself is drawn
|
|
||||||
* 'init' => '',
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
class MarkupGoogleMap extends WireData implements Module {
|
|
||||||
|
|
||||||
public static function getModuleInfo() {
|
|
||||||
return array(
|
|
||||||
'title' => 'Map Markup (Google Maps)',
|
|
||||||
'version' => 300,
|
|
||||||
'summary' => 'Renders Google Maps for the MapMarker Fieldtype',
|
|
||||||
'requires' => 'FieldtypeMapMarker',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Include our MapMarker class, which serves as the value for fields of type FieldtypeMapMarker
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function init() {
|
|
||||||
require_once(dirname(__FILE__) . '/MapMarker.php');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get associative array of map options
|
|
||||||
*
|
|
||||||
* @param string $fieldName
|
|
||||||
* @return array
|
|
||||||
* @throws WireException
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getOptions($fieldName) {
|
|
||||||
|
|
||||||
static $n = 0;
|
|
||||||
$field = $this->wire()->fields->get($fieldName);
|
|
||||||
if(!$field) throw new WireException("Unknown field: $fieldName");
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'useStyles' => true,
|
|
||||||
'fitToMarkers' => true,
|
|
||||||
'useMarkerSettings' => true,
|
|
||||||
'useHoverBox' => false,
|
|
||||||
'hoverBoxMarkup' => "<div data-top='-10' data-left='15' style='background: #000; color: #fff; padding: 0.25em 0.5em; border-radius: 3px;'></div>",
|
|
||||||
'markerLinkField' => 'url',
|
|
||||||
'markerTitleField' => 'title',
|
|
||||||
'width' => '100%',
|
|
||||||
'height' => $field->get('height'),
|
|
||||||
'zoom' => $field->get('defaultZoom') ? (int) $field->get('defaultZoom') : 12,
|
|
||||||
'type' => $field->get('defaultType') ? $field->get('defaultType') : 'HYBRID',
|
|
||||||
'id' => "mgmap" . (++$n),
|
|
||||||
'class' => "MarkupGoogleMap",
|
|
||||||
'lat' => $field->get('defaultLat'),
|
|
||||||
'lng' => $field->get('defaultLng'),
|
|
||||||
'icon' => '', // url to icon (blank=use default)
|
|
||||||
'iconHover' => '', // url to icon when hovered (default=none)
|
|
||||||
'shadow' => '', // url to icon shadow (blank=use default)
|
|
||||||
'init' => '', // additional javascript code to insert in map initialization
|
|
||||||
'n' => $n,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the script tag for loading Google Maps
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
* @throws WireException
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getGMapScript() {
|
|
||||||
$url = 'https://maps.google.com/maps/api/js';
|
|
||||||
$key = $this->wire()->modules->get('FieldtypeMapMarker')->get('googleApiKey');
|
|
||||||
if($key) $url .= "?key=$key";
|
|
||||||
return "<script type='text/javascript' src='$url'></script>";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render map markup
|
|
||||||
*
|
|
||||||
* @param PageArray|Page $pageArray Page (or multiple pages in PageArray) containing map field
|
|
||||||
* @param string $fieldName Name of the map field
|
|
||||||
* @param array $options Options to adjust behavior
|
|
||||||
* @return string
|
|
||||||
* @throws WireException
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function render($pageArray, $fieldName, array $options = array()) {
|
|
||||||
$config = $this->wire()->config;
|
|
||||||
|
|
||||||
static $n = 0;
|
|
||||||
$n++;
|
|
||||||
|
|
||||||
$defaultOptions = $this->getOptions($fieldName);
|
|
||||||
$options = array_merge($defaultOptions, $options);
|
|
||||||
|
|
||||||
if($pageArray instanceof Page) {
|
|
||||||
$page = $pageArray;
|
|
||||||
$pageArray = new PageArray();
|
|
||||||
$pageArray->add($page);
|
|
||||||
}
|
|
||||||
|
|
||||||
$height = $options['height'];
|
|
||||||
$width = $options['width'];
|
|
||||||
if(empty($height)) $height = 300;
|
|
||||||
if(ctype_digit("$height")) $height .= "px";
|
|
||||||
if(ctype_digit("$width")) $width .= "px";
|
|
||||||
|
|
||||||
$style = '';
|
|
||||||
if($options['useStyles'] && !empty($height) && !empty($width)) {
|
|
||||||
$style = " style='width: $width; height: $height;'";
|
|
||||||
}
|
|
||||||
|
|
||||||
$lat = $options['lat'];
|
|
||||||
$lng = $options['lng'];
|
|
||||||
$zoom = $options['zoom'] > 0 ? (int) $options['zoom'] : $defaultOptions['zoom'];
|
|
||||||
$type = in_array($options['type'], array('ROADMAP', 'SATELLITE', 'HYBRID')) ? $options['type'] : 'HYBRID';
|
|
||||||
|
|
||||||
if($options['useMarkerSettings'] && (count($pageArray) == 1 || !$lat)) {
|
|
||||||
// single marker overrides lat, lng and zoom settings
|
|
||||||
$marker = $pageArray->first()->get($fieldName);
|
|
||||||
$lat = $marker->lat;
|
|
||||||
$lng = $marker->lng;
|
|
||||||
if($marker->zoom > 0) $zoom = (int) $marker->zoom;
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = $options['id'];
|
|
||||||
$out = '';
|
|
||||||
|
|
||||||
if($n === 1) {
|
|
||||||
$url = $config->urls('MarkupGoogleMap');
|
|
||||||
$out .= "<script type='text/javascript' src='{$url}MarkupGoogleMap.js'></script>";
|
|
||||||
}
|
|
||||||
|
|
||||||
$out .= "<div id='$id' class='$options[class]'$style></div>";
|
|
||||||
|
|
||||||
$out .=
|
|
||||||
"<script type='text/javascript'>" .
|
|
||||||
"if(typeof google === 'undefined' || typeof google.maps === 'undefined') { " .
|
|
||||||
"alert('MarkupGoogleMap Error: Please add the maps.google.com script in your document head.'); " .
|
|
||||||
"} else { " .
|
|
||||||
"var $id = new MarkupGoogleMap(); " .
|
|
||||||
"$id.setOption('zoom', $zoom); " .
|
|
||||||
"$id.setOption('mapTypeId', google.maps.MapTypeId.$type); " .
|
|
||||||
($options['icon'] ? "$id.setIcon('$options[icon]'); " : "") .
|
|
||||||
($options['iconHover'] ? "$id.setIconHover('$options[iconHover]'); " : "") .
|
|
||||||
($options['shadow'] ? "$id.setShadow('$options[shadow]'); " : "") .
|
|
||||||
($options['useHoverBox'] ? "$id.setHoverBox('" . str_replace("'", '"', $options['hoverBoxMarkup']) . "');" : "") .
|
|
||||||
$options['init'] .
|
|
||||||
"$id.init('$id', $lat, $lng); ";
|
|
||||||
|
|
||||||
foreach($pageArray as $page) {
|
|
||||||
$marker = $page->get($fieldName);
|
|
||||||
if(!$marker instanceof MapMarker) continue;
|
|
||||||
if(!$marker->lat) continue;
|
|
||||||
$url = $options['markerLinkField'] ? $page->get($options['markerLinkField']) : '';
|
|
||||||
$title = $options['markerTitleField'] ? $page->get($options['markerTitleField']) : '';
|
|
||||||
$out .= "$id.addMarker($marker->lat, $marker->lng, '$url', '$title', ''); ";
|
|
||||||
}
|
|
||||||
|
|
||||||
if(count($pageArray) > 1 && $options['fitToMarkers']) $out .= "$id.fitToMarkers(); ";
|
|
||||||
$out .= "}</script>";
|
|
||||||
|
|
||||||
return $out;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,146 +0,0 @@
|
||||||
# FieldtypeMapMarker Module for ProcessWire
|
|
||||||
|
|
||||||
This Fieldtype for ProcessWire holds an address or location name, and automatically
|
|
||||||
geocodes the address to latitude/longitude using Google Maps API. The resulting
|
|
||||||
values may be used to populate any kind of map (whether Google Maps or another).
|
|
||||||
|
|
||||||
This Fieldtype was created to serve as an example of creating a custom Fieldtype and
|
|
||||||
Inputfield that contains multiple pieces of data. Though the Fieldtype has now gone
|
|
||||||
far beyond that and is relatively full featured. As a result, it may no longer be
|
|
||||||
the simplest example of how to implement a Fieldtype/Inputfield, though it is very
|
|
||||||
effective and useful.
|
|
||||||
|
|
||||||
MapMarker also has a corresponding Inputfield and Markup module, named
|
|
||||||
InputfieldMapMarker and MarkupGoogleMap. When you install FieldtypeMapMarker, the
|
|
||||||
Inputfield will also be installed and used for input on the admin side. Installation
|
|
||||||
of MarkupGoogleMap is optional. It provides a simple way to render Google maps with
|
|
||||||
the data managed by FieldtypeMapMarker.
|
|
||||||
|
|
||||||
This Fieldtype has a [support forum](http://processwire.com/talk/index.php/topic,752.0.html)
|
|
||||||
|
|
||||||
## Using Map Marker
|
|
||||||
|
|
||||||
### How to install
|
|
||||||
|
|
||||||
1. Copy all of the files for this module into /site/modules/FieldtypeMapMarker/
|
|
||||||
|
|
||||||
2. In your admin, go to the Modules screen and "check for new modules." Click *install*
|
|
||||||
for the Map Marker Fieldtype.
|
|
||||||
|
|
||||||
3. In your admin, go to Setup > Fields > Add New Field. Choose MapMarker as the type.
|
|
||||||
If you are not sure what to name your field, simply "map" is a good one! Once created,
|
|
||||||
configure the settings on the *input* tab.
|
|
||||||
|
|
||||||
4. Add your new "map" field to one or more templates, as you would any other field.
|
|
||||||
|
|
||||||
### How to use from the page editor
|
|
||||||
|
|
||||||
1. Create or edit a page using one of the templates you added the "map" field to.
|
|
||||||
|
|
||||||
2. Type in a location or address into the "address" box for the map field. Then click
|
|
||||||
outside of the address, and the Javascript geocoder should automatically populate the
|
|
||||||
latitude, longitude and map location. The Google geocoder will accept full addresses
|
|
||||||
or known location names. For instance, you could type in "Disney Land" and it knows
|
|
||||||
how to find locations like that.
|
|
||||||
|
|
||||||
3. The geocoding also works in reverse. You may drag the map marker wherever you want
|
|
||||||
and it will populate the address field for you. You may also populate the latitude,
|
|
||||||
longitude and zoom fields manually if you like. Unchecking the box between address
|
|
||||||
and latitude disables the geocoder.
|
|
||||||
|
|
||||||
### How to use from the API, in your template files
|
|
||||||
|
|
||||||
In your template files, you can utilize this data for your own Google Maps (or anything
|
|
||||||
else that you might need latitude/longitude for).
|
|
||||||
|
|
||||||
Lets assume that your field is called 'map'. Here is how you would access the
|
|
||||||
components of it from the API:
|
|
||||||
```php
|
|
||||||
echo $page->map->address; // outputs the address you entered
|
|
||||||
echo $page->map->lat; // outputs the latitude
|
|
||||||
echo $page->map->lng; // outputs the longitude
|
|
||||||
echo $page->map->zoom; // outputs the zoom level
|
|
||||||
```
|
|
||||||
|
|
||||||
-------------
|
|
||||||
|
|
||||||
## Markup Google Map
|
|
||||||
|
|
||||||
This package also comes with a module called MarkupGoogleMap. It provides a simple means
|
|
||||||
of outputting a Google Map based on the data managed by FieldtypeMapMarker. To install,
|
|
||||||
simply click "install" for the Google Maps (Markup) module. This is a Markup module,
|
|
||||||
meaning it exists primarily to generate markup for output on the front-end of your site.
|
|
||||||
|
|
||||||
### How to use
|
|
||||||
|
|
||||||
Add this somewhere before your closing `</head>` tag:
|
|
||||||
```html
|
|
||||||
<script type='text/javascript' src='https://maps.googleapis.com/maps/api/js?sensor=false'></script>
|
|
||||||
```
|
|
||||||
|
|
||||||
In the location where you want to output your map, place the following in your template file:
|
|
||||||
```php
|
|
||||||
$map = $modules->get('MarkupGoogleMap');
|
|
||||||
echo $map->render($page, 'map');
|
|
||||||
```
|
|
||||||
In the above, $page is the Page object that has the 'map' field. Replace 'map' with the name of
|
|
||||||
your map field.
|
|
||||||
|
|
||||||
To render a map with multiple markers on it, specify a PageArray rather than a single $page:
|
|
||||||
```php
|
|
||||||
$items = $pages->find("template=something, map!='', sort=title");
|
|
||||||
$map = $modules->get('MarkupGoogleMap');
|
|
||||||
echo $map->render($items, 'map');
|
|
||||||
````
|
|
||||||
|
|
||||||
To specify options, provide a 3rd argument with an options array:
|
|
||||||
```php
|
|
||||||
$map = $modules->get('MarkupGoogleMap');
|
|
||||||
echo $map->render($items, 'map', array('height' => '500px'));
|
|
||||||
```
|
|
||||||
|
|
||||||
### Options
|
|
||||||
|
|
||||||
Here is a list of all possible options (with defaults shown):
|
|
||||||
|
|
||||||
`width`
|
|
||||||
Width of the map (type: string; default: 100%).
|
|
||||||
|
|
||||||
`height`
|
|
||||||
Height of the map (type: string; default: 300px)
|
|
||||||
|
|
||||||
`zoom`
|
|
||||||
Zoom level 1-25 (type: integer; default: from your field settings)
|
|
||||||
|
|
||||||
`type`
|
|
||||||
Map type: ROADMAP, HYBRID or SATELLITE (type: string; default: from your field settings)
|
|
||||||
|
|
||||||
`id`
|
|
||||||
Map ID attribute (type: string; default: mgmap)
|
|
||||||
|
|
||||||
`class`
|
|
||||||
Map class attribute (type: string; default: MarkupGoogleMap)
|
|
||||||
|
|
||||||
`lat`
|
|
||||||
Map center latitude (type: string|float; default: from your field settings)
|
|
||||||
|
|
||||||
`lng`
|
|
||||||
Map center longitude (type: string|float; default: from your field settings)
|
|
||||||
|
|
||||||
`useStyles`
|
|
||||||
Whether to populate inline styles to the map div for width/height (type: boolean; default: true).
|
|
||||||
Set to false only if you will style the map div yourself.
|
|
||||||
|
|
||||||
`useMarkerSettings`
|
|
||||||
Makes single-marker map use marker settings rather than map settings (type: boolean; default: true).
|
|
||||||
|
|
||||||
`markerLinkField`
|
|
||||||
Page field to use for the marker link, or blank to not link (type: string; default: url).
|
|
||||||
|
|
||||||
`markerTitleField`
|
|
||||||
Page field to use for the marker title, or blank not to use a marker title (type: string; default: title).
|
|
||||||
|
|
||||||
`fitToMarkers`
|
|
||||||
When multiple markers are present, set map to automatically adjust to fit to the given markers (type: boolean; default: true).
|
|
||||||
|
|
||||||
---------
|
|
Loading…
Reference in a new issue