. */ 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 = '
'; $html .= '
'; $html .= '
'; $html .= $mapdata; $html .= '
'; if (Auth::isAdmin()) { $html .= '
'; $html .= '' . I18N::translate('Google Maps™ preferences') . ''; $html .= ' | ' . I18N::translate('Geographic data') . ''; $html .= ' | ' . I18N::translate('Place check') . ''; $html .= '
'; } $html .= ''; $html .= '
'; } else { $html = '
' . I18N::translate('No map data exists for this individual') . '
'; if (Auth::isAdmin()) { $html .= '
' . I18N::translate('Google Maps™ preferences') . '
'; } } return $html; } /** {@inheritdoc} */ public function hasTabContent() { return Module::getModuleByName('googlemap') || Auth::isAdmin(); } /** {@inheritdoc} */ public function isGrayedOut() { return false; } /** * Return a menu item for this chart. * * @param Individual $individual * * @return Menu */ public function getChartMenu(Individual $individual) { return new Menu( I18N::translate('Pedigree map'), 'module.php?mod=googlemap&mod_action=pedigree_map&rootid=' . $individual->getXref() . '&ged=' . $individual->getTree()->getNameUrl(), 'menu-chart-pedigree_map', array('rel' => 'nofollow') ); } /** * Return a menu item for this chart - for use in individual boxes. * * @param Individual $individual * * @return Menu */ public function getBoxChartMenu(Individual $individual) { return $this->getChartMenu($individual); } /** * A form to edit the module configuration. */ private function config() { $controller = new PageController; $controller ->restrictAccess(Auth::isAdmin()) ->setPageTitle(I18N::translate('Google Maps™')); if (Filter::post('action') === 'update') { $this->setSetting('GM_API_KEY', Filter::post('GM_API_KEY')); $this->setSetting('GM_MAP_TYPE', Filter::post('GM_MAP_TYPE')); $this->setSetting('GM_USE_STREETVIEW', Filter::post('GM_USE_STREETVIEW')); $this->setSetting('GM_MIN_ZOOM', Filter::post('GM_MIN_ZOOM')); $this->setSetting('GM_MAX_ZOOM', Filter::post('GM_MAX_ZOOM')); $this->setSetting('GM_PLACE_HIERARCHY', Filter::post('GM_PLACE_HIERARCHY')); $this->setSetting('GM_PH_XSIZE', Filter::post('GM_PH_XSIZE')); $this->setSetting('GM_PH_YSIZE', Filter::post('GM_PH_YSIZE')); $this->setSetting('GM_PH_MARKER', Filter::post('GM_PH_MARKER')); $this->setSetting('GM_PREFIX_1', Filter::post('GM_PREFIX_1')); $this->setSetting('GM_PREFIX_2', Filter::post('GM_PREFIX_2')); $this->setSetting('GM_PREFIX_3', Filter::post('GM_PREFIX_3')); $this->setSetting('GM_PREFIX_4', Filter::post('GM_PREFIX_4')); $this->setSetting('GM_PREFIX_5', Filter::post('GM_PREFIX_5')); $this->setSetting('GM_PREFIX_6', Filter::post('GM_PREFIX_6')); $this->setSetting('GM_PREFIX_7', Filter::post('GM_PREFIX_7')); $this->setSetting('GM_PREFIX_8', Filter::post('GM_PREFIX_8')); $this->setSetting('GM_PREFIX_9', Filter::post('GM_PREFIX_9')); $this->setSetting('GM_POSTFIX_1', Filter::post('GM_POSTFIX_1')); $this->setSetting('GM_POSTFIX_2', Filter::post('GM_POSTFIX_2')); $this->setSetting('GM_POSTFIX_3', Filter::post('GM_POSTFIX_3')); $this->setSetting('GM_POSTFIX_4', Filter::post('GM_POSTFIX_4')); $this->setSetting('GM_POSTFIX_5', Filter::post('GM_POSTFIX_5')); $this->setSetting('GM_POSTFIX_6', Filter::post('GM_POSTFIX_6')); $this->setSetting('GM_POSTFIX_7', Filter::post('GM_POSTFIX_7')); $this->setSetting('GM_POSTFIX_8', Filter::post('GM_POSTFIX_8')); $this->setSetting('GM_POSTFIX_9', Filter::post('GM_POSTFIX_9')); FlashMessages::addMessage(I18N::translate('The preferences for the module “%s” have been updated.', $this->getTitle()), 'success'); header('Location: ' . WT_BASE_URL . 'module.php?mod=googlemap&mod_action=admin_config'); return; } $controller->pageHeader(); ?>

I18N::translate('Map'), 'SATELLITE' => I18N::translate('Satellite'), 'HYBRID' => I18N::translate('Hybrid'), 'TERRAIN' => I18N::translate('Terrain'), ); echo FunctionsEdit::selectEditControl('GM_MAP_TYPE', $options, null, $this->getSetting('GM_MAP_TYPE'), 'class="form-control"'); ?>
I18N::translate('hide'), true => I18N::translate('show')), $this->getSetting('GM_USE_STREETVIEW'), 'class="radio-inline"') ?>
getSetting('GM_MIN_ZOOM'), 'class="form-control"') ?>
getSetting('GM_MAX_ZOOM'), 'class="form-control"') ?>

getSetting('GM_PLACE_HIERARCHY'), 'class="radio-inline"') ?>
I18N::translate('Standard'), 'G_FLAG' => I18N::translate('Flag'), ); echo FunctionsEdit::selectEditControl('GM_PH_MARKER', $ph_options, null, $this->getSetting('GM_PH_MARKER'), 'class="form-control"'); ?>
getSetting('GM_API_KEY'); return 'https://maps.googleapis.com/maps/api/js?v=3&key=' . $key . '&language=' . WT_LOCALE; } /** * Select a flag. */ private function flags() { global $WT_TREE; $controller = new SimpleController; $controller ->setPageTitle(I18N::translate('Select flag')) ->pageHeader(); $stats = new Stats($WT_TREE); $countries = $stats->getAllCountries(); $action = Filter::post('action'); $countrySelected = Filter::get('countrySelected', null, 'Countries'); $stateSelected = Filter::get('stateSelected', null, 'States'); $country = array(); if (is_dir(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/flags')) { $files = glob(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/flags/*.png'); foreach ($files as $file) { $country[] = basename($file, '.png'); } } if ($countrySelected == 'Countries') { $flags = $country; } else { $flags = array(); if (is_dir(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/' . $countrySelected . '/flags')) { $files = glob(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/' . $countrySelected . '/flags/*.png'); foreach ($files as $file) { $flags[] = basename($file, '.png'); } } } $flags_s = array(); if ($stateSelected != 'States' && is_dir(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/' . $countrySelected . '/flags/' . $stateSelected)) { $files = glob(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/' . $countrySelected . '/flags/' . $stateSelected . '/*.png'); foreach ($files as $file) { $flags_s[] = basename($file, '.png'); } } if ($action == 'ChangeFlag' && Filter::post('FLAGS') !== null) { ?>

"; foreach ($row as $flag) { if ($flag != 'blank') { if (isset($countries[$flag])) { $title = $countries[$flag]; } else { $title = $flag; } } else { $title = $countries['???']; } echo ''; } echo str_repeat('', 4 - count($row)); echo ""; } echo''; ?> "; foreach ($row as $flag) { echo ''; } echo str_repeat('', 4 - count($row)); echo ''; } } ?>
'; echo ''; echo '' . $flag . ''; echo $flag . '
  ', $flag, '

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 '
'; echo '
'; echo '
'; echo '
'; echo '
'; if (Auth::isAdmin()) { 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 . ''; $latlongval[$i] = $this->getLatitudeAndLongitudeFromPlaceLocation($person->getBirthPlace()); if ($latlongval[$i]) { $lat[$i] = (double) strtr($latlongval[$i]->pl_lati, array('N' => '', 'S' => '-', ',' => '.')); $lon[$i] = (double) strtr($latlongval[$i]->pl_long, array('E' => '', 'W' => '-', ',' => '.')); if ($lat[$i] || $lon[$i]) { $marker_number = $curgen; $dups = 0; for ($k = 0; $k < $i; $k++) { if ($latlongval[$i] == $latlongval[$k]) { $dups++; switch ($dups) { case 1: $marker_number = $curgen . 'L'; break; case 2: $marker_number = $curgen . 'R'; break; case 3: $marker_number = $curgen . 'Ls'; break; case 4: $marker_number = $curgen . 'Rs'; break; case 5: //adjust position where markers have same coodinates default: $marker_number = $curgen; $lon[$i] += 0.0025; $lat[$i] += 0.0025; break; } } } $js .= 'var point = new google.maps.LatLng(' . $lat[$i] . ',' . $lon[$i] . ');'; $js .= 'var marker = createMarker(point, "' . Filter::escapeJs($name) . '","' . $data . '", "'; $js .= '
' . $data . '
", "' . $marker_number . '");'; // Construct the polygon lines $to_child = (intval(($i - 1) / 2)); // Draw a line from parent to child if (array_key_exists($to_child, $lat) && $lat[$to_child] != 0 && $lon[$to_child] != 0) { $js .= ' var linecolor; var plines; var lines = [new google.maps.LatLng(' . $lat[$i] . ',' . $lon[$i] . '), new google.maps.LatLng(' . $lat[$to_child] . ',' . $lon[$to_child] . ')]; linecolor = "' . $colored_line[$curgen] . '"; plines = new google.maps.Polygon({ paths: lines, strokeColor: linecolor, strokeOpacity: 0.8, strokeWeight: 3, fillColor: "#FF0000", fillOpacity: 0.1 }); plines.setMap(pm_map);'; } // Extend and fit marker bounds $js .= 'bounds.extend(point);'; $count++; } } } else { $latlongval[$i] = null; } } $js .= ' pm_map.setCenter(bounds.getCenter()); pm_map.fitBounds(bounds); google.maps.event.addListenerOnce(pm_map, "bounds_changed", function(event) { var maxZoom = ' . $this->getSetting('GM_MAX_ZOOM') . '; if (this.getZoom() > maxZoom) { this.setZoom(maxZoom); } }); // Close the sidebar highlight when the infowindow is closed google.maps.event.addListener(infowindow, "closeclick", function() { jQuery(".gm-ancestor[data-marker=" + lastlinkid + "]").toggleClass("gm-ancestor-visited person_box"); lastlinkid = null; }); // put the assembled gm_ancestors_html contents into the gm-ancestors div document.querySelector(".gm-ancestors").innerHTML = gm_ancestors_html; jQuery(".gm-ancestor-link") .on("click", "a", function(e) { e.stopPropagation(); }) .on("click", function(e) { if (lastlinkid != null) { jQuery(".gm-ancestor[data-marker=" + lastlinkid + "]").toggleClass("person_box gm-ancestor-visited"); } var target = jQuery(this).closest(".gm-ancestor").data("marker"); google.maps.event.trigger(gmarkers[target], "click"); }); '; return $js; } /** * Check places for missing data, etc. */ private function adminPlaceCheck() { global $WT_TREE; $gedcom_id = Filter::get('gedcom_id', null, $WT_TREE->getTreeId()); $country = Filter::get('country', '.+', 'XYZ'); $state = Filter::get('state', '.+', 'XYZ'); $matching = Filter::getBool('matching'); $controller = new PageController; $controller ->restrictAccess(Auth::isAdmin()) ->setPageTitle(I18N::translate('Google Maps™')) ->pageHeader(); ?> ', I18N::translate('Place check'), ''; // User options $rows = Database::prepare("SELECT pl_id, pl_place FROM `##placelocation` WHERE pl_level=0 ORDER BY pl_place")->fetchAssoc(); echo '
'; echo ''; echo ''; echo '
'; echo ' '; echo FunctionsEdit::selectEditControl('gedcom_id', Tree::getIdList(), null, $gedcom_id, ' onchange="this.form.submit();" class="form-control"'), ' '; echo ' '; echo ' '; if ($country != 'XYZ') { echo ' '; echo ' '; } echo '
'; echo ''; echo '
'; echo '
'; echo '
'; //Select all '2 PLAC ' tags in the file and create array $place_list = array(); $ged_data = Database::prepare("SELECT i_gedcom FROM `##individuals` WHERE i_gedcom LIKE ? AND i_file=?") ->execute(array("%\n2 PLAC %", $gedcom_id)) ->fetchOneColumn(); foreach ($ged_data as $ged_datum) { preg_match_all('/\n2 PLAC (.+)/', $ged_datum, $matches); foreach ($matches[1] as $match) { $place_list[$match] = true; } } $ged_data = Database::prepare("SELECT f_gedcom FROM `##families` WHERE f_gedcom LIKE ? AND f_file=?") ->execute(array("%\n2 PLAC %", $gedcom_id)) ->fetchOneColumn(); foreach ($ged_data as $ged_datum) { preg_match_all('/\n2 PLAC (.+)/', $ged_datum, $matches); foreach ($matches[1] as $match) { $place_list[$match] = true; } } // Unique list of places $place_list = array_keys($place_list); // Apply_filter if ($country == 'XYZ') { $filter = '.*$'; } else { $filter = preg_quote($country) . '$'; if ($state != 'XYZ') { $filter = preg_quote($state) . ', ' . $filter; } } $place_list = preg_grep('/' . $filter . '/', $place_list); //sort the array, limit to unique values, and count them usort($place_list, '\Fisharebest\Webtrees\I18N::strcasecmp'); $i = count($place_list); //calculate maximum no. of levels to display $x = 0; $max = 0; while ($x < $i) { $levels = explode(",", $place_list[$x]); $parts = count($levels); if ($parts > $max) { $max = $parts; } $x++; } $x = 0; //scripts for edit, add and refresh ?> '; echo '', I18N::translate('Place'), ''; echo '', I18N::translate('Geographic data'), ''; echo ''; for ($cols = 0; $cols < $max; ++$cols) { if ($cols == 0) { echo '', I18N::translate('Country'), ''; } else { echo '', I18N::translate('Level'), ' ', $cols + 1, ''; } } echo ''; for ($cols = 0; $cols < $max; ++$cols) { echo '', GedcomTag::getLabel('PLAC'), ''; echo '', I18N::translate('Latitude'), ''; echo '', I18N::translate('Longitude'), ''; } echo ''; $countrows = 0; $matched = array(); while ($x < $i) { $placestr = ''; $levels = explode(', ', $place_list[$x]); $parts = count($levels); $levels = array_reverse($levels); $placestr .= '' . $place_list[$x] . ""; $gedplace = '' . $placestr . ''; $z = 0; $id = 0; $level = 0; $matched[$x] = 0; // used to exclude places where the gedcom place is matched at all levels $mapstr_edit = ''; $mapstr7 = '\')">'; $mapstr8 = ''; $plac = array(); $lati = array(); $long = array(); while ($z < $parts) { if ($levels[$z] == '') { $levels[$z] = 'unknown'; // GoogleMap module uses "unknown" while GEDCOM uses , , } $placelist = $this->createPossiblePlaceNames($levels[$z], $z + 1); // add the necessary prefix/postfix values to the place name foreach ($placelist as $key => $placename) { $row = Database::prepare("SELECT pl_id, pl_place, pl_long, pl_lati, pl_zoom FROM `##placelocation` WHERE pl_level=? AND pl_parent_id=? AND pl_place LIKE ? ORDER BY pl_place") ->execute(array($z, $id, $placename)) ->fetchOneRow(PDO::FETCH_ASSOC); if (!empty($row['pl_id'])) { $row['pl_placerequested'] = $levels[$z]; // keep the actual place name that was requested so we can display that instead of what is in the db break; } } if ($row['pl_id'] != '') { $id = $row['pl_id']; } if ($row['pl_place'] != '') { $placestr2 = $mapstr_edit . $id . "&level=" . $level . $mapstr3 . $mapstr5 . I18N::translate('Zoom') . ' ' . $row['pl_zoom'] . $mapstr6 . $row['pl_placerequested'] . $mapstr8; if ($row['pl_place'] === 'unknown') { $matched[$x]++; } } else { if ($levels[$z] === 'unknown') { $placestr2 = $mapstr_add . $id . "&level=" . $level . $mapstr3 . $mapstr7 . "" . I18N::translate('unknown') . "" . $mapstr8; $matched[$x]++; } else { $placestr2 = $mapstr_add . $id . "&place_name=" . urlencode($levels[$z]) . "&level=" . $level . $mapstr3 . $mapstr7 . '' . $levels[$z] . '' . $mapstr8; $matched[$x]++; } } $plac[$z] = '' . $placestr2 . ''; if ($row['pl_lati'] == '0' && $row['pl_long'] == '0') { $lati[$z] = '0'; } elseif ($row['pl_lati'] != '') { $lati[$z] = '' . $row['pl_lati'] . ''; } else { $lati[$z] = ''; $matched[$x]++; } if ($row['pl_lati'] == '0' && $row['pl_long'] == '0') { $long[$z] = '0'; } elseif ($row['pl_long'] != '') { $long[$z] = '' . $row['pl_long'] . ''; } else { $long[$z] = ''; $matched[$x]++; } $level++; $mapstr3 = $mapstr3 . "&parent[" . $z . "]=" . Filter::escapeJs($row['pl_placerequested']); $mapstr4 = $mapstr4 . "&parent[" . $z . "]=" . Filter::escapeJs($levels[$z]); $z++; } if ($matching) { $matched[$x] = 1; } if ($matched[$x] != 0) { echo $gedplace; $z = 0; while ($z < $max) { if ($z < $parts) { echo $plac[$z]; echo $lati[$z]; echo $long[$z]; } else { echo ''; echo ''; echo ''; } $z++; } echo ''; $countrows++; } $x++; } echo ''; echo ''; echo ''; echo '', /* I18N: A count of places */ I18N::translate('Total places: %s', I18N::number($countrows)), ''; echo ''; echo ''; echo ''; } /** * Does an individual (or their spouse-families) have any facts with places? * * @param Individual $individual * * @return bool */ private function checkMapData(Individual $individual) { $statement = Database::prepare( "SELECT COUNT(*) FROM `##placelinks` WHERE pl_gid = :xref AND pl_file = :tree_id" ); $args = array( 'xref' => $individual->getXref(), 'tree_id' => $individual->getTree()->getTreeId(), ); if ($statement->execute($args)->fetchOne()) { return true; } foreach ($individual->getSpouseFamilies() as $family) { $args['xref'] = $family->getXref(); if ($statement->execute($args)->fetchOne()) { return true; } } return false; } /** * Remove prefixes from a place name to allow it to be matched. * * @param string $prefix_list * @param string $place * @param string[] $placelist * * @return string[] */ private function removePrefixFromPlaceName($prefix_list, $place, $placelist) { if ($prefix_list) { foreach (explode(';', $prefix_list) as $prefix) { if ($prefix && substr($place, 0, strlen($prefix) + 1) == $prefix . ' ') { $placelist[] = substr($place, strlen($prefix) + 1); } } } return $placelist; } /** * Remove suffixes from a place name to allow it to be matched. * * @param string $suffix_list * @param string $place * @param string[] $placelist * * @return string[] */ private function removeSuffixFromPlaceName($suffix_list, $place, $placelist) { if ($suffix_list) { foreach (explode(';', $suffix_list) as $postfix) { if ($postfix && substr($place, -strlen($postfix) - 1) == ' ' . $postfix) { $placelist[] = substr($place, 0, strlen($place) - strlen($postfix) - 1); } } } return $placelist; } /** * Remove prefixes and sufixes to allow place names to be matched. * * @param string $prefix_list * @param string $suffix_list * @param string $place * @param string[] $placelist * * @return string[] */ private function removePrefixAndSuffixFromPlaceName($prefix_list, $suffix_list, $place, $placelist) { if ($prefix_list && $suffix_list) { foreach (explode(';', $prefix_list) as $prefix) { foreach (explode(';', $suffix_list) as $postfix) { if ($prefix && $postfix && substr($place, 0, strlen($prefix) + 1) == $prefix . ' ' && substr($place, -strlen($postfix) - 1) == ' ' . $postfix) { $placelist[] = substr($place, strlen($prefix) + 1, strlen($place) - strlen($prefix) - strlen($postfix) - 2); } } } } return $placelist; } /** * Match placenames with different prefixes and suffixes. * * @param string $placename * @param int $level * * @return string[] */ private function createPossiblePlaceNames($placename, $level) { $retlist = array(); if ($level <= 9) { $retlist = $this->removePrefixAndSuffixFromPlaceName($this->getSetting('GM_PREFIX_' . $level), $this->getSetting('GM_POSTFIX_' . $level), $placename, $retlist); // Remove both $retlist = $this->removePrefixFromPlaceName($this->getSetting('GM_PREFIX_' . $level), $placename, $retlist); // Remove prefix $retlist = $this->removeSuffixFromPlaceName($this->getSetting('GM_POSTFIX_' . $level), $placename, $retlist); // Remove suffix } $retlist[] = $placename; // Exact return $retlist; } /** * Get the map co-ordinates of a place. * * @param string $place * * @return null|\stdClass */ private function getLatitudeAndLongitudeFromPlaceLocation($place) { $parent = explode(',', $place); $parent = array_reverse($parent); $place_id = 0; $num_parent = count($parent); for ($i = 0; $i < $num_parent; $i++) { $parent[$i] = trim($parent[$i]); if (empty($parent[$i])) { $parent[$i] = 'unknown'; // GoogleMap module uses "unknown" while GEDCOM uses , , } $placelist = $this->createPossiblePlaceNames($parent[$i], $i + 1); foreach ($placelist as $placename) { $pl_id = Database::prepare( "SELECT pl_id FROM `##placelocation` WHERE pl_level=? AND pl_parent_id=? AND pl_place LIKE ? ORDER BY pl_place" )->execute(array($i, $place_id, $placename))->fetchOne(); if (!empty($pl_id)) { break; } } if (empty($pl_id)) { break; } $place_id = $pl_id; } return Database::prepare( "SELECT sv_lati, sv_long, sv_bearing, sv_elevation, sv_zoom, pl_lati, pl_long, pl_zoom, pl_icon, pl_level" . " FROM `##placelocation`" . " WHERE pl_id = ?" . " ORDER BY pl_place" )->execute(array($place_id))->fetchOneRow(); } /** * @param Fact $fact * * @return array */ private function getPlaceData(Fact $fact) { $result = array(); $has_latitude = preg_match('/\n4 LATI (.+)/', $fact->getGedcom(), $match1); $has_longitude = preg_match('/\n4 LONG (.+)/', $fact->getGedcom(), $match2); // If co-ordinates are stored in the GEDCOM then use them if ($has_latitude && $has_longitude) { $result = array( 'index' => 'ID' . $match1[1] . $match2[1], 'mapdata' => array( 'class' => 'optionbox', 'place' => $fact->getPlace()->getFullName(), 'tooltip' => $fact->getPlace()->getGedcomName(), 'lat' => strtr($match1[1], array('N' => '', 'S' => '-', ',' => '.')), 'lng' => strtr($match2[1], array('E' => '', 'W' => '-', ',' => '.')), 'pl_icon' => '', 'pl_zoom' => '0', 'sv_bearing' => '0', 'sv_elevation' => '0', 'sv_lati' => '0', 'sv_long' => '0', 'sv_zoom' => '0', 'events' => '', ), ); } else { $place_location = $this->getLatitudeAndLongitudeFromPlaceLocation($fact->getPlace()->getGedcomName()); if ($place_location && $place_location->pl_lati && $place_location->pl_long) { $result = array( 'index' => 'ID' . $place_location->pl_lati . $place_location->pl_long, 'mapdata' => array( 'class' => 'optionbox', 'place' => $fact->getPlace()->getFullName(), 'tooltip' => $fact->getPlace()->getGedcomName(), 'lat' => strtr($place_location->pl_lati, array('N' => '', 'S' => '-', ',' => '.')), 'lng' => strtr($place_location->pl_long, array('E' => '', 'W' => '-', ',' => '.')), 'pl_icon' => $place_location->pl_icon, 'pl_zoom' => $place_location->pl_zoom, 'sv_bearing' => $place_location->sv_bearing, 'sv_elevation' => $place_location->sv_elevation, 'sv_lati' => $place_location->sv_lati, 'sv_long' => $place_location->sv_long, 'sv_zoom' => $place_location->sv_zoom, 'events' => '', ), ); } } return $result; } /** * Build a map for an individual. * * @param Individual $indi */ private function buildIndividualMap(Individual $indi) { $GM_MAX_ZOOM = $this->getSetting('GM_MAX_ZOOM'); $facts = $indi->getFacts(); foreach ($indi->getSpouseFamilies() as $family) { $facts = array_merge($facts, $family->getFacts()); // Add birth of children from this family to the facts array foreach ($family->getChildren() as $child) { $facts[] = $child->getFirstFact('BIRT'); } } $facts = array_values(array_filter($facts, function ($item) { // remove null facts (child without birth event) and // facts without places return !is_null($item) && !$item->getPlace()->isEmpty(); })); Functions::sortFacts($facts); // At this point we have an array of valid sorted facts // so now build the data structures needed for the map display $events = array(); $unique_places = array(); foreach ($facts as $fact) { $place_data = $this->getPlaceData($fact); if (!empty($place_data)) { $index = $place_data['index']; if ($place_data['mapdata']['pl_zoom']) { $GM_MAX_ZOOM = min($GM_MAX_ZOOM, $place_data['mapdata']['pl_zoom']); } // Produce the html for the sidebar $parent = $fact->getParent(); if ($parent instanceof Individual && $parent->getXref() !== $indi->getXref()) { // Childs birth $name = '' . $parent->getFullName() . ''; $label = strtr($parent->getSex(), array('F' => I18N::translate('Birth of a daughter'), 'M' => I18N::translate('Birth of a son'), 'U' => I18N::translate('Birth of a child'))); $class = 'person_box' . strtr($parent->getSex(), array('F' => 'F', 'M' => '', 'U' => 'NN')); $evtStr = '
' . $label . '
' . $name . '
' . $fact->getDate()->display(true) . '
'; } else { $spouse = $parent instanceof Family ? $parent->getSpouse($indi) : null; $name = $spouse ? '' . $spouse->getFullName() . '' : ''; $label = $fact->getLabel(); $class = 'optionbox'; if ($fact->getValue() && $spouse) { $evtStr = '
' . $label . '
' . $fact->getValue() . '
' . $name . '' . $fact->getDate()->display(true) . '
'; } elseif ($spouse) { $evtStr = '
' . $label . '
' . $name . '
' . $fact->getDate()->display(true) . '
'; } elseif ($fact->getValue()) { $evtStr = '
' . $label . '
' . $fact->getValue() . '
' . $fact->getDate()->display(true) . '
'; } else { $evtStr = '
' . $label . '
' . $fact->getDate()->display(true) . '
'; } } if (empty($unique_places[$index])) { $unique_places[$index] = $place_data['mapdata']; } $unique_places[$index]['events'] .= $evtStr; $events[] = array( 'class' => $class, 'fact_label' => $label, 'date' => $fact->getDate()->display(true), 'info' => $fact->getValue(), 'name' => $name, 'place' => '' . $fact->getPlace()->getFullName() . '', 'placeid' => $index, ); } } if (!empty($events)) { $places = array_keys($unique_places); ob_start(); // Create the normal googlemap sidebar of events and children echo '
'; foreach ($events as $event) { $index = array_search($event['placeid'], $places); echo ''; echo ''; echo ''; echo ''; } echo '
'; echo '', $event['fact_label'], ''; if ($event['info']) { echo '
', Filter::escapeHtml($event['info']), '
'; } if ($event['name']) { echo '
', $event['name'], '
'; } echo '
', $event['place'], '
'; if ($event['date']) { echo '
', $event['date'], '
'; } echo '
'; // *** ENABLE STREETVIEW *** $STREETVIEW = (bool) $this->getSetting('GM_USE_STREETVIEW'); ?> createPossiblePlaceNames($par[$i], $i + 1); foreach ($placelist as $key => $placename) { $pl_id = (int) Database::prepare( "SELECT pl_id FROM `##placelocation` WHERE pl_level = :level AND pl_parent_id = :parent_id AND pl_place LIKE :placename" )->execute(array( 'level' => $i, 'parent_id' => $place_id, 'placename' => $placename, ))->fetchOne(); if ($pl_id) { break; } } if (!$pl_id) { break; } $place_id = $pl_id; } return $place_id; } /** * Get the place ID. * * @param string $place * * @return int */ private function getPlaceId($place) { global $WT_TREE; $par = explode(',', $place); $par = array_reverse($par); $place_id = 0; $pl_id = 0; $num_par = count($par); for ($i = 0; $i < $num_par; $i++) { $par[$i] = trim($par[$i]); $placelist = $this->createPossiblePlaceNames($par[$i], $i + 1); foreach ($placelist as $placename) { $pl_id = (int) Database::prepare( "SELECT p_id FROM `##places` WHERE p_parent_id = :place_id AND p_file = :tree_id AND p_place = :placename" )->execute(array( 'place_id' => $place_id, 'tree_id' => $WT_TREE->getTreeId(), 'placename' => $placename, ))->fetchOne(); if ($pl_id) { break; } } if (!$pl_id) { break; } $place_id = $pl_id; } return $place_id; } /** * Set the place IDs. * * @param int $level * @param string[] $parent * * @return int */ private function setPlaceIdMap($level, $parent) { $fullplace = ''; if ($level == 0) { return 0; } else { for ($i = 1; $i <= $level; $i++) { $fullplace .= $parent[$level - $i] . ', '; } $fullplace = substr($fullplace, 0, -2); return $this->getPlaceId($fullplace); } } /** * Set the map level. * * @param int $level * @param string[] $parent * * @return int */ private function setLevelMap($level, $parent) { $fullplace = ''; if ($level == 0) { return 0; } else { for ($i = 1; $i <= $level; $i++) { if ($parent[$level - $i] != '') { $fullplace .= $parent[$level - $i] . ', '; } else { $fullplace .= 'Unknown, '; } } $fullplace = substr($fullplace, 0, -2); return $this->getPlaceLocationId($fullplace); } } /** * Called by placelist.php */ public function createMap() { global $level, $levelm, $plzoom, $WT_TREE; Database::updateSchema(self::SCHEMA_MIGRATION_PREFIX, self::SCHEMA_SETTING_NAME, self::SCHEMA_TARGET_VERSION); $STREETVIEW = (bool) $this->getSetting('GM_USE_STREETVIEW'); $parent = Filter::getArray('parent'); $levelm = $this->setLevelMap($level, $parent); $latlng = Database::prepare("SELECT pl_place, pl_id, pl_lati, pl_long, pl_zoom, sv_long, sv_lati, sv_bearing, sv_elevation, sv_zoom FROM `##placelocation` WHERE pl_id=?") ->execute(array($levelm)) ->fetch(PDO::FETCH_ASSOC); echo ''; echo ''; if ($STREETVIEW) { echo '
'; if ($STREETVIEW && $level != 0) { // Leave space for the Street View buttons, so that the maps align vertically echo '
'; echo ''; $plzoom = $latlng['pl_zoom']; // Map zoom level if (Auth::isAdmin()) { $placecheck_url = 'module.php?mod=googlemap&mod_action=admin_placecheck'; if ($parent && isset($parent[0])) { $placecheck_url .= '&country=' . $parent[0]; if (isset($parent[1])) { $placecheck_url .= '&state=' . $parent[1]; } } $adminplaces_url = 'module.php?mod=googlemap&mod_action=admin_places'; if ($latlng && isset($latlng['pl_id'])) { $adminplaces_url .= '&parent=' . $latlng['pl_id']; } $update_places_url = 'admin_trees_places.php?ged=' . $WT_TREE->getNameHtml() . '&search=' . urlencode(implode(', ', array_reverse($parent))); echo ''; } echo '
'; 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 '

', I18N::translate('Individuals'), ': ', $place_count_indi, ', ', I18N::translate('Families'), ': ', $place_count_fam; } /** * Print the flags and markers. * * @param string[] $place2 * @param int $level * @param string[] $parent * @param int $levelm * @param string $linklevels */ private function printGoogleMapMarkers($place2, $level, $parent, $levelm, $linklevels) { echo 'var icon_url = null;'; if (!$place2['lati'] || !$place2['long']) { echo 'var icon_url ="' . WT_STATIC_URL . WT_MODULES_DIR . 'googlemap/images/marker_yellow.png";'; echo 'var point = new google.maps.LatLng(0, 0);'; echo 'var marker = createMarker(point, "

'; } else { echo addslashes($place2['place']), '\">
'; } if (($place2['icon'] !== null) && ($place2['icon'] !== '')) { echo '  '; } if ($place2['place'] == 'Unknown') { echo I18N::translate('unknown'); } else { echo addslashes($place2['place']); } echo '
'; $parent[$level] = $place2['place']; $this->printHowManyPeople($level + 1, $parent); echo '
', I18N::translate('This place has no coordinates'); if (Auth::isAdmin()) { echo '
', I18N::translate('Geographic data'), ''; } echo '
", icon_url, "', str_replace(array('‎', '‏'), array(WT_UTF8_LRM, WT_UTF8_RLM), addslashes($place2['place'])), '");'; } else { $lati = strtr($place2['lati'], array('N' => '', 'S' => '-', ',' => '.')); $long = strtr($place2['long'], array('E' => '', 'W' => '-', ',' => '.')); //delete leading zero if ($lati >= 0) { $lati = abs($lati); } elseif ($lati < 0) { $lati = '-' . abs($lati); } if ($long >= 0) { $long = abs($long); } elseif ($long < 0) { $long = '-' . abs($long); } if ($place2['icon'] !== null && $place2['icon'] !== '' && $this->getSetting('GM_PH_MARKER') === 'G_FLAG') { echo 'icon_url = "', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/', $place2['icon'], '";'; } echo 'var point = new google.maps.LatLng(', $lati, ', ', $long, ');'; echo 'var marker = createMarker(point, "

'; if ($place2['icon'] !== null && $place2['icon'] !== '') { echo '  '; } if ($place2['place'] === 'Unknown') { echo I18N::translate('unknown'); } else { echo Filter::escapeJs($place2['place']); } echo '
'; $parent[$level] = $place2['place']; $this->printHowManyPeople($level + 1, $parent); echo '
", icon_url, "', Filter::escapeJs($place2['place']), '");'; } } /** * Called by placelist.php * * @param int $numfound * @param int $level * @param string[] $parent * @param string $linklevels * @param string[] $place_names */ public function mapScripts($numfound, $level, $parent, $linklevels, $place_names) { global $plzoom, $controller; $controller->addInlineJavascript(' jQuery("head").append(\'\'); var numMarkers = "' . $numfound . '"; var mapLevel = "' . $level . '"; var placezoom = "' . $plzoom . '"; var infowindow = new google.maps.InfoWindow({ // size: new google.maps.Size(150,50), // maxWidth: 600 }); var map_center = new google.maps.LatLng(0,0); var map = ""; var bounds = new google.maps.LatLngBounds (); var markers = []; var gmarkers = []; var i = 0; // Create the map and mapOptions var mapOptions = { zoom: 8, center: map_center, mapTypeId: google.maps.MapTypeId.' . $this->getSetting('GM_MAP_TYPE') . ', mapTypeControlOptions: { style: google.maps.MapTypeControlStyle.DROPDOWN_MENU // DEFAULT, DROPDOWN_MENU, HORIZONTAL_BAR }, navigationControl: true, 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 }; map = new google.maps.Map(document.getElementById("place_map"), mapOptions); // Close any infowindow when map is clicked google.maps.event.addListener(map, "click", function() { infowindow.close(); }); // If only one marker, set zoom level to that of place in database if (mapLevel != 0) { var pointZoom = placezoom; } else { var pointZoom = 1; } // Creates a marker whose info window displays the given name function createMarker(point, html, icon, name) { // Choose icon ============ if (icon && ' . $level . '<=3) { if (icon != "' . WT_STATIC_URL . WT_MODULES_DIR . 'googlemap/images/marker_yellow.png") { var iconImage = { url: icon, size: new google.maps.Size(25, 15), origin: new google.maps.Point(0,0), anchor: new google.maps.Point(12, 15) }; } else { var iconImage = { url: icon, size: new google.maps.Size(20, 34), origin: new google.maps.Point(0,0), anchor: new google.maps.Point(9, 34) }; } } else { var iconImage = { url: "https://maps.google.com/mapfiles/marker.png", size: new google.maps.Size(20, 34), origin: new google.maps.Point(0,0), anchor: new google.maps.Point(9, 34) }; } var posn = new google.maps.LatLng(0,0); var marker = new google.maps.Marker({ position: point, icon: iconImage, map: map, title: name }); // Show this markers name in the info window when it is clicked google.maps.event.addListener(marker, "click", function() { infowindow.close(); infowindow.setContent(html); infowindow.open(map, marker); }); // === Store the tab, category and event info as marker properties === marker.mypoint = point; marker.mytitle = name; marker.myposn = posn; gmarkers.push(marker); bounds.extend(marker.position); // If only one marker use database place zoom level rather than fitBounds of markers if (numMarkers > 1) { map.fitBounds(bounds); } else { map.setCenter(bounds.getCenter()); map.setZoom(parseFloat(pointZoom)); } return marker; } '); $levelm = $this->setLevelMap($level, $parent); //create markers ob_start(); if ($numfound == 0 && $level > 0) { // show the current place on the map $place = Database::prepare("SELECT pl_id AS place_id, pl_place AS place, pl_lati AS lati, pl_long AS `long`, pl_zoom AS zoom, pl_icon AS icon FROM `##placelocation` WHERE pl_id=?") ->execute(array($levelm)) ->fetch(PDO::FETCH_ASSOC); if ($place) { // re-calculate the hierarchy information required to display the current place $thisloc = $parent; array_pop($thisloc); $thislevel = $level - 1; $thislinklevels = substr($linklevels, 0, strrpos($linklevels, '&')); $this->printGoogleMapMarkers($place, $thislevel, $thisloc, $place['place_id'], $thislinklevels); } } // display any sub-places $placeidlist = array(); foreach ($place_names as $placename) { $thisloc = $parent; $thisloc[] = $placename; $this_levelm = $this->setLevelMap($level + 1, $thisloc); if ($this_levelm) { $placeidlist[] = $this_levelm; } } // flip the array (thus removing duplicates) $placeidlist = array_flip($placeidlist); // remove entry for parent location unset($placeidlist[$levelm]); if (!empty($placeidlist)) { // the keys are all we care about (this reverses the earlier array_flip, and ensures there are no "holes" in the array) $placeidlist = array_keys($placeidlist); // note: this implode/array_fill code generates one '?' for each entry in the $placeidlist array $placelist = Database::prepare( "SELECT pl_id as place_id, pl_place as place, pl_lati as lati, pl_long as `long`, pl_zoom as zoom, pl_icon as icon" . " FROM `##placelocation` WHERE pl_id IN (" . implode(',', array_fill(0, count($placeidlist), '?')) . ')' )->execute($placeidlist) ->fetchAll(PDO::FETCH_ASSOC); foreach ($placelist as $place) { $this->printGoogleMapMarkers($place, $level, $parent, $place['place_id'], $linklevels); } } $controller->addInlineJavascript(ob_get_clean()); } /** * Take a place id and find its place in the hierarchy * Input: place ID * Output: ordered array of id=>name values, starting with the Top level * e.g. 0=>"Top level", 16=>"England", 19=>"London", 217=>"Westminster" * * @param int $id * * @return string[] */ private function placeIdToHierarchy($id) { $statement = Database::prepare("SELECT pl_parent_id, pl_place FROM `##placelocation` WHERE pl_id=?"); $arr = array(); while ($id != 0) { $row = $statement->execute(array($id))->fetchOneRow(); $arr = array($id => $row->pl_place) + $arr; $id = $row->pl_parent_id; } return $arr; } /** * Get the highest index. * * @return int */ private function getHighestIndex() { return (int) Database::prepare("SELECT MAX(pl_id) FROM `##placelocation`")->fetchOne(); } /** * Get the highest level. * * @return int */ private function getHighestLevel() { return (int) Database::prepare("SELECT MAX(pl_level) FROM `##placelocation`")->fetchOne(); } /** * Find all of the places in the hierarchy * * @param int $parent_id * @param bool $inactive * * @return array[] */ private function getPlaceListLocation($parent_id, $inactive = false) { if ($inactive) { $rows = Database::prepare( "SELECT pl_id, pl_place, pl_lati, pl_long, pl_zoom, pl_icon" . " FROM `##placelocation`" . " WHERE pl_parent_id = :parent_id" . " ORDER BY pl_place COLLATE :collation" )->execute(array( 'parent_id' => $parent_id, 'collation' => I18N::collation(), ))->fetchAll(); } else { $rows = Database::prepare( "SELECT DISTINCT pl_id, pl_place, pl_lati, pl_long, pl_zoom, pl_icon" . " FROM `##placelocation`" . " INNER JOIN `##places` ON `##placelocation`.pl_place=`##places`.p_place" . " WHERE pl_parent_id = :parent_id" . " ORDER BY pl_place COLLATE :collation" )->execute(array( 'parent_id' => $parent_id, 'collation' => I18N::collation(), ))->fetchAll(); } $placelist = array(); foreach ($rows as $row) { $placelist[] = array( 'place_id' => $row->pl_id, 'place' => $row->pl_place, 'lati' => $row->pl_lati, 'long' => $row->pl_long, 'zoom' => $row->pl_zoom, 'icon' => $row->pl_icon, ); } return $placelist; } /** * Set the output level. * * @param int $parent_id */ private function outputLevel($parent_id) { $tmp = $this->placeIdToHierarchy($parent_id); $maxLevel = $this->getHighestLevel(); if ($maxLevel > 8) { $maxLevel = 8; } $prefix = implode(';', $tmp); if ($prefix != '') { $prefix .= ';'; } $suffix = str_repeat(';', $maxLevel - count($tmp)); $level = count($tmp); $rows = Database::prepare( "SELECT pl_id, pl_place, pl_long, pl_lati, pl_zoom, pl_icon FROM `##placelocation` WHERE pl_parent_id=? ORDER BY pl_place" )->execute(array($parent_id))->fetchAll(); foreach ($rows as $row) { echo $level, ';', $prefix, $row->pl_place, $suffix, ';', $row->pl_long, ';', $row->pl_lati, ';', $row->pl_zoom, ';', $row->pl_icon, "\r\n"; if ($level < $maxLevel) { $this->outputLevel($row->pl_id); } } } /** * recursively find all of the csv files on the server * * @param string $path * * @return string[] */ private function findFiles($path) { $placefiles = array(); try { $di = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); $it = new \RecursiveIteratorIterator($di); foreach ($it as $file) { if ($file->getExtension() == "csv") { $placefiles[] = '/' . $file->getFilename(); } } } catch (\Exception $e) { Log::addErrorLog(basename($e->getFile()) . ' - line: ' . $e->getLine() . ' - ' . $e->getMessage()); } return $placefiles; } /** * Edit places. */ private function placesEdit() { $GM_MAX_ZOOM = $this->getSetting('GM_MAX_ZOOM'); $action = Filter::post('action', null, Filter::get('action')); $placeid = Filter::post('placeid', null, Filter::get('placeid')); $place_name = Filter::post('place_name', null, Filter::get('place_name')); $placeid = (int) $placeid; // Convert empty string to zero $place_icon = ''; // Update Street View fields fields if ($action === 'update_sv_params' && Auth::isAdmin() && Filter::checkCsrf()) { Database::prepare( "UPDATE `##placelocation`" . " SET sv_lati = :sv_latitude, sv_long = :sv_longitude, sv_bearing = :sv_bearing, sv_elevation = :sv_elevation, sv_zoom = :sv_zoom" . " WHERE pl_id = :place_id" )->execute(array( 'sv_latitude' => (float) Filter::post('sv_latiText'), 'sv_longitude' => (float) Filter::post('sv_longText'), 'sv_bearing' => (float) Filter::post('sv_bearText'), 'sv_elevation' => (float) Filter::post('sv_elevText'), 'sv_zoom' => (float) Filter::post('sv_zoomText'), 'place_id' => $placeid, )); // TODO - submit this data via AJAX, so we won't need to redraw the page. header('Location: ' . Filter::post('destination', null, 'index.php')); return; } $controller = new SimpleController; $controller ->restrictAccess(Auth::isAdmin()) ->setPageTitle(I18N::translate('Geographic data')) ->addInlineJavascript('jQuery("", {rel: "stylesheet", type: "text/css", href: "' . WT_STATIC_URL . WT_MODULES_DIR . 'googlemap/css/wt_v3_googlemap.css"}).appendTo("head");') ->pageHeader(); $where_am_i = $this->placeIdToHierarchy($placeid); $level = count($where_am_i); if ($action == 'addrecord' && Auth::isAdmin()) { $statement = Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_long, pl_lati, pl_zoom, pl_icon) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); if ((Filter::post('LONG_CONTROL') == '') || (Filter::post('NEW_PLACE_LONG') == '') || (Filter::post('NEW_PLACE_LATI') == '')) { $statement->execute(array($this->getHighestIndex() + 1, $placeid, $level, Filter::post('NEW_PLACE_NAME'), null, null, Filter::post('NEW_ZOOM_FACTOR'), Filter::post('icon'))); } else { $statement->execute(array($this->getHighestIndex() + 1, $placeid, $level, Filter::post('NEW_PLACE_NAME'), Filter::post('LONG_CONTROL') . Filter::post('NEW_PLACE_LONG'), Filter::post('LATI_CONTROL') . Filter::post('NEW_PLACE_LATI'), Filter::post('NEW_ZOOM_FACTOR'), Filter::post('icon'))); } $controller->addInlineJavascript('closePopupAndReloadParent();'); return; } if ($action == 'updaterecord' && Auth::isAdmin()) { $statement = Database::prepare("UPDATE `##placelocation` SET pl_place=?, pl_lati=?, pl_long=?, pl_zoom=?, pl_icon=? WHERE pl_id=?"); if ((Filter::post('LONG_CONTROL') == '') || (Filter::post('NEW_PLACE_LONG') == '') || (Filter::post('NEW_PLACE_LATI') == '')) { $statement->execute(array(Filter::post('NEW_PLACE_NAME'), null, null, (int) Filter::post('NEW_ZOOM_FACTOR'), Filter::post('icon'), $placeid)); } else { $statement->execute(array(Filter::post('NEW_PLACE_NAME'), Filter::post('LATI_CONTROL') . Filter::post('NEW_PLACE_LATI'), Filter::post('LONG_CONTROL') . Filter::post('NEW_PLACE_LONG'), Filter::post('NEW_ZOOM_FACTOR'), Filter::post('icon'), $placeid)); } $controller->addInlineJavascript('closePopupAndReloadParent();'); return; } if ($action === 'update') { // --- find the place in the file $row = Database::prepare("SELECT pl_place, pl_lati, pl_long, pl_icon, pl_parent_id, pl_level, pl_zoom FROM `##placelocation` WHERE pl_id=?") ->execute(array($placeid)) ->fetchOneRow(); $place_name = $row->pl_place; $place_icon = $row->pl_icon; $selected_country = explode("/", $place_icon); if (isset($selected_country[1]) && $selected_country[1] !== 'flags') { $selected_country = $selected_country[1]; } else { $selected_country = 'Countries'; } $parent_id = $row->pl_parent_id; $level = $row->pl_level; $zoomfactor = $row->pl_zoom; $parent_lati = 0.0; $parent_long = 0.0; if ($row->pl_lati !== null && $row->pl_long !== null) { $place_lati = (float) (str_replace(array('N', 'S', ','), array('', '-', '.'), $row->pl_lati)); $place_long = (float) (str_replace(array('E', 'W', ','), array('', '-', '.'), $row->pl_long)); } else { $place_lati = 0.0; $place_long = 0.0; $zoomfactor = 1; } do { $row = Database::prepare("SELECT pl_lati, pl_long, pl_parent_id, pl_zoom FROM `##placelocation` WHERE pl_id=?") ->execute(array($parent_id)) ->fetchOneRow(); if (!$row) { break; } if ($row->pl_lati !== null && $row->pl_long !== null) { $parent_lati = (float) (str_replace(array('N', 'S', ','), array('', '-', '.'), $row->pl_lati)); $parent_long = (float) (str_replace(array('E', 'W', ','), array('', '-', '.'), $row->pl_long)); if ($zoomfactor == 1) { $zoomfactor = $row->pl_zoom; } } $parent_id = $row->pl_parent_id; } while ($row->pl_parent_id != 0 && $row->pl_lati === null && $row->pl_long === null); echo '', Filter::escapeHtml(str_replace('Unknown', I18N::translate('unknown'), implode(I18N::$list_separator, array_reverse($where_am_i, true)))), '
'; } if ($action === 'add') { // --- find the parent place in the file if ($placeid != 0) { $place_lati = 0.0; $place_long = 0.0; $zoomfactor = 1; $parent_lati = 0.0; $parent_long = 0.0; $parent_id = $placeid; do { $row = Database::prepare("SELECT pl_lati, pl_long, pl_parent_id, pl_zoom, pl_level FROM `##placelocation` WHERE pl_id=?") ->execute(array($parent_id)) ->fetchOneRow(); if ($row->pl_lati !== null && $row->pl_long !== null) { $parent_lati = strtr($row->pl_lati, array('N' => '', 'S' => '-', ',' => '.')); $parent_long = strtr($row->pl_long, array('E' => '', 'W' => '-', ',' => '.')); $zoomfactor = min($row->pl_zoom, $GM_MAX_ZOOM); $level = $row->pl_level + 1; } $parent_id = $row->pl_parent_id; } while ($row->pl_parent_id != 0 && $row->pl_lati === null && $row->pl_long === null); } else { $place_lati = 0.0; $place_long = 0.0; $parent_lati = 0.0; $parent_long = 0.0; $parent_id = 0; $level = 0; $zoomfactor = $this->getSetting('GM_MIN_ZOOM'); } $selected_country = 'Countries'; if ($place_name == '') { echo '', I18N::translate('unknown'); } else { echo '', $place_name; } if (count($where_am_i) > 0) { echo ', ', Filter::escapeHtml(str_replace('Unknown', I18N::translate('unknown'), implode(I18N::$list_separator, array_reverse($where_am_i, true)))), '
'; } echo '

'; } ?>
|
<?php echo /* I18N: The emblem of a country or region */ I18N::translate('Flag') ?>




restrictAccess(Auth::isAdmin()); if ($action == 'ExportFile' && Auth::isAdmin()) { $tmp = $this->placeIdToHierarchy($parent); $maxLevel = $this->getHighestLevel(); if ($maxLevel > 8) { $maxLevel = 8; } $tmp[0] = 'places'; $outputFileName = preg_replace('/[:;\/\\\(\)\{\}\[\] $]/', '_', implode('-', $tmp)) . '.csv'; header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . $outputFileName . '"'); echo '"', I18N::translate('Level'), '";"', I18N::translate('Country'), '";'; if ($maxLevel > 0) { echo '"', I18N::translate('State'), '";'; } if ($maxLevel > 1) { echo '"', I18N::translate('County'), '";'; } if ($maxLevel > 2) { echo '"', I18N::translate('City'), '";'; } if ($maxLevel > 3) { echo '"', I18N::translate('Place'), '";'; } if ($maxLevel > 4) { echo '"', I18N::translate('Place'), '";'; } if ($maxLevel > 5) { echo '"', I18N::translate('Place'), '";'; } if ($maxLevel > 6) { echo '"', I18N::translate('Place'), '";'; } if ($maxLevel > 7) { echo '"', I18N::translate('Place'), '";'; } echo '"', I18N::translate('Longitude'), '";"', I18N::translate('Latitude'), '";'; echo '"', I18N::translate('Zoom level'), '";"', I18N::translate('Icon'), '";', WT_EOL; $this->outputLevel($parent); return; } $controller ->setPageTitle(I18N::translate('Google Maps™')) ->pageHeader(); ?> ' . I18N::translate('Geographic data') . ''; $placelist = array(); $j = 0; $gedcom_records = Database::prepare("SELECT i_gedcom FROM `##individuals` WHERE i_file=? UNION ALL SELECT f_gedcom FROM `##families` WHERE f_file=?") ->execute(array($WT_TREE->getTreeId(), $WT_TREE->getTreeId())) ->fetchOneColumn(); foreach ($gedcom_records as $gedrec) { $i = 1; $placerec = Functions::getSubRecord(2, '2 PLAC', $gedrec, $i); while (!empty($placerec)) { if (preg_match("/2 PLAC (.+)/", $placerec, $match)) { $placelist[$j] = array(); $placelist[$j]['place'] = trim($match[1]); if (preg_match("/4 LATI (.*)/", $placerec, $match)) { $placelist[$j]['lati'] = trim($match[1]); if (($placelist[$j]['lati'][0] != 'N') && ($placelist[$j]['lati'][0] != 'S')) { if ($placelist[$j]['lati'] < 0) { $placelist[$j]['lati'][0] = 'S'; } else { $placelist[$j]['lati'] = 'N' . $placelist[$j]['lati']; } } } else { $placelist[$j]['lati'] = null; } if (preg_match("/4 LONG (.*)/", $placerec, $match)) { $placelist[$j]['long'] = trim($match[1]); if (($placelist[$j]['long'][0] != 'E') && ($placelist[$j]['long'][0] != 'W')) { if ($placelist[$j]['long'] < 0) { $placelist[$j]['long'][0] = 'W'; } else { $placelist[$j]['long'] = 'E' . $placelist[$j]['long']; } } } else { $placelist[$j]['long'] = null; } $j = $j + 1; } $i = $i + 1; $placerec = Functions::getSubRecord(2, '2 PLAC', $gedrec, $i); } } asort($placelist); $prevPlace = ''; $prevLati = ''; $prevLong = ''; $placelistUniq = array(); $j = 0; foreach ($placelist as $k => $place) { if ($place['place'] != $prevPlace) { $placelistUniq[$j] = array(); $placelistUniq[$j]['place'] = $place['place']; $placelistUniq[$j]['lati'] = $place['lati']; $placelistUniq[$j]['long'] = $place['long']; $j = $j + 1; } elseif (($place['place'] == $prevPlace) && (($place['lati'] != $prevLati) || ($place['long'] != $prevLong))) { if (($placelistUniq[$j - 1]['lati'] == 0) || ($placelistUniq[$j - 1]['long'] == 0)) { $placelistUniq[$j - 1]['lati'] = $place['lati']; $placelistUniq[$j - 1]['long'] = $place['long']; } elseif (($place['lati'] != '0') || ($place['long'] != '0')) { echo 'Difference: previous value = ', $prevPlace, ', ', $prevLati, ', ', $prevLong, ' current = ', $place['place'], ', ', $place['lati'], ', ', $place['long'], '
'; } } $prevPlace = $place['place']; $prevLati = $place['lati']; $prevLong = $place['long']; } $highestIndex = $this->getHighestIndex(); $default_zoom_level = array(4, 7, 10, 12); foreach ($placelistUniq as $k => $place) { $parent = preg_split('/ *, */', $place['place']); $parent = array_reverse($parent); $parent_id = 0; $num_parent = count($parent); for ($i = 0; $i < $num_parent; $i++) { if (!isset($default_zoom_level[$i])) { $default_zoom_level[$i] = $default_zoom_level[$i - 1]; } $escparent = $parent[$i]; if ($escparent == '') { $escparent = 'Unknown'; } $row = Database::prepare("SELECT pl_id, pl_long, pl_lati, pl_zoom FROM `##placelocation` WHERE pl_level=? AND pl_parent_id=? AND pl_place LIKE ?") ->execute(array($i, $parent_id, $escparent)) ->fetchOneRow(); if ($i < $num_parent - 1) { // Create higher-level places, if necessary if (empty($row)) { $highestIndex++; Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_zoom) VALUES (?, ?, ?, ?, ?)") ->execute(array($highestIndex, $parent_id, $i, $escparent, $default_zoom_level[$i])); echo Filter::escapeHtml($escparent), '
'; $parent_id = $highestIndex; } else { $parent_id = $row->pl_id; } } else { // Create lowest-level place, if necessary if (empty($row->pl_id)) { $highestIndex++; Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_long, pl_lati, pl_zoom) VALUES (?, ?, ?, ?, ?, ?, ?)") ->execute(array($highestIndex, $parent_id, $i, $escparent, $place['long'], $place['lati'], $default_zoom_level[$i])); echo Filter::escapeHtml($escparent), '
'; } else { if (empty($row->pl_long) && empty($row->pl_lati) && $place['lati'] != '0' && $place['long'] != '0') { Database::prepare("UPDATE `##placelocation` SET pl_lati=?, pl_long=? WHERE pl_id=?") ->execute(array($place['lati'], $place['long'], $row->pl_id)); echo Filter::escapeHtml($escparent), '
'; } } } } } $parent = 0; } if ($action === 'ImportFile') { echo '

' . I18N::translate('Upload geographic data') . '

'; $placefiles = $this->findFiles(WT_MODULES_DIR . 'googlemap/extra'); sort($placefiles); ?>
$placefile) { unset($placefiles[$p]); $p = Filter::escapeHtml($placefile); if (substr($placefile, 0, 1) == "/") { $placefiles[$p] = substr($placefile, 1); } else { $placefiles[$p] = $placefile; } } echo FunctionsEdit::selectEditControl('localfile', $placefiles, '', '', 'class="form-control"'); ?>
' . I18N::translate('Geographic data') . ''; $country_names = array(); $stats = new Stats($WT_TREE); foreach ($stats->iso3166() as $key => $value) { $country_names[$key] = I18N::translate($key); } if (Filter::postBool('cleardatabase')) { Database::exec("DELETE FROM `##placelocation` WHERE 1=1"); } if (!empty($_FILES['placesfile']['tmp_name'])) { $lines = file($_FILES['placesfile']['tmp_name']); } elseif (!empty($_REQUEST['localfile'])) { $lines = file(WT_MODULES_DIR . 'googlemap/extra' . $_REQUEST['localfile']); } // Strip BYTE-ORDER-MARK, if present if (!empty($lines[0]) && substr($lines[0], 0, 3) === WT_UTF8_BOM) { $lines[0] = substr($lines[0], 3); } asort($lines); $highestIndex = $this->getHighestIndex(); $placelist = array(); $j = 0; $maxLevel = 0; foreach ($lines as $p => $placerec) { $fieldrec = explode(';', $placerec); if ($fieldrec[0] > $maxLevel) { $maxLevel = $fieldrec[0]; } } $fields = count($fieldrec); $set_icon = true; if (!is_dir(WT_MODULES_DIR . 'googlemap/places/flags/')) { $set_icon = false; } foreach ($lines as $p => $placerec) { $fieldrec = explode(';', $placerec); if (is_numeric($fieldrec[0]) && $fieldrec[0] <= $maxLevel) { $placelist[$j] = array(); $placelist[$j]['place'] = ''; for ($ii = $fields - 4; $ii > 1; $ii--) { if ($fieldrec[0] > $ii - 2) { $placelist[$j]['place'] .= $fieldrec[$ii] . ','; } } foreach ($country_names as $countrycode => $countryname) { if ($countrycode == strtoupper($fieldrec[1])) { $fieldrec[1] = $countryname; break; } } $placelist[$j]['place'] .= $fieldrec[1]; $placelist[$j]['long'] = $fieldrec[$fields - 4]; $placelist[$j]['lati'] = $fieldrec[$fields - 3]; $placelist[$j]['zoom'] = $fieldrec[$fields - 2]; if ($set_icon) { $placelist[$j]['icon'] = trim($fieldrec[$fields - 1]); } else { $placelist[$j]['icon'] = ''; } $j = $j + 1; } } $prevPlace = ''; $prevLati = ''; $prevLong = ''; $placelistUniq = array(); $j = 0; foreach ($placelist as $k => $place) { if ($place['place'] != $prevPlace) { $placelistUniq[$j] = array(); $placelistUniq[$j]['place'] = $place['place']; $placelistUniq[$j]['lati'] = $place['lati']; $placelistUniq[$j]['long'] = $place['long']; $placelistUniq[$j]['zoom'] = $place['zoom']; $placelistUniq[$j]['icon'] = $place['icon']; $j = $j + 1; } elseif (($place['place'] == $prevPlace) && (($place['lati'] != $prevLati) || ($place['long'] != $prevLong))) { if (($placelistUniq[$j - 1]['lati'] == 0) || ($placelistUniq[$j - 1]['long'] == 0)) { $placelistUniq[$j - 1]['lati'] = $place['lati']; $placelistUniq[$j - 1]['long'] = $place['long']; $placelistUniq[$j - 1]['zoom'] = $place['zoom']; $placelistUniq[$j - 1]['icon'] = $place['icon']; } elseif (($place['lati'] != '0') || ($place['long'] != '0')) { echo 'Difference: previous value = ', $prevPlace, ', ', $prevLati, ', ', $prevLong, ' current = ', $place['place'], ', ', $place['lati'], ', ', $place['long'], '
'; } } $prevPlace = $place['place']; $prevLati = $place['lati']; $prevLong = $place['long']; } $default_zoom_level = array(); $default_zoom_level[0] = 4; $default_zoom_level[1] = 7; $default_zoom_level[2] = 10; $default_zoom_level[3] = 12; foreach ($placelistUniq as $k => $place) { $parent = explode(',', $place['place']); $parent = array_reverse($parent); $parent_id = 0; $num_parent = count($parent); for ($i = 0; $i < $num_parent; $i++) { $escparent = $parent[$i]; if ($escparent == '') { $escparent = 'Unknown'; } $row = Database::prepare("SELECT pl_id, pl_long, pl_lati, pl_zoom, pl_icon FROM `##placelocation` WHERE pl_level=? AND pl_parent_id=? AND pl_place LIKE ? ORDER BY pl_place") ->execute(array($i, $parent_id, $escparent)) ->fetchOneRow(); if (empty($row)) { // this name does not yet exist: create entry if (!Filter::postBool('updateonly')) { $highestIndex = $highestIndex + 1; if (($i + 1) == $num_parent) { $zoomlevel = $place['zoom']; } elseif (isset($default_zoom_level[$i])) { $zoomlevel = $default_zoom_level[$i]; } else { $zoomlevel = $this->getSetting('GM_MAX_ZOOM'); } if (($place['lati'] == '0') || ($place['long'] == '0') || (($i + 1) < $num_parent)) { Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_zoom, pl_icon) VALUES (?, ?, ?, ?, ?, ?)") ->execute(array($highestIndex, $parent_id, $i, $escparent, $zoomlevel, $place['icon'])); } else { //delete leading zero $pl_lati = str_replace(array('N', 'S', ','), array('', '-', '.'), $place['lati']); $pl_long = str_replace(array('E', 'W', ','), array('', '-', '.'), $place['long']); if ($pl_lati >= 0) { $place['lati'] = 'N' . abs($pl_lati); } elseif ($pl_lati < 0) { $place['lati'] = 'S' . abs($pl_lati); } if ($pl_long >= 0) { $place['long'] = 'E' . abs($pl_long); } elseif ($pl_long < 0) { $place['long'] = 'W' . abs($pl_long); } Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_long, pl_lati, pl_zoom, pl_icon) VALUES (?, ?, ?, ?, ?, ?, ?, ?)") ->execute(array($highestIndex, $parent_id, $i, $escparent, $place['long'], $place['lati'], $zoomlevel, $place['icon'])); } $parent_id = $highestIndex; } } else { $parent_id = $row->pl_id; if (Filter::postBool('overwritedata') && ($i + 1 == count($parent))) { Database::prepare("UPDATE `##placelocation` SET pl_lati = ?, pl_long = ?, pl_zoom = ?, pl_icon = ? WHERE pl_id = ?") ->execute(array($place['lati'], $place['long'], $place['zoom'], $place['icon'], $parent_id)); } else { // Update only if existing data is missing if (!$row->pl_long && !$row->pl_lati) { Database::prepare("UPDATE `##placelocation` SET pl_lati = ?, pl_long = ? WHERE pl_id = ?") ->execute(array($place['lati'], $place['long'], $parent_id)); } if (!$row->pl_icon && $place['icon']) { Database::prepare("UPDATE `##placelocation` SET pl_icon = ? WHERE pl_id = ?") ->execute(array($place['icon'], $parent_id)); } } } } } $parent = 0; } if ($action == 'DeleteRecord') { echo '

' . I18N::translate('Geographic data') . '

'; $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'), '
'; } } ?>

placeIdToHierarchy($parent); foreach (array_reverse($where_am_i, true) as $id => $place) { if ($id == $parent) { if ($place != 'Unknown') { echo Filter::escapeHtml($place); } else { echo I18N::translate('unknown'); } } else { echo ''; if ($place != 'Unknown') { echo Filter::escapeHtml($place), ''; } else { echo I18N::translate('unknown'), ''; } } echo ' - '; } ?>

getPlaceListLocation($parent, $inactive); echo '
'; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; if (count($placelist) == 0) { echo ''; } foreach ($placelist as $place) { echo ''; } else { echo I18N::translate('unknown'), ''; } echo ''; echo ''; echo ''; echo ''; echo ''; $noRows = Database::prepare("SELECT COUNT(pl_id) FROM `##placelocation` WHERE pl_parent_id=?") ->execute(array($place['place_id'])) ->fetchOne(); if ($noRows == 0) { ?>
', GedcomTag::getLabel('PLAC'), '', GedcomTag::getLabel('LATI'), '', GedcomTag::getLabel('LONG'), '', I18N::translate('Zoom level'), '', I18N::translate('Icon'), ''; echo I18N::translate('Edit'), '', I18N::translate('Delete'), '
', I18N::translate('No places found'), '
'; if ($place['place'] != 'Unknown') { echo Filter::escapeHtml($place['place']), '', $place['lati'], '', $place['long'], '', $place['zoom'], ''; if ($place['icon']) { echo ''; } else { if ($place['lati'] || $place['long']) { echo ''; } else { echo ''; } } echo '

getName(), 'class="form-control"') ?>
getTreeId(), 'class="form-control"') ?>
Click and drag the marker.