.
*/
namespace Fisharebest\Webtrees\Module;
use Fisharebest\Webtrees\Auth;
use Fisharebest\Webtrees\Controller\ChartController;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Controller\SimpleController;
use Fisharebest\Webtrees\Database;
use Fisharebest\Webtrees\Fact;
use Fisharebest\Webtrees\Family;
use Fisharebest\Webtrees\Filter;
use Fisharebest\Webtrees\FlashMessages;
use Fisharebest\Webtrees\Functions\Functions;
use Fisharebest\Webtrees\Functions\FunctionsCharts;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
use Fisharebest\Webtrees\Functions\FunctionsPrint;
use Fisharebest\Webtrees\GedcomTag;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Individual;
use Fisharebest\Webtrees\Log;
use Fisharebest\Webtrees\Menu;
use Fisharebest\Webtrees\Module;
use Fisharebest\Webtrees\Stats;
use Fisharebest\Webtrees\Tree;
use PDO;
/**
* Class GoogleMapsModule
*
* @link http://www.google.com/permissions/guidelines.html
*
* "... an unregistered Google Brand Feature should be followed by
* the superscripted letters TM or SM ..."
*
* Hence, use "Google Maps™"
*
* "... Use the trademark only as an adjective"
*
* "... Use a generic term following the trademark, for example:
* GOOGLE search engine, Google search"
*
* Hence, use "Google Maps™ mapping service" where appropriate.
*/
class GoogleMapsModule extends AbstractModule implements ModuleConfigInterface, ModuleTabInterface, ModuleChartInterface {
// How to update the database schema for this module
const SCHEMA_TARGET_VERSION = 6;
const SCHEMA_SETTING_NAME = 'GM_SCHEMA_VERSION';
const SCHEMA_MIGRATION_PREFIX = '\Fisharebest\Webtrees\Module\GoogleMaps\Schema';
/** @var Individual[] of ancestors of root person */
private $ancestors = array();
/** @var int Number of nodes in the chart */
private $treesize;
/** {@inheritdoc} */
public function getTitle() {
return /* I18N: The name of a module. Google Maps™ is a trademark. Do not translate it? http://en.wikipedia.org/wiki/Google_maps */ I18N::translate('Google Maps™');
}
/** {@inheritdoc} */
public function getDescription() {
return /* I18N: Description of the “Google Maps™” module */ I18N::translate('Show the location of places and events using the Google Maps™ mapping service.');
}
/**
* This is a general purpose hook, allowing modules to respond to routes
* of the form module.php?mod=FOO&mod_action=BAR
*
* @param string $mod_action
*/
public function modAction($mod_action) {
Database::updateSchema(self::SCHEMA_MIGRATION_PREFIX, self::SCHEMA_SETTING_NAME, self::SCHEMA_TARGET_VERSION);
switch ($mod_action) {
case 'admin_config':
$this->config();
break;
case 'flags':
$this->flags();
break;
case 'pedigree_map':
$this->pedigreeMap();
break;
case 'admin_placecheck':
$this->adminPlaceCheck();
break;
case 'admin_places':
$this->adminPlaces();
break;
case 'places_edit':
$this->placesEdit();
break;
case 'wt_street_view':
$this->wtStreetView();
break;
default:
http_response_code(404);
break;
}
}
/** {@inheritdoc} */
public function getConfigLink() {
Database::updateSchema(self::SCHEMA_MIGRATION_PREFIX, self::SCHEMA_SETTING_NAME, self::SCHEMA_TARGET_VERSION);
return 'module.php?mod=' . $this->getName() . '&mod_action=admin_config';
}
/** {@inheritdoc} */
public function defaultTabOrder() {
return 80;
}
/** {@inheritdoc} */
public function getPreLoadContent() {
global $controller;
$controller->addInlineJavascript("
jQuery('head').append('');
");
ob_start();
?>
checkMapData($controller->record)) {
// This call can return an empty string if no facts with map co-ordinates exist
$mapdata = $this->buildIndividualMap($controller->record);
} else {
$mapdata = '';
}
if ($mapdata) {
$html = '
getPreference('MAX_PEDIGREE_GENERATIONS');
// Limit this to match available number of icons.
// 8 generations equals 255 individuals
$MAX_PEDIGREE_GENERATIONS = min($MAX_PEDIGREE_GENERATIONS, 8);
$controller = new ChartController();
$generations = Filter::getInteger('PEDIGREE_GENERATIONS', 2, $MAX_PEDIGREE_GENERATIONS, $WT_TREE->getPreference('DEFAULT_PEDIGREE_GENERATIONS'));
$this->treesize = pow(2, $generations) - 1;
$this->ancestors = array_values($controller->sosaAncestors($generations));
$controller
->setPageTitle(/* I18N: %s is an individual’s name */ I18N::translate('Pedigree map of %s', $controller->root->getFullName()))
->pageHeader()
->addExternalJavascript(WT_AUTOCOMPLETE_JS_URL)
/* prepending the module css in the page head allows the theme to over-ride it*/
->addInlineJavascript("
jQuery('head').prepend('');
autocomplete();" .
$this->pedigreeMapJavascript()
);
echo '
', $controller->getPageTitle(), '
';
// -- print the form to change the number of displayed generations
?>
treesize); $i++) {
// -- check to see if we have moved to the next generation
if ($i + 1 >= pow(2, $curgen)) {
$curgen++;
}
$person = $this->ancestors[$i];
if (!empty($person)) {
$name = $person->getFullName();
if ($name == I18N::translate('Private')) {
$priv++;
}
$place = $person->getBirthPlace();
if (empty($place)) {
$latlongval[$i] = null;
} else {
$latlongval[$i] = $this->getLatitudeAndLongitudeFromPlaceLocation($person->getBirthPlace());
}
if ($latlongval[$i]) {
$lat[$i] = strtr($latlongval[$i]->pl_lati, array('N' => '', 'S' => '-', ',' => '.'));
$lon[$i] = strtr($latlongval[$i]->pl_long, array('N' => '', 'S' => '-', ',' => '.'));
if ($lat[$i] && $lon[$i]) {
$count++;
} else {
// The place is in the table but has empty values
if ($name) {
$missing[] = '' . $name . '';
$miscount++;
}
}
} else {
// There was no place, or not listed in the map table
if ($name) {
$missing[] = '' . $name . '';
$miscount++;
}
}
}
}
//
//
echo '
';
}
// display info under map
echo '';
// print summary statistics
if (isset($curgen)) {
$total = pow(2, $curgen) - 1;
echo '
';
echo I18N::plural(
'%1$s individual displayed, out of the normal total of %2$s, from %3$s generations.',
'%1$s individuals displayed, out of the normal total of %2$s, from %3$s generations.',
$count,
I18N::number($count), I18N::number($total), I18N::number($curgen)
);
echo '
';
if ($priv) {
echo '
' . I18N::plural('%s individual is private.', '%s individuals are private.', $priv, $priv), '
';
}
if ($count + $priv != $total) {
if ($miscount == 0) {
echo '
' . I18N::translate('No ancestors in the database.'), '
';
} else {
echo '
' . /* I18N: %1$s is a count of individuals, %2$s is a list of their names */ I18N::plural(
'%1$s individual is missing birthplace map coordinates: %2$s.',
'%1$s individuals are missing birthplace map coordinates: %2$s.',
$miscount, I18N::number($miscount), implode(I18N::$list_separator, $missing)),
'
';
}
}
}
echo '
';
echo '
';
echo '';
}
/**
* Create the Javascript to activate the map.
*
* @return string
*/
private function pedigreeMapJavascript() {
$js = '
// this variable will collect the html which will eventually be placed in the side bar
var gm_ancestors_html = "";
// arrays to hold copies of the markers and html used by the side bar
// because the function closure trick doesnt work there
var gmarkers = [];
var index = 0;
var lastlinkid;
var infowindow = new google.maps.InfoWindow({});
// === Create an associative array of GIcons()
var gicons = [];
gicons["1"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon1.png"
}
gicons["2"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon2.png"
}
gicons["2L"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon2L.png",
size: new google.maps.Size(32, 32), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(28, 28) // Image anchor
};
gicons["2R"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon2R.png",
size: new google.maps.Size(32, 32), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(4, 28) // Image anchor
};
gicons["2Ls"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon2Ls.png",
size: new google.maps.Size(24, 24), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(22, 22) // Image anchor
};
gicons["2Rs"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon2Rs.png",
size: new google.maps.Size(24, 24), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(2, 22) // Image anchor
};
gicons["3"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon3.png"
}
gicons["3L"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon3L.png",
size: new google.maps.Size(32, 32), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(28, 28) // Image anchor
};
gicons["3R"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon3R.png",
size: new google.maps.Size(32, 32), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(4, 28) // Image anchor
};
gicons["3Ls"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon3Ls.png",
size: new google.maps.Size(24, 24), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(22, 22) // Image anchor
};
gicons["3Rs"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon3Rs.png",
size: new google.maps.Size(24, 24), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(2, 22) // Image anchor
};
gicons["4"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon4.png"
}
gicons["4L"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon4L.png",
size: new google.maps.Size(32, 32), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(28, 28) // Image anchor
};
gicons["4R"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon4R.png",
size: new google.maps.Size(32, 32), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(4, 28) // Image anchor
};
gicons["4Ls"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon4Ls.png",
size: new google.maps.Size(24, 24), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(22, 22) // Image anchor
};
gicons["4Rs"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon4Rs.png",
size: new google.maps.Size(24, 24), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(2, 22) // Image anchor
};
gicons["5"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon5.png"
}
gicons["5L"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon5L.png",
size: new google.maps.Size(32, 32), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(28, 28) // Image anchor
};
gicons["5R"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon5R.png",
size: new google.maps.Size(32, 32), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(4, 28) // Image anchor
};
gicons["5Ls"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon5Ls.png",
size: new google.maps.Size(24, 24), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(22, 22) // Image anchor
};
gicons["5Rs"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon5Rs.png",
size: new google.maps.Size(24, 24), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(2, 22) // Image anchor
};
gicons["6"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon6.png"
}
gicons["6L"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon6L.png",
size: new google.maps.Size(32, 32), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(28, 28) // Image anchor
};
gicons["6R"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon6R.png",
size: new google.maps.Size(32, 32), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(4, 28) // Image anchor
};
gicons["6Ls"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon6Ls.png",
size: new google.maps.Size(24, 24), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(22, 22) // Image anchor
};
gicons["6Rs"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon6Rs.png",
size: new google.maps.Size(24, 24), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(2, 22) // Image anchor
};
gicons["7"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon7.png"
}
gicons["7L"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon7L.png",
size: new google.maps.Size(32, 32), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(28, 28) // Image anchor
};
gicons["7R"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon7R.png",
size: new google.maps.Size(32, 32), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(4, 28) // Image anchor
};
gicons["7Ls"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon7Ls.png",
size: new google.maps.Size(24, 24), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(22, 22) // Image anchor
};
gicons["7Rs"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon7Rs.png",
size: new google.maps.Size(24, 24), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(2, 22) // Image anchor
};
gicons["8"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon8.png"
}
gicons["8L"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon8L.png",
size: new google.maps.Size(32, 32), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(28, 28) // Image anchor
};
gicons["8R"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon8R.png",
size: new google.maps.Size(32, 32), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(4, 28) // Image anchor
};
gicons["8Ls"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon8Ls.png",
size: new google.maps.Size(24, 24), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(22, 22) // Image anchor
};
gicons["8Rs"] = {
url: WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon8Rs.png",
size: new google.maps.Size(24, 24), // Image size
origin: new google.maps.Point(0, 0), // Image origin
anchor: new google.maps.Point(2, 22) // Image anchor
};
// / A function to create the marker and set up the event window
function createMarker(point, name, html, mhtml, icontype) {
// Create a marker with the requested icon
var marker = new google.maps.Marker({
icon: gicons[icontype],
map: pm_map,
position: point,
id: index,
zIndex: 0
});
google.maps.event.addListener(marker, "click", function() {
infowindow.close();
infowindow.setContent(mhtml);
infowindow.open(pm_map, marker);
var el = jQuery(".gm-ancestor[data-marker=" + marker.id + "]");
if(el.hasClass("person_box")) {
el
.removeClass("person_box")
.addClass("gm-ancestor-visited");
infowindow.close();
} else {
el
.addClass("person_box")
.removeClass("gm-ancestor-visited");
}
var anchor = infowindow.getAnchor();
lastlinkid = anchor ? anchor.id : null;
});
// save the info we need to use later for the side bar
gmarkers[index] = marker;
gm_ancestors_html += "
" + html +"
";
return marker;
};
// create the map
var myOptions = {
zoom: 6,
center: new google.maps.LatLng(0, 0),
mapTypeId: google.maps.MapTypeId.TERRAIN, // ROADMAP, SATELLITE, HYBRID, TERRAIN
mapTypeControlOptions: {
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU // DEFAULT, DROPDOWN_MENU, HORIZONTAL_BAR
},
navigationControlOptions: {
position: google.maps.ControlPosition.TOP_RIGHT, // BOTTOM, BOTTOM_LEFT, LEFT, TOP, etc
style: google.maps.NavigationControlStyle.SMALL // ANDROID, DEFAULT, SMALL, ZOOM_PAN
},
streetViewControl: false, // Show Pegman or not
scrollwheel: true
};
var pm_map = new google.maps.Map(document.querySelector(".gm-map"), myOptions);
google.maps.event.addListener(pm_map, "click", function() {
jQuery(".gm-ancestor.person_box")
.removeClass("person_box")
.addClass("gm-ancestor-visited");
infowindow.close();
lastlinkid = null;
});
// create the map bounds
var bounds = new google.maps.LatLngBounds();';
// add the points
$curgen = 1;
$count = 0;
$colored_line = array(
'1' => '#FF0000',
'2' => '#0000FF',
'3' => '#00FF00',
'4' => '#FFFF00',
'5' => '#00FFFF',
'6' => '#FF00FF',
'7' => '#C0C0FF',
'8' => '#808000',
);
$lat = array();
$lon = array();
$latlongval = array();
for ($i = 0; $i < $this->treesize; $i++) {
// moved up to grab the sex of the individuals
$person = $this->ancestors[$i];
if ($person) {
$name = $person->getFullName();
// -- check to see if we have moved to the next generation
if ($i + 1 >= pow(2, $curgen)) {
$curgen++;
}
$relationship = FunctionsCharts::getSosaName($i + 1);
// get thumbnail image
if ($person->getTree()->getPreference('SHOW_HIGHLIGHT_IMAGES')) {
$image = $person->displayImage();
} else {
$image = '';
}
$event = ' ';
$event .= '' . $relationship . '';
$birth = $person->getFirstFact('BIRT');
$data = Filter::escapeJs($image . '
';
global $pl_lati, $pl_long;
if ($level >= 1) {
$pl_lati = strtr($latlng['pl_lati'], array('N' => '', 'S' => '-', ',' => '.')); // WT_placelocation lati
$pl_long = strtr($latlng['pl_long'], array('E' => '', 'W' => '-', ',' => '.')); // WT_placelocation long
// Check if Streetview location parameters are stored in database
$placeid = $latlng['pl_id']; // Placelocation place id
$sv_lat = $latlng['sv_lati']; // StreetView Point of View Latitude
$sv_lng = $latlng['sv_long']; // StreetView Point of View Longitude
$sv_dir = $latlng['sv_bearing']; // StreetView Point of View Direction (degrees from North)
$sv_pitch = $latlng['sv_elevation']; // StreetView Point of View Elevation (+90 to -90 degrees (+=down, -=up)
$sv_zoom = $latlng['sv_zoom']; // StreetView Point of View Zoom (0, 1, 2 or 3)
// Check if Street View Lati/Long are the default of 0, if so use regular Place Lati/Long to set an initial location for the panda
if ($latlng['sv_lati'] == 0 && $latlng['sv_long'] == 0) {
$sv_lat = $pl_lati;
$sv_lng = $pl_long;
}
$frameheight = $this->getSetting('GM_PH_YSIZE') + 35; // Add height of buttons
?>
';
}
echo '
';
}
/**
* Print the numbers of individuals.
*
* @param int $level
* @param string[] $parent
*/
private function printHowManyPeople($level, $parent) {
global $WT_TREE;
$stats = new Stats($WT_TREE);
$place_count_indi = 0;
$place_count_fam = 0;
if (!isset($parent[$level - 1])) {
$parent[$level - 1] = '';
}
$p_id = $this->setPlaceIdMap($level, $parent);
$indi = $stats->statsPlaces('INDI', false, $p_id);
$fam = $stats->statsPlaces('FAM', false, $p_id);
foreach ($indi as $place) {
$place_count_indi = $place['tot'];
}
foreach ($fam as $place) {
$place_count_fam = $place['tot'];
}
echo '
';
$exists =
Database::prepare("SELECT 1 FROM `##placelocation` WHERE pl_parent_id=?")
->execute(array($deleteRecord))
->fetchOne();
if (!$exists) {
Database::prepare("DELETE FROM `##placelocation` WHERE pl_id=?")
->execute(array($deleteRecord));
} else {
echo '
', I18N::translate('Location not removed: this location contains sub-locations'), '