Completed
Push — charts ( 02447f )
by Greg
12:32
created

GoogleMapsModule::checkWhereAmI()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 11
rs 9.4285
cc 2
eloc 8
nc 2
nop 2
1
<?php
2
/**
3
 * webtrees: online genealogy
4
 * Copyright (C) 2016 webtrees development team
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
 * GNU General Public License for more details.
13
 * You should have received a copy of the GNU General Public License
14
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15
 */
16
namespace Fisharebest\Webtrees\Module;
17
18
use Fisharebest\Webtrees\Auth;
19
use Fisharebest\Webtrees\Controller\ChartController;
20
use Fisharebest\Webtrees\Controller\PageController;
21
use Fisharebest\Webtrees\Controller\SimpleController;
22
use Fisharebest\Webtrees\Database;
23
use Fisharebest\Webtrees\Family;
24
use Fisharebest\Webtrees\Filter;
25
use Fisharebest\Webtrees\FlashMessages;
26
use Fisharebest\Webtrees\Functions\Functions;
27
use Fisharebest\Webtrees\Functions\FunctionsCharts;
28
use Fisharebest\Webtrees\Functions\FunctionsEdit;
29
use Fisharebest\Webtrees\Functions\FunctionsPrint;
30
use Fisharebest\Webtrees\GedcomTag;
31
use Fisharebest\Webtrees\I18N;
32
use Fisharebest\Webtrees\Individual;
33
use Fisharebest\Webtrees\Module;
34
use Fisharebest\Webtrees\Stats;
35
use Fisharebest\Webtrees\Theme;
36
use Fisharebest\Webtrees\Tree;
37
use Fisharebest\Webtrees\Menu;
38
use PDO;
39
40
/**
41
 * Class GoogleMapsModule
42
 *
43
 * @link http://www.google.com/permissions/guidelines.html
44
 *
45
 * "... an unregistered Google Brand Feature should be followed by
46
 * the superscripted letters TM or SM ..."
47
 *
48
 * Hence, use "Google Maps™"
49
 *
50
 * "... Use the trademark only as an adjective"
51
 *
52
 * "... Use a generic term following the trademark, for example:
53
 * GOOGLE search engine, Google search"
54
 *
55
 * Hence, use "Google Maps™ mapping service" where appropriate.
56
 */
57
class GoogleMapsModule extends AbstractModule implements ModuleConfigInterface, ModuleTabInterface, ModuleChartInterface {
58
	// How to update the database schema for this module
59
	const SCHEMA_TARGET_VERSION   = 6;
60
	const SCHEMA_SETTING_NAME     = 'GM_SCHEMA_VERSION';
61
	const SCHEMA_MIGRATION_PREFIX = '\Fisharebest\Webtrees\Module\GoogleMaps\Schema';
62
63
	/** @var Individual[] of ancestors of root person */
64
	private $ancestors = array();
65
66
	/** @var int Number of generation to display */
67
	private $generations;
68
69
	/** @var int Number of nodes in the chart */
70
	private $treesize;
71
72
	/** {@inheritdoc} */
73
	public function getTitle() {
74
		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™');
75
	}
76
77
	/** {@inheritdoc} */
78
	public function getDescription() {
79
		return /* I18N: Description of the “Google Maps™” module */ I18N::translate('Show the location of places and events using the Google Maps™ mapping service.');
80
	}
81
82
	/**
83
	 * This is a general purpose hook, allowing modules to respond to routes
84
	 * of the form module.php?mod=FOO&mod_action=BAR
85
	 *
86
	 * @param string $mod_action
87
	 */
88
	public function modAction($mod_action) {
89
		Database::updateSchema(self::SCHEMA_MIGRATION_PREFIX, self::SCHEMA_SETTING_NAME, self::SCHEMA_TARGET_VERSION);
90
91
		switch ($mod_action) {
92
		case 'admin_config':
93
			$this->config();
94
			break;
95
		case 'flags':
96
			$this->flags();
97
			break;
98
		case 'pedigree_map':
99
			$this->pedigreeMap();
100
			break;
101
		case 'admin_placecheck':
102
			$this->adminPlaceCheck();
103
			break;
104
		case 'admin_places':
105
			$this->adminPlaces();
106
			break;
107
		case 'places_edit':
108
			$this->placesEdit();
109
			break;
110
		case 'wt_street_view':
111
			$this->wtStreetView();
112
			break;
113
		default:
114
			http_response_code(404);
115
			break;
116
		}
117
	}
118
119
	/** {@inheritdoc} */
120
	public function getConfigLink() {
121
		Database::updateSchema(self::SCHEMA_MIGRATION_PREFIX, self::SCHEMA_SETTING_NAME, self::SCHEMA_TARGET_VERSION);
122
123
		return 'module.php?mod=' . $this->getName() . '&amp;mod_action=admin_config';
124
	}
125
126
	/** {@inheritdoc} */
127
	public function defaultTabOrder() {
128
		return 80;
129
	}
130
131
	/** {@inheritdoc} */
132
	public function getPreLoadContent() {
133
		ob_start();
134
		?>
135
		<script src="<?php echo $this->googleMapsScript() ?>"></script>
136
		<script>
137
			var minZoomLevel   = <?php echo $this->getSetting('GM_MIN_ZOOM') ?>;
138
			var maxZoomLevel   = <?php echo $this->getSetting('GM_MAX_ZOOM') ?>;
139
			var startZoomLevel = maxZoomLevel;
140
		</script>
141
		<?php
142
		return ob_get_clean();
143
	}
144
145
	/** {@inheritdoc} */
146
	public function canLoadAjax() {
147
		return true;
148
	}
149
150
	/** {@inheritdoc} */
151
	public function getTabContent() {
152
		global $controller;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
153
154
		Database::updateSchema(self::SCHEMA_MIGRATION_PREFIX, self::SCHEMA_SETTING_NAME, self::SCHEMA_TARGET_VERSION);
155
156
		if ($this->checkMapData($controller->record)) {
157
			ob_start();
158
			echo '<link type="text/css" href ="', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/css/wt_v3_googlemap.css" rel="stylesheet">';
159
			echo '<table border="0" width="100%"><tr><td>';
160
			echo '<table width="100%" border="0" class="facts_table">';
161
			echo '<tr><td>';
162
			echo '<div id="map_pane" style="border: 1px solid gray; color: black; width: 100%; height: ', $this->getSetting('GM_YSIZE'), 'px"></div>';
163
			if (Auth::isAdmin()) {
164
				echo '<table width="100%"><tr>';
165
				echo '<td>';
166
				echo '<a href="module.php?mod=', $this->getName(), '&amp;mod_action=admin_config">', I18N::translate('Google Maps™ preferences'), '</a>';
167
				echo '</td>';
168
				echo '<td "style=text-align:center;">';
169
				echo '<a href="module.php?mod=', $this->getName(), '&amp;mod_action=admin_places">', I18N::translate('Geographic data'), '</a>';
170
				echo '</td>';
171
				echo '<td style="text-align:end;">';
172
				echo '<a href="module.php?mod=', $this->getName(), '&amp;mod_action=admin_placecheck">', I18N::translate('Place check'), '</a>';
173
				echo '</td>';
174
				echo '</tr></table>';
175
			}
176
			echo '</td>';
177
			echo '<td width="30%">';
178
			echo '<div id="map_content">';
179
180
			$this->buildIndividualMap($controller->record);
181
			echo '</div>';
182
			echo '</td>';
183
			echo '</tr></table>';
184
			// start
185
			echo '<img src="', Theme::theme()->parameter('image-spacer'), '" id="marker6" width="1" height="1" alt="">';
186
			// end
187
			echo '</td></tr></table>';
188
			echo '<script>loadMap();</script>';
189
190
			return '<div id="' . $this->getName() . '_content">' . ob_get_clean() . '</div>';
191
		} else {
192
			$html = '<table class="facts_table">';
193
			$html .= '<tr><td colspan="2" class="facts_value">' . I18N::translate('No map data for this individual');
194
			$html .= '</td></tr>';
195
			if (Auth::isAdmin()) {
196
				$html .= '<tr><td class="center" colspan="2">';
197
				$html .= '<a href="module.php?mod=googlemap&amp;mod_action=admin_config">' . I18N::translate('Google Maps™ preferences') . '</a>';
198
				$html .= '</td></tr>';
199
			}
200
201
			return $html;
202
		}
203
	}
204
205
	/** {@inheritdoc} */
206
	public function hasTabContent() {
207
		return Module::getModuleByName('googlemap') || Auth::isAdmin();
208
	}
209
210
	/** {@inheritdoc} */
211
	public function isGrayedOut() {
212
		return false;
213
	}
214
215
	/**
216
	 * Return a menu item for this chart.
217
	 *
218
	 * @return Menu|null
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use Menu.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
219
	 */
220
	public function getChartMenu(Individual $individual) {
221
		return new Menu(
222
			I18N::translate('Pedigree map'), 
223
			'module.php?mod=googlemap&amp;mod_action=pedigree_map&amp;rootid=' . $individual->getXref() . '&amp;ged=' . $individual->getTree()->getNameUrl(), 
224
			'menu-chart-pedigree_map', 
225
			array('rel' => 'nofollow')
226
		);
227
	}
228
		
229
	/**
230
	 * Return a menu item for this chart - for use in individual boxes.
231
	 *
232
	 * @return Menu|null
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use Menu.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
233
	 */
234
	public function getBoxChartMenu(Individual $individual) {
235
		return $this->getChartMenu($individual);
236
	}
237
	
238
	/**
239
	 * A form to edit the module configuration.
240
	 */
241
	private function config() {
242
		$controller = new PageController;
243
		$controller
244
			->restrictAccess(Auth::isAdmin())
245
			->setPageTitle(I18N::translate('Google Maps™'));
246
247
		if (Filter::post('action') === 'update') {
248
			$this->setSetting('GM_MAP_TYPE', Filter::post('GM_MAP_TYPE'));
249
			$this->setSetting('GM_USE_STREETVIEW', Filter::post('GM_USE_STREETVIEW'));
250
			$this->setSetting('GM_MIN_ZOOM', Filter::post('GM_MIN_ZOOM'));
251
			$this->setSetting('GM_MAX_ZOOM', Filter::post('GM_MAX_ZOOM'));
252
			$this->setSetting('GM_XSIZE', Filter::post('GM_XSIZE'));
253
			$this->setSetting('GM_YSIZE', Filter::post('GM_YSIZE'));
254
			$this->setSetting('GM_PRECISION_0', Filter::post('GM_PRECISION_0'));
255
			$this->setSetting('GM_PRECISION_1', Filter::post('GM_PRECISION_1'));
256
			$this->setSetting('GM_PRECISION_2', Filter::post('GM_PRECISION_2'));
257
			$this->setSetting('GM_PRECISION_3', Filter::post('GM_PRECISION_3'));
258
			$this->setSetting('GM_PRECISION_4', Filter::post('GM_PRECISION_4'));
259
			$this->setSetting('GM_PRECISION_5', Filter::post('GM_PRECISION_5'));
260
			$this->setSetting('GM_COORD', Filter::post('GM_COORD'));
261
			$this->setSetting('GM_PLACE_HIERARCHY', Filter::post('GM_PLACE_HIERARCHY'));
262
			$this->setSetting('GM_PH_XSIZE', Filter::post('GM_PH_XSIZE'));
263
			$this->setSetting('GM_PH_YSIZE', Filter::post('GM_PH_YSIZE'));
264
			$this->setSetting('GM_PH_MARKER', Filter::post('GM_PH_MARKER'));
265
			$this->setSetting('GM_PREFIX_1', Filter::post('GM_PREFIX_1'));
266
			$this->setSetting('GM_PREFIX_2', Filter::post('GM_PREFIX_2'));
267
			$this->setSetting('GM_PREFIX_3', Filter::post('GM_PREFIX_3'));
268
			$this->setSetting('GM_PREFIX_4', Filter::post('GM_PREFIX_4'));
269
			$this->setSetting('GM_PREFIX_5', Filter::post('GM_PREFIX_5'));
270
			$this->setSetting('GM_PREFIX_6', Filter::post('GM_PREFIX_6'));
271
			$this->setSetting('GM_PREFIX_7', Filter::post('GM_PREFIX_7'));
272
			$this->setSetting('GM_PREFIX_8', Filter::post('GM_PREFIX_8'));
273
			$this->setSetting('GM_PREFIX_9', Filter::post('GM_PREFIX_9'));
274
			$this->setSetting('GM_POSTFIX_1', Filter::post('GM_POSTFIX_1'));
275
			$this->setSetting('GM_POSTFIX_2', Filter::post('GM_POSTFIX_2'));
276
			$this->setSetting('GM_POSTFIX_3', Filter::post('GM_POSTFIX_3'));
277
			$this->setSetting('GM_POSTFIX_4', Filter::post('GM_POSTFIX_4'));
278
			$this->setSetting('GM_POSTFIX_5', Filter::post('GM_POSTFIX_5'));
279
			$this->setSetting('GM_POSTFIX_6', Filter::post('GM_POSTFIX_6'));
280
			$this->setSetting('GM_POSTFIX_7', Filter::post('GM_POSTFIX_7'));
281
			$this->setSetting('GM_POSTFIX_8', Filter::post('GM_POSTFIX_8'));
282
			$this->setSetting('GM_POSTFIX_9', Filter::post('GM_POSTFIX_9'));
283
284
			FlashMessages::addMessage(I18N::translate('The preferences for the module “%s” have been updated.', $this->getName()), 'success');
285
			header('Location: ' . WT_BASE_URL . 'module.php?mod=googlemap&mod_action=admin_config');
286
287
			return;
288
		}
289
290
		$controller->pageHeader();
291
292
		?>
293
		<ol class="breadcrumb small">
294
			<li><a href="admin.php"><?php echo I18N::translate('Control panel') ?></a></li>
295
			<li><a href="admin_modules.php"><?php echo I18N::translate('Module administration') ?></a></li>
296
			<li class="active"><?php echo $controller->getPageTitle() ?></li>
297
		</ol>
298
299
		<ul class="nav nav-tabs nav-justified" role="tablist">
300
			<li role="presentation" class="active">
301
				<a href="#" role="tab">
302
					<?php echo I18N::translate('Google Maps™ preferences') ?>
303
				</a>
304
			</li>
305
			<li role="presentation">
306
				<a href="?mod=googlemap&amp;mod_action=admin_places">
307
					<?php echo I18N::translate('Geographic data') ?>
308
				</a>
309
			</li>
310
			<li role="presentation">
311
				<a href="?mod=googlemap&amp;mod_action=admin_placecheck">
312
					<?php echo I18N::translate('Place check') ?>
313
				</a>
314
			</li>
315
		</ul>
316
317
		<h2><?php echo I18N::translate('Google Maps™ preferences') ?></h2>
318
319
		<form class="form-horizontal" method="post" name="configform" action="module.php?mod=googlemap&mod_action=admin_config">
320
			<input type="hidden" name="action" value="update">
321
			<h3><?php echo I18N::translate('Basic') ?></h3>
322
323
			<!-- GM_MAP_TYPE -->
324
			<div class="form-group">
325
				<label class="control-label col-sm-3" for="GM_MAP_TYPE">
326
					<?php echo I18N::translate('Default map type') ?>
327
				</label>
328
				<div class="col-sm-9">
329
					<?php
330
					$options = array(
331
						'ROADMAP'   => I18N::translate('Map'),
332
						'SATELLITE' => I18N::translate('Satellite'),
333
						'HYBRID'    => I18N::translate('Hybrid'),
334
						'TERRAIN'   => I18N::translate('Terrain'),
335
					);
336
					echo FunctionsEdit::selectEditControl('GM_MAP_TYPE', $options, null, $this->getSetting('GM_MAP_TYPE'), 'class="form-control"');
337
					?>
338
				</div>
339
			</div>
340
341
			<!-- GM_USE_STREETVIEW -->
342
			<fieldset class="form-group">
343
				<legend class="control-label col-sm-3">
344
					<?php echo /* I18N: http://en.wikipedia.org/wiki/Google_street_view */ I18N::translate('Google Street View™') ?>
345
				</legend>
346
				<div class="col-sm-9">
347
					<?php echo FunctionsEdit::radioButtons('GM_USE_STREETVIEW', array(false => I18N::translate('hide'), true => I18N::translate('show')), $this->getSetting('GM_USE_STREETVIEW'), 'class="radio-inline"') ?>
348
				</div>
349
			</fieldset>
350
351
			<!-- GM_XSIZE / GM_YSIZE -->
352
			<fieldset class="form-group">
353
				<legend class="control-label col-sm-3">
354
					<?php echo I18N::translate('Size of map (in pixels)') ?>
355
				</legend>
356
				<div class="col-sm-9">
357
					<div class="row">
358
						<div class="col-sm-6">
359
							<div class="input-group">
360
								<label class="input-group-addon" for="GM_XSIZE"><?php echo I18N::translate('Width') ?></label>
361
								<input id="GM_XSIZE" class="form-control" type="text" name="GM_XSIZE" value="<?php echo $this->getSetting('GM_XSIZE') ?>">
362
							</div>
363
						</div>
364
						<div class="col-sm-6">
365
							<div class="input-group">
366
								<label class="input-group-addon" for="GM_YSIZE"><?php echo I18N::translate('Height') ?></label>
367
								<input id="GM_YSIZE" class="form-control" type="text" name="GM_YSIZE" value="<?php echo $this->getSetting('GM_YSIZE') ?>">
368
							</div>
369
						</div>
370
					</div>
371
				</div>
372
			</fieldset>
373
374
			<!-- GM_MIN_ZOOM / GM_MAX_ZOOM -->
375
			<fieldset class="form-group">
376
				<legend class="control-label col-sm-3">
377
					<?php echo I18N::translate('Zoom level of map') ?>
378
				</legend>
379
				<div class="col-sm-9">
380
					<div class="row">
381
						<div class="col-sm-6">
382
							<div class="input-group">
383
								<label class="input-group-addon" for="GM_MIN_ZOOM"><?php echo I18N::translate('minimum') ?></label>
384
								<?php echo FunctionsEdit::selectEditControl('GM_MIN_ZOOM', array_combine(range(1, 14), range(1, 14)), null, $this->getSetting('GM_MIN_ZOOM'), 'class="form-control"') ?>
385
							</div>
386
						</div>
387
						<div class="col-sm-6">
388
							<div class="input-group">
389
								<label class="input-group-addon" for="GM_MAX_ZOOM"><?php echo I18N::translate('maximum') ?></label>
390
								<?php echo FunctionsEdit::selectEditControl('GM_MAX_ZOOM', array_combine(range(1, 20), range(1, 20)), null, $this->getSetting('GM_MAX_ZOOM'), 'class="form-control"') ?>
391
							</div>
392
						</div>
393
					</div>
394
					<p class="small text-muted"><?php echo I18N::translate('Minimum and maximum zoom level for the Google map. 1 is the full map, 15 is single house. Note that 15 is only available in certain areas.') ?></p>
395
				</div>
396
			</fieldset>
397
398
			<h3><?php echo I18N::translate('Advanced') ?></h3>
399
400
			<!-- GM_PRECISION -->
401
			<fieldset class="form-group">
402
				<legend class="control-label col-sm-3">
403
					<?php echo I18N::translate('Precision of the latitude and longitude') ?>
404
				</legend>
405
				<div class="col-sm-9">
406
					<div class="row">
407
						<?php foreach (array(I18N::translate('Country'), I18N::translate('State'), I18N::translate('City'), I18N::translate('Neighborhood'), I18N::translate('House'), I18N::translate('Max')) as $level => $label): ?>
408
						<div class="col-sm-4">
409
							<div class="input-group">
410
								<label class="input-group-addon" for="GM_PRECISION_<?php echo $level ?>"><?php echo $label ?></label>
411
								<?php echo FunctionsEdit::selectEditControl('GM_PRECISION_' . $level, range(0, 9), null, $this->getSetting('GM_PRECISION_' . $level), 'class="form-control"') ?>
412
							</div>
413
						</div>
414
						<?php endforeach ?>
415
					</div>
416
					<p class="small text-muted"><?php echo I18N::translate('This specifies the precision of the different levels when entering new geographic locations. For example a country will be specified with precision 0 (=0 digits after the decimal point), while a town needs 3 or 4 digits.') ?></p>
417
				</div>
418
			</fieldset>
419
420
			<!-- GM_PREFIX / GM_POSTFIX -->
421
			<fieldset class="form-group">
422
				<legend class="control-label col-sm-3">
423
					<?php echo I18N::translate('Optional prefixes and suffixes') ?>
424
				</legend>
425
				<div class="col-sm-9">
426
					<div class="row">
427
						<div class ="col-sm-6">
428
							<p class="form-control-static"><strong><?php echo I18N::translate('Prefixes') ?></strong></p>
429 View Code Duplication
							<?php for ($level = 1; $level < 10; $level++): ?>
430
							<?php
431
							if ($level == 1) {
432
								$label = I18N::translate('Country');
433
							} else {
434
								$label = I18N::translate('Level') .  ' ' . $level;
435
							}
436
							?>
437
							<div class="input-group">
438
								<label class="input-group-addon" for="GM_PREFIX_<?php echo $level ?>"><?php echo $label ?></label>
439
								<input class="form-control" type="text" name="GM_PREFIX_<?php echo $level ?>" value="<?php echo $this->getSetting('GM_PREFIX_' . $level) ?>">
440
							</div>
441
							<?php endfor ?>
442
						</div>
443
						<div class="col-sm-6">
444
							<p class="form-control-static"><strong><?php echo I18N::translate('Suffixes') ?></strong></p>
445 View Code Duplication
							<?php for ($level = 1; $level < 10; $level++): ?>
446
							<?php
447
							if ($level == 1) {
448
								$label = I18N::translate('Country');
449
							} else {
450
								$label = I18N::translate('Level') .  ' ' . $level;
451
							}
452
							?>
453
							<div class="input-group">
454
								<label class="input-group-addon" for="GM_POSTFIX_<?php echo $level ?>"><?php echo $label ?></label>
455
								<input class="form-control" type="text" name="GM_POSTFIX_<?php echo $level ?>" value="<?php echo $this->getSetting('GM_POSTFIX_' . $level) ?>">
456
							</div>
457
							<?php endfor ?>
458
						</div>
459
					</div>
460
					<p class="small text-muted"><?php echo I18N::translate('Some place names may be written with optional prefixes and suffixes. For example “Orange” versus “Orange County”. If the family tree contains the full place names, but the geographic database contains the short place names, then you should specify a list of the prefixes and suffixes to be disregarded. Multiple options should be separated with semicolons. For example “County;County of” or “Township;Twp;Twp.”.') ?></p>
461
				</div>
462
			</fieldset>
463
464
			<h3><?php echo I18N::translate('Place hierarchy') ?></h3>
465
466
			<!-- GM_PLACE_HIERARCHY -->
467
			<fieldset class="form-group">
468
				<legend class="control-label col-sm-3">
469
					<?php echo I18N::translate('Use Google Maps™ for the place hierarchy') ?>
470
				</legend>
471
				<div class="col-sm-9">
472
					<?php echo FunctionsEdit::editFieldYesNo('GM_PLACE_HIERARCHY', $this->getSetting('GM_PLACE_HIERARCHY'), 'class="radio-inline"') ?>
0 ignored issues
show
Documentation introduced by
$this->getSetting('GM_PLACE_HIERARCHY') is of type string|null, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
473
				</div>
474
			</fieldset>
475
476
			<!-- GM_PH_XSIZE / GM_PH_YSIZE -->
477
			<fieldset class="form-group">
478
				<legend class="control-label col-sm-3">
479
					<?php echo I18N::translate('Size of map (in pixels)') ?>
480
				</legend>
481
				<div class="col-sm-9">
482
					<div class="row">
483
						<div class="col-sm-6">
484
							<div class="input-group">
485
								<label class="input-group-addon" for="GM_PH_XSIZE"><?php echo I18N::translate('Width') ?></label>
486
								<input id="GM_XSIZE" class="form-control" type="text" name="GM_PH_XSIZE" value="<?php echo $this->getSetting('GM_PH_XSIZE') ?>">
487
							</div>
488
						</div>
489
						<div class="col-sm-6">
490
							<div class="input-group">
491
								<label class="input-group-addon" for="GM_PH_YSIZE"><?php echo I18N::translate('Height') ?></label>
492
								<input id="GM_YSIZE" class="form-control" type="text" name="GM_PH_YSIZE" value="<?php echo $this->getSetting('GM_PH_YSIZE') ?>">
493
							</div>
494
						</div>
495
					</div>
496
				</div>
497
			</fieldset>
498
499
			<!-- GM_PH_MARKER -->
500
			<div class="form-group">
501
				<label class="control-label col-sm-3" for="GM_PH_MARKER">
502
					<?php echo I18N::translate('Type of place markers in Place Hierarchy') ?>
503
				</label>
504
				<div class="col-sm-9">
505
					<?php
506
					$ph_options = array(
507
						'G_DEFAULT_ICON' => I18N::translate('Standard'),
508
						'G_FLAG'         => I18N::translate('Flag'),
509
					);
510
					echo FunctionsEdit::selectEditControl('GM_PH_MARKER', $ph_options, null, $this->getSetting('GM_PH_MARKER'), 'class="form-control"');
511
					?>
512
				</div>
513
			</div>
514
515
			<!-- GM_COORD -->
516
			<fieldset class="form-group">
517
				<legend class="control-label col-sm-3">
518
					<?php echo I18N::translate('Display map coordinates') ?>
519
				</legend>
520
				<div class="col-sm-9">
521
					<?php echo FunctionsEdit::editFieldYesNo('GM_COORD', $this->getSetting('GM_COORD'), 'class="radio-inline"') ?>
0 ignored issues
show
Documentation introduced by
$this->getSetting('GM_COORD') is of type string|null, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
522
					<p class="small text-muted">
523
						<?php echo I18N::translate('This options sets whether latitude and longitude are displayed on the pop-up window attached to map markers.') ?>
524
					</p>
525
				</div>
526
			</fieldset>
527
528
			<!-- SAVE BUTTON -->
529
			<div class="form-group">
530
				<div class="col-sm-offset-3 col-sm-9">
531
					<button type="submit" class="btn btn-primary">
532
						<i class="fa fa-check"></i>
533
						<?php echo I18N::translate('save') ?>
534
					</button>
535
				</div>
536
			</div>
537
		</form>
538
		<?php
539
	}
540
541
	/**
542
	 * Google Maps API script
543
	 *
544
	 * @return string
545
	 */
546
	private function googleMapsScript() {
547
		return 'https://maps.google.com/maps/api/js?v=3.2&amp;sensor=false&amp;language=' . WT_LOCALE;
548
	}
549
550
	/**
551
	 * Select a flag.
552
	 */
553
	private function flags() {
0 ignored issues
show
Coding Style introduced by
flags uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
554
		global $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
555
556
		$controller = new SimpleController;
557
		$controller
558
			->setPageTitle(I18N::translate('Select flag'))
559
			->pageHeader();
560
561
		$stats           = new Stats($WT_TREE);
562
		$countries       = $stats->getAllCountries();
563
		$action          = Filter::post('action');
564
		$countrySelected = Filter::get('countrySelected', null, 'Countries');
565
		$stateSelected   = Filter::get('stateSelected', null, 'States');
566
567
		$country = array();
568
		if (is_dir(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/flags')) {
569
			$rep = opendir(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/flags');
570 View Code Duplication
			while ($file = readdir($rep)) {
571
				if (stristr($file, '.png')) {
572
					$country[] = substr($file, 0, strlen($file) - 4);
573
				}
574
			}
575
			closedir($rep);
576
			sort($country);
577
		}
578
579
		if ($countrySelected == 'Countries') {
580
			$flags = $country;
581
		} else {
582
			$flags = array();
583
			if (is_dir(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/' . $countrySelected . '/flags')) {
584
				$rep = opendir(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/' . $countrySelected . '/flags');
585 View Code Duplication
				while ($file = readdir($rep)) {
586
					if (stristr($file, '.png')) {
587
						$flags[] = substr($file, 0, strlen($file) - 4);
588
					}
589
				}
590
				closedir($rep);
591
				sort($flags);
592
			}
593
		}
594
		$flags_s = array();
595
		if ($stateSelected != 'States' && is_dir(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/' . $countrySelected . '/flags/' . $stateSelected)) {
596
			$rep = opendir(WT_ROOT . WT_MODULES_DIR . 'googlemap/places/' . $countrySelected . '/flags/' . $stateSelected);
597 View Code Duplication
			while ($file = readdir($rep)) {
598
				if (stristr($file, '.png')) {
599
					$flags_s[] = substr($file, 0, strlen($file) - 4);
600
				}
601
			}
602
			closedir($rep);
603
			sort($flags_s);
604
		}
605
606
		if ($action == 'ChangeFlag' && isset($_POST['FLAGS'])) {
607
		?>
608
			<script>
609
		<?php if ($_POST['selcountry'] == 'Countries') { ?>
610
					window.opener.document.editplaces.icon.value = 'places/flags/<?php echo $flags[$_POST['FLAGS']] ?>.png';
611
					window.opener.document.getElementById('flagsDiv').innerHTML = "<img src=\"<?php echo WT_STATIC_URL . WT_MODULES_DIR ?>googlemap/places/flags/<?php echo $country[$_POST['FLAGS']] ?>.png\">&nbsp;&nbsp;<a href=\"#\" onclick=\"change_icon();return false;\"><?php echo I18N::translate('Change flag') ?></a>&nbsp;&nbsp;<a href=\"#\" onclick=\"remove_icon();return false;\"><?php echo I18N::translate('Remove flag') ?></a>";
612
		<?php } elseif ($_POST['selstate'] != "States") { ?>
613
					window.opener.document.editplaces.icon.value = 'places/<?php echo $countrySelected . '/flags/' . $_POST['selstate'] . '/' . $flags_s[$_POST['FLAGS']] ?>.png';
0 ignored issues
show
Security Cross-Site Scripting introduced by
$countrySelected . '/fla...lags_s[$_POST['FLAGS']] can contain request data and is used in html attribute with single-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
614
					window.opener.document.getElementById('flagsDiv').innerHTML = "<img src=\"<?php echo WT_STATIC_URL . WT_MODULES_DIR ?>googlemap/places/<?php echo $countrySelected . "/flags/" . $_POST['selstate'] . '/' . $flags_s[$_POST['FLAGS']] ?>.png\">&nbsp;&nbsp;<a href=\"#\" onclick=\"change_icon();return false;\"><?php echo I18N::translate('Change flag') ?></a>&nbsp;&nbsp;<a href=\"#\" onclick=\"remove_icon();return false;\"><?php echo I18N::translate('Remove flag') ?></a>";
0 ignored issues
show
Security Cross-Site Scripting introduced by
$countrySelected . '/fla...lags_s[$_POST['FLAGS']] can contain request data and is used in html attribute with double-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
615
		<?php } else { ?>
616
					window.opener.document.editplaces.icon.value = "places/<?php echo $countrySelected . '/flags/' . $flags[$_POST['FLAGS']] ?>.png";
617
					window.opener.document.getElementById('flagsDiv').innerHTML = "<img src=\"<?php echo WT_STATIC_URL . WT_MODULES_DIR ?>googlemap/places/<?php echo $countrySelected . '/flags/' . $flags[$_POST['FLAGS']] ?>.png\">&nbsp;&nbsp;<a href=\"#\" onclick=\"change_icon();return false;\"><?php echo I18N::translate('Change flag') ?></a>&nbsp;&nbsp;<a href=\"#\" onclick=\"remove_icon();return false;\"><?php echo I18N::translate('Remove flag') ?></a>";
618
		<?php } ?>
619
					window.opener.updateMap();
620
					window.close();
621
			</script>
622
		<?php
623
			return;
624
		} else {
625
		?>
626
		<script>
627
			function selectCountry() {
628
				if (document.flags.COUNTRYSELECT.value == 'Countries') {
629
					window.location="module.php?mod=googlemap&mod_action=flags&countrySelected=Countries";
630
				} else if (document.flags.STATESELECT.value != 'States') {
631
					window.location="module.php?mod=googlemap&mod_action=flags&countrySelected=" + document.flags.COUNTRYSELECT.value + "&stateSelected=" + document.flags.STATESELECT.value;
632
				} else {
633
					window.location="module.php?mod=googlemap&mod_action=flags&countrySelected=" + document.flags.COUNTRYSELECT.value;
634
				}
635
			}
636
		</script>
637
		<?php
638
		}
639
		$countryList = array();
640
		$placesDir   = scandir(WT_MODULES_DIR . 'googlemap/places/');
641
		for ($i = 0; $i < count($country); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
642
			if (count(preg_grep('/' . $country[$i] . '/', $placesDir)) != 0) {
643
				$rep = opendir(WT_MODULES_DIR . 'googlemap/places/' . $country[$i] . '/');
644
				while ($file = readdir($rep)) {
645
					if (stristr($file, 'flags')) {
646
						if (isset($countries[$country[$i]])) {
647
							$countryList[$country[$i]] = $countries[$country[$i]];
648
						} else {
649
							$countryList[$country[$i]] = $country[$i];
650
						}
651
					}
652
				}
653
				closedir($rep);
654
			}
655
		}
656
		asort($countryList);
657
		$stateList = array();
658
		if ($countrySelected != 'Countries') {
659
			$placesDir = scandir(WT_MODULES_DIR . 'googlemap/places/' . $countrySelected . '/flags/');
660
			for ($i = 0; $i < count($flags); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
661
				if (in_array($flags[$i], $placesDir)) {
662
					$rep = opendir(WT_MODULES_DIR . 'googlemap/places/' . $countrySelected . '/flags/' . $flags[$i] . '/');
663
					while ($file = readdir($rep)) {
0 ignored issues
show
Unused Code introduced by
$file is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
664
						$stateList[$flags[$i]] = $flags[$i];
665
					}
666
					closedir($rep);
667
				}
668
			}
669
			asort($stateList);
670
		}
671
		?>
672
		<h4><?php echo I18N::translate('Change flag') ?></h4>
673
674
		<p class="small text-muted">
675
			<?php echo I18N::translate('Using the pull down menu it is possible to select a country, of which a flag can be selected. If no flags are shown, then there are no flags defined for this country.') ?>
676
		</p>
677
678
		<form method="post" id="flags" name="flags" action="module.php?mod=googlemap&amp;mod_action=flags&amp;countrySelected=<?php echo $countrySelected ?>&amp;stateSelected=<?php echo $stateSelected ?>">
679
			<input type="hidden" name="action" value="ChangeFlag">
680
			<input type="hidden" name="selcountry" value="<?php echo $countrySelected ?>">
681
			<input type="hidden" name="selstate" value="<?php echo $stateSelected ?>">
682
			<table class="facts_table">
683
				<tr>
684
					<td class="optionbox" colspan="4">
685
						<select name="COUNTRYSELECT" dir="ltr" onchange="selectCountry()">
686
							<option value="Countries"><?php echo I18N::translate('Countries') ?></option>
687
							<?php foreach ($countryList as $country_key => $country_name) {
688
								echo '<option value="', $country_key, '" ';
689
								if ($countrySelected == $country_key) {
690
									echo 'selected';
691
								}
692
								echo '>', $country_name, '</option>';
693
							} ?>
694
						</select>
695
					</td>
696
				</tr>
697
				<tr>
698
		<?php
699
				$j = 1;
700
				for ($i = 0; $i < count($flags); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
701
					if ($countrySelected == 'Countries') {
702
						$tempstr = '<td><input type="radio" dir="ltr" name="FLAGS" value="' . $i . '"><img src="' . WT_STATIC_URL . WT_MODULES_DIR . 'googlemap/places/flags/' . $flags[$i] . '.png" alt="' . $flags[$i] . '"  title="';
703
						if ($flags[$i] != 'blank') {
704
							if (isset($countries[$flags[$i]])) {
705
								$tempstr .= $countries[$flags[$i]];
706
							} else {
707
								$tempstr .= $flags[$i];
708
							}
709
						} else {
710
							$tempstr .= $countries['???'];
711
						}
712
						echo $tempstr, '">&nbsp;&nbsp;', $flags[$i], '</input></td>';
713
					} else {
714
						echo '<td><input type="radio" dir="ltr" name="FLAGS" value="', $i, '"><img src="', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/places/', $countrySelected, '/flags/', $flags[$i], '.png">&nbsp;&nbsp;', $flags[$i], '</input></td>';
715
					}
716
					if ($j == 4) {
717
						echo '</tr><tr>';
718
						$j = 0;
719
					}
720
					$j++;
721
				}
722
				echo '</tr><tr';
723
				if ($countrySelected == 'Countries' || count($stateList) == 0) {
724
					echo ' style=" visibility: hidden"';
725
				}
726
				echo '>';
727
		?>
728
					<td class="optionbox" colspan="4">
729
						<select name="STATESELECT" dir="ltr" onchange="selectCountry()">
730
							<option value="States"><?php echo /* I18N: Part of a country, state/region/county */ I18N::translate('Subdivision') ?></option>
731
							<?php foreach ($stateList as $state_key => $state_name) {
732
								echo '<option value="', $state_key, '" ';
733
								if ($stateSelected == $state_key) {
734
									echo 'selected';
735
								}
736
								echo '>', $state_name, '</option>';
737
							} ?>
738
						</select>
739
					</td>
740
				</tr>
741
				<tr>
742
		<?php
743
				$j = 1;
744
				for ($i = 0; $i < count($flags_s); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
745
					if ($stateSelected != 'States') {
746
						echo '<td><input type="radio" dir="ltr" name="FLAGS" value="', $i, '"><img src="', WT_STATIC_URL . WT_MODULES_DIR, 'googlemap/places/', $countrySelected, '/flags/', $stateSelected, '/', $flags_s[$i], '.png">&nbsp;&nbsp;', $flags_s[$i], '</input></td>';
747
					}
748
					if ($j == 4) {
749
						echo '</tr><tr>';
750
						$j = 0;
751
					}
752
					$j++;
753
				}
754
		?>
755
				</tr>
756
			</table>
757
			<p id="save-cancel">
758
				<input type="submit" class="save" value="<?php echo I18N::translate('save') ?>">
759
				<input type="button" class="cancel" value="<?php echo I18N::translate('close') ?>" onclick="window.close();">
760
			</p>
761
		</form>
762
		<?php
763
	}
764
765
	/**
766
	 * Display a map showing the originas of ones ancestors.
767
	 */
768
	private function pedigreeMap() {
769
		global $controller, $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
770
771
		$MAX_PEDIGREE_GENERATIONS = $WT_TREE->getPreference('MAX_PEDIGREE_GENERATIONS');
772
		// Limit this to match available number of icons.
773
		// 8 generations equals 255 individuals
774
		$MAX_PEDIGREE_GENERATIONS = min($MAX_PEDIGREE_GENERATIONS, 8);
775
776
		$controller        = new ChartController();
777
		$this->generations = Filter::getInteger('PEDIGREE_GENERATIONS', 2, $MAX_PEDIGREE_GENERATIONS, $WT_TREE->getPreference('DEFAULT_PEDIGREE_GENERATIONS'));
778
		$this->treesize    = pow(2, $this->generations) - 1;
0 ignored issues
show
Documentation Bug introduced by
It seems like pow(2, $this->generations) - 1 can also be of type double. However, the property $treesize is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
779
		$this->ancestors   = array_values($controller->sosaAncestors($this->generations));
780
781
		$controller
782
			->setPageTitle(/* I18N: %s is an individual’s name */ I18N::translate('Pedigree map of %s', $controller->root->getFullName()))
783
			->pageHeader()
784
			->addExternalJavascript(WT_AUTOCOMPLETE_JS_URL)
785
			->addInlineJavascript('autocomplete();');
786
787
		echo '<link type="text/css" href="', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/css/wt_v3_googlemap.css" rel="stylesheet">';
788
		echo '<div id="pedigreemap-page">
789
				<h2>', $controller->getPageTitle(), '</h2>';
790
791
		// -- print the form to change the number of displayed generations
792
		?>
793
		<form name="people" method="get" action="?">
794
			<input type="hidden" name="ged" value="<?php echo $WT_TREE->getNameHtml() ?>">
795
			<input type="hidden" name="mod" value="googlemap">
796
			<input type="hidden" name="mod_action" value="pedigree_map">
797
			<table class="list_table" width="555">
798
				<tr>
799
					<td class="descriptionbox wrap">
800
						<?php echo I18N::translate('Individual') ?>
801
					</td>
802
					<td class="optionbox">
803
						<input class="pedigree_form" data-autocomplete-type="INDI" type="text" id="rootid" name="rootid" size="3" value="<?php echo $controller->root->getXref() ?>">
804
						<?php echo FunctionsPrint::printFindIndividualLink('rootid') ?>
805
					</td>
806
					<td class="topbottombar" rowspan="2">
807
						<input type="submit" value="<?php echo I18N::translate('View') ?>">
808
					</td>
809
				</tr>
810
				<tr>
811
					<td class="descriptionbox wrap">
812
						<?php echo I18N::translate('Generations') ?>
813
					</td>
814
					<td class="optionbox">
815
						<select name="PEDIGREE_GENERATIONS">
816
						<?php
817
							for ($p = 3; $p <= $MAX_PEDIGREE_GENERATIONS; $p++) {
818
								echo '<option value="', $p, '" ';
819
								if ($p == $this->generations) {
820
									echo 'selected';
821
								}
822
								echo '>', $p, '</option>';
823
							}
824
						?>
825
						</select>
826
					</td>
827
				</tr>
828
			</table>
829
		</form>
830
		<!-- end of form -->
831
832
		<!-- count records by type -->
833
		<?php
834
		$curgen   = 1;
835
		$priv     = 0;
836
		$count    = 0;
837
		$miscount = 0;
838
		$missing  = '';
839
840
		$latlongval = array();
841
		$lat        = array();
842
		$lon        = array();
843
		for ($i = 0; $i < ($this->treesize); $i++) {
844
			// -- check to see if we have moved to the next generation
845
			if ($i + 1 >= pow(2, $curgen)) {$curgen++; }
846
			$person = $this->ancestors[$i];
847
			if (!empty($person)) {
848
				$name = $person->getFullName();
849
				if ($name == I18N::translate('Private')) $priv++;
850
				$place = $person->getBirthPlace();
851
				if (empty($place)) {
852
					$latlongval[$i] = null;
853
				} else {
854
					$latlongval[$i] = $this->getLatitudeAndLongitudeFromPlaceLocation($person->getBirthPlace());
855
				}
856
				if ($latlongval[$i]) {
857
					$lat[$i] = str_replace(array('N', 'S', ','), array('', '-', '.'), $latlongval[$i]->pl_lati);
858
					$lon[$i] = str_replace(array('E', 'W', ','), array('', '-', '.'), $latlongval[$i]->pl_long);
859
					if ($lat[$i] && $lon[$i]) {
860
						$count++;
861 View Code Duplication
					} else {
862
						// The place is in the table but has empty values
863
						if ($name) {
864
							if ($missing) {
865
								$missing .= ', ';
866
							}
867
							$missing .= '<a href="' . $person->getHtmlUrl() . '">' . $name . '</a>';
868
							$miscount++;
869
						}
870
					}
871 View Code Duplication
				} else {
872
					// There was no place, or not listed in the map table
873
					if ($name) {
874
						if ($missing) {
875
							$missing .= ', ';
876
						}
877
						$missing .= '<a href="' . $person->getHtmlUrl() . '">' . $name . '</a>';
878
						$miscount++;
879
					}
880
				}
881
			}
882
		}
883
		//<!-- end of count records by type -->
884
		//<!-- start of map display -->
885
		echo '<div id="pedigreemap_chart">';
886
		echo '<table class="tabs_table" cellspacing="0" cellpadding="0" border="0" width="100%">';
887
		echo '<tr>';
888
		echo '<td>';
889
		echo '<div id="pm_map" style="border: 1px solid gray; height: ', $this->getSetting('GM_YSIZE'), 'px; font-size: 0.9em;';
890
		echo '"><i class="icon-loading-large"></i></div>';
891
		if (Auth::isAdmin()) {
892
			echo '<table width="100%">';
893
			echo '<tr><td>';
894
			echo '<a href="module.php?mod=googlemap&amp;mod_action=admin_config">', I18N::translate('Google Maps™ preferences'), '</a>';
895
			echo '</td>';
896
			echo '<td style="text-align:center;">';
897
			echo '<a href="module.php?mod=googlemap&amp;mod_action=admin_places">', I18N::translate('Geographic data'), '</a>';
898
			echo '</td>';
899
			echo '<td style="text-align:end;">';
900
			echo '<a href="module.php?mod=googlemap&amp;mod_action=admin_placecheck">', I18N::translate('Place check'), '</a>';
901
			echo '</td></tr>';
902
			echo '</table>';
903
		}
904
		echo '</td><td width="15px"></td>';
905
		echo '<td width="310px">';
906
		echo '<div id="side_bar" style="width:300px; font-size:0.9em; overflow:auto; overflow-x:hidden; overflow-y:auto; height:', $this->getSetting('GM_YSIZE'), 'px;"></div></td>';
907
		echo '</tr>';
908
		echo '</table>';
909
		// display info under map
910
		echo '<hr>';
911
		echo '<table cellspacing="0" cellpadding="0" border="0" width="100%">';
912
		echo '<tr>';
913
		echo '<td>';
914
		// print summary statistics
915
		if (isset($curgen)) {
916
			$total = pow(2, $curgen) - 1;
917
			echo I18N::plural(
918
				'%1$s individual displayed, out of the normal total of %2$s, from %3$s generations.',
919
				'%1$s individuals displayed, out of the normal total of %2$s, from %3$s generations.',
920
				$count,
921
				I18N::number($count), I18N::number($total), I18N::number($curgen)
922
			), '<br>';
923
			echo '</td>';
924
			echo '</tr>';
925
			echo '<tr>';
926
			echo '<td>';
927
			if ($priv) {
928
				echo I18N::plural('%s individual is private.', '%s individuals are private.', $priv, $priv), '<br>';
929
			}
930
			if ($count + $priv != $total) {
931
				if ($miscount == 0) {
932
					echo I18N::translate('No ancestors in the database.'), "<br>";
933
				} else {
934
					echo /* I18N: %1$s is a count of individuals, %2$s is a list of their names */ I18N::plural(
935
						'%1$s individual is missing birthplace map coordinates: %2$s.',
936
						'%1$s individuals are missing birthplace map coordinates: %2$s.',
937
						$miscount, I18N::number($miscount), $missing),
938
						'<br>';
939
				}
940
			}
941
		}
942
		echo '</td>';
943
		echo '</tr>';
944
		echo '</table>';
945
		echo '</div>'; // close #pedigreemap_chart
946
		echo '</div>'; // close #pedigreemap-page
947
		?>
948
		<!-- end of map display -->
949
		<!-- Start of map scripts -->
950
		<?php
951
		echo '<script src="', $this->googleMapsScript(), '"></script>';
952
		$controller->addInlineJavascript($this->pedigreeMapJavascript());
953
	}
954
955
	/**
956
	 * Create the Javascript to activate the map.
957
	 *
958
	 * @return string
959
	 */
960
	private function pedigreeMapJavascript() {
961
		global $PEDIGREE_GENERATIONS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
962
963
		// The HomeControl returns the map to the original position and style
964
		$js = 'function HomeControl(controlDiv, pm_map) {' .
965
			// Set CSS styles for the DIV containing the control
966
			// Setting padding to 5 px will offset the control from the edge of the map
967
			'controlDiv.style.paddingTop = "5px";
968
			controlDiv.style.paddingRight = "0px";' .
969
			// Set CSS for the control border
970
			'var controlUI = document.createElement("DIV");
971
			controlUI.style.backgroundColor = "white";
972
			controlUI.style.color = "black";
973
			controlUI.style.borderColor = "black";
974
			controlUI.style.borderColor = "black";
975
			controlUI.style.borderStyle = "solid";
976
			controlUI.style.borderWidth = "2px";
977
			controlUI.style.cursor = "pointer";
978
			controlUI.style.textAlign = "center";
979
			controlUI.title = "";
980
			controlDiv.appendChild(controlUI);' .
981
			// Set CSS for the control interior
982
			'var controlText = document.createElement("DIV");
983
			controlText.style.fontFamily = "Arial,sans-serif";
984
			controlText.style.fontSize = "12px";
985
			controlText.style.paddingLeft = "15px";
986
			controlText.style.paddingRight = "15px";
987
			controlText.innerHTML = "<b>' . I18N::translate('Redraw map') . '<\/b>";
988
			controlUI.appendChild(controlText);' .
989
			// Setup the click event listeners: simply set the map to original LatLng
990
			'google.maps.event.addDomListener(controlUI, "click", function() {
991
				pm_map.setMapTypeId(google.maps.MapTypeId.TERRAIN),
992
				pm_map.fitBounds(bounds),
993
				pm_map.setCenter(bounds.getCenter()),
994
				infowindow.close()
995
				if (document.getElementById(lastlinkid) != null) {
996
					document.getElementById(lastlinkid).className = "person_box:target";
997
				}
998
			});
999
		}' .
1000
		// This function picks up the click and opens the corresponding info window
1001
		'function myclick(i) {
1002
			if (document.getElementById(lastlinkid) != null) {
1003
				document.getElementById(lastlinkid).className = "person_box:target";
1004
			}
1005
			google.maps.event.trigger(gmarkers[i], "click");
1006
			return false;
1007
		}' .
1008
		// this variable will collect the html which will eventually be placed in the side_bar
1009
		'var side_bar_html = "";' .
1010
		// arrays to hold copies of the markers and html used by the side_bar
1011
		// because the function closure trick doesnt work there
1012
		'var gmarkers = [];
1013
		var i = 0;
1014
		var lastlinkid;
1015
		var infowindow = new google.maps.InfoWindow({});' .
1016
		// === Create an associative array of GIcons()
1017
		'var gicons = [];
1018
		gicons["1"]        = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon1.png")
1019
		gicons["1"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow50.png",
1020
									new google.maps.Size(37, 34), // Shadow size
1021
									new google.maps.Point(0, 0),  // Shadow origin
1022
									new google.maps.Point(10, 34) // Shadow anchor is base of image
1023
								);
1024
		gicons["2"]         = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon2.png")
1025
		gicons["2"].shadow  = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow50.png",
1026
									new google.maps.Size(37, 34), // Shadow size
1027
									new google.maps.Point(0, 0),  // Shadow origin
1028
									new google.maps.Point(10, 34) // Shadow anchor is base of image
1029
								);
1030
		gicons["2L"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon2L.png",
1031
									new google.maps.Size(32, 32), // Image size
1032
									new google.maps.Point(0, 0),  // Image origin
1033
									new google.maps.Point(28, 28) // Image anchor
1034
								);
1035
		gicons["2L"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-left-large.png",
1036
									new google.maps.Size(49, 32), // Shadow size
1037
									new google.maps.Point(0, 0),  // Shadow origin
1038
									new google.maps.Point(32, 27) // Shadow anchor is base of image
1039
								);
1040
		gicons["2R"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon2R.png",
1041
									new google.maps.Size(32, 32), // Image size
1042
									new google.maps.Point(0, 0),  // Image origin
1043
									new google.maps.Point(4, 28)  // Image anchor
1044
								);
1045
		gicons["2R"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-right-large.png",
1046
									new google.maps.Size(49, 32), // Shadow size
1047
									new google.maps.Point(0, 0),  // Shadow origin
1048
									new google.maps.Point(15, 27) // Shadow anchor is base of image
1049
								);
1050
		gicons["2Ls"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon2Ls.png",
1051
									new google.maps.Size(24, 24), // Image size
1052
									new google.maps.Point(0, 0),  // Image origin
1053
									new google.maps.Point(22, 22) // Image anchor
1054
								);
1055
		gicons["2Rs"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon2Rs.png",
1056
									new google.maps.Size(24, 24), // Image size
1057
									new google.maps.Point(0, 0),  // Image origin
1058
									new google.maps.Point(2, 22)  // Image anchor
1059
								);
1060
		gicons["3"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon3.png")
1061
		gicons["3"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow50.png",
1062
									new google.maps.Size(37, 34), // Shadow size
1063
									new google.maps.Point(0, 0),  // Shadow origin
1064
									new google.maps.Point(10, 34) // Shadow anchor is base of image
1065
								);
1066
		gicons["3L"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon3L.png",
1067
									new google.maps.Size(32, 32), // Image size
1068
									new google.maps.Point(0, 0),  // Image origin
1069
									new google.maps.Point(28, 28) // Image anchor
1070
								);
1071
		gicons["3L"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-left-large.png",
1072
									new google.maps.Size(49, 32), // Shadow size
1073
									new google.maps.Point(0, 0),  // Shadow origin
1074
									new google.maps.Point(32, 27) // Shadow anchor is base of image
1075
								);
1076
		gicons["3R"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon3R.png",
1077
									new google.maps.Size(32, 32), // Image size
1078
									new google.maps.Point(0, 0),  // Image origin
1079
									new google.maps.Point(4, 28)  // Image anchor
1080
								);
1081
		gicons["3R"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-right-large.png",
1082
									new google.maps.Size(49, 32), // Shadow size
1083
									new google.maps.Point(0, 0),  // Shadow origin
1084
									new google.maps.Point(15, 27) // Shadow anchor is base of image
1085
								);
1086
		gicons["3Ls"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon3Ls.png",
1087
									new google.maps.Size(24, 24), // Image size
1088
									new google.maps.Point(0, 0),  // Image origin
1089
									new google.maps.Point(22, 22) // Image anchor
1090
								);
1091
		gicons["3Rs"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon3Rs.png",
1092
									new google.maps.Size(24, 24), // Image size
1093
									new google.maps.Point(0, 0),  // Image origin
1094
									new google.maps.Point(2, 22)  // Image anchor
1095
								);
1096
		gicons["4"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon4.png")
1097
		gicons["4"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow50.png",
1098
									new google.maps.Size(37, 34), // Shadow size
1099
									new google.maps.Point(0, 0),  // Shadow origin
1100
									new google.maps.Point(10, 34) // Shadow anchor is base of image
1101
								);
1102
		gicons["4L"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon4L.png",
1103
									new google.maps.Size(32, 32), // Image size
1104
									new google.maps.Point(0, 0),  // Image origin
1105
									new google.maps.Point(28, 28) // Image anchor
1106
								);
1107
		gicons["4L"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-left-large.png",
1108
									new google.maps.Size(49, 32), // Shadow size
1109
									new google.maps.Point(0, 0),  // Shadow origin
1110
									new google.maps.Point(32, 27) // Shadow anchor is base of image
1111
								);
1112
		gicons["4R"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon4R.png",
1113
									new google.maps.Size(32, 32), // Image size
1114
									new google.maps.Point(0, 0),  // Image origin
1115
									new google.maps.Point(4, 28)  // Image anchor
1116
								);
1117
		gicons["4R"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-right-large.png",
1118
									new google.maps.Size(49, 32), // Shadow size
1119
									new google.maps.Point(0, 0),  // Shadow origin
1120
									new google.maps.Point(15, 27) // Shadow anchor is base of image
1121
								);
1122
		gicons["4Ls"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon4Ls.png",
1123
									new google.maps.Size(24, 24), // Image size
1124
									new google.maps.Point(0, 0),  // Image origin
1125
									new google.maps.Point(22, 22) // Image anchor
1126
								);
1127
		gicons["4Rs"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon4Rs.png",
1128
									new google.maps.Size(24, 24), // Image size
1129
									new google.maps.Point(0, 0),  // Image origin
1130
									new google.maps.Point(2, 22)  // Image anchor
1131
								);
1132
		gicons["5"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon5.png")
1133
		gicons["5"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow50.png",
1134
									new google.maps.Size(37, 34), // Shadow size
1135
									new google.maps.Point(0, 0),  // Shadow origin
1136
									new google.maps.Point(10, 34) // Shadow anchor is base of image
1137
								);
1138
		gicons["5L"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon5L.png",
1139
									new google.maps.Size(32, 32), // Image size
1140
									new google.maps.Point(0, 0),  // Image origin
1141
									new google.maps.Point(28, 28) // Image anchor
1142
								);
1143
		gicons["5L"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-left-large.png",
1144
									new google.maps.Size(49, 32), // Shadow size
1145
									new google.maps.Point(0, 0),  // Shadow origin
1146
									new google.maps.Point(32, 27) // Shadow anchor is base of image
1147
								);
1148
		gicons["5R"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon5R.png",
1149
									new google.maps.Size(32, 32), // Image size
1150
									new google.maps.Point(0, 0),  // Image origin
1151
									new google.maps.Point(4, 28)  // Image anchor
1152
								);
1153
		gicons["5R"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-right-large.png",
1154
									new google.maps.Size(49, 32), // Shadow size
1155
									new google.maps.Point(0, 0),  // Shadow origin
1156
									new google.maps.Point(15, 27) // Shadow anchor is base of image
1157
								);
1158
		gicons["5Ls"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon5Ls.png",
1159
									new google.maps.Size(24, 24), // Image size
1160
									new google.maps.Point(0, 0),  // Image origin
1161
									new google.maps.Point(22, 22) // Image anchor
1162
								);
1163
		gicons["5Rs"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon5Rs.png",
1164
									new google.maps.Size(24, 24), // Image size
1165
									new google.maps.Point(0, 0),  // Image origin
1166
									new google.maps.Point(2, 22)  // Image anchor
1167
								);
1168
		gicons["6"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon6.png")
1169
		gicons["6"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow50.png",
1170
									new google.maps.Size(37, 34), // Shadow size
1171
									new google.maps.Point(0, 0),  // Shadow origin
1172
									new google.maps.Point(10, 34) // Shadow anchor is base of image
1173
								);
1174
		gicons["6L"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon6L.png",
1175
									new google.maps.Size(32, 32), // Image size
1176
									new google.maps.Point(0, 0),  // Image origin
1177
									new google.maps.Point(28, 28) // Image anchor
1178
								);
1179
		gicons["6L"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-left-large.png",
1180
									new google.maps.Size(49, 32), // Shadow size
1181
									new google.maps.Point(0, 0),  // Shadow origin
1182
									new google.maps.Point(32, 27) // Shadow anchor is base of image
1183
								);
1184
		gicons["6R"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon6R.png",
1185
									new google.maps.Size(32, 32), // Image size
1186
									new google.maps.Point(0, 0),  // Image origin
1187
									new google.maps.Point(4, 28)  // Image anchor
1188
								);
1189
		gicons["6R"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-right-large.png",
1190
									new google.maps.Size(49, 32), // Shadow size
1191
									new google.maps.Point(0, 0),  // Shadow origin
1192
									new google.maps.Point(15, 27) // Shadow anchor is base of image
1193
								);
1194
		gicons["6Ls"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon6Ls.png",
1195
									new google.maps.Size(24, 24), // Image size
1196
									new google.maps.Point(0, 0),  // Image origin
1197
									new google.maps.Point(22, 22) // Image anchor
1198
								);
1199
		gicons["6Rs"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon6Rs.png",
1200
									new google.maps.Size(24, 24), // Image size
1201
									new google.maps.Point(0, 0),  // Image origin
1202
									new google.maps.Point(2, 22)  // Image anchor
1203
								);
1204
		gicons["7"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon7.png")
1205
		gicons["7"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow50.png",
1206
									new google.maps.Size(37, 34), // Shadow size
1207
									new google.maps.Point(0, 0),  // Shadow origin
1208
									new google.maps.Point(10, 34) // Shadow anchor is base of image
1209
								);
1210
		gicons["7L"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon7L.png",
1211
									new google.maps.Size(32, 32), // Image size
1212
									new google.maps.Point(0, 0),  // Image origin
1213
									new google.maps.Point(28, 28) // Image anchor
1214
								);
1215
		gicons["7L"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-left-large.png",
1216
									new google.maps.Size(49, 32), // Shadow size
1217
									new google.maps.Point(0, 0),  // Shadow origin
1218
									new google.maps.Point(32, 27) // Shadow anchor is base of image
1219
								);
1220
		gicons["7R"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon7R.png",
1221
									new google.maps.Size(32, 32), // Image size
1222
									new google.maps.Point(0, 0),  // Image origin
1223
									new google.maps.Point(4, 28)  // Image anchor
1224
								);
1225
		gicons["7R"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-right-large.png",
1226
									new google.maps.Size(49, 32), // Shadow size
1227
									new google.maps.Point(0, 0),  // Shadow origin
1228
									new google.maps.Point(15, 27) // Shadow anchor is base of image
1229
								);
1230
		gicons["7Ls"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon7Ls.png",
1231
									new google.maps.Size(24, 24), // Image size
1232
									new google.maps.Point(0, 0),  // Image origin
1233
									new google.maps.Point(22, 22) // Image anchor
1234
								);
1235
		gicons["7Rs"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon7Rs.png",
1236
									new google.maps.Size(24, 24), // Image size
1237
									new google.maps.Point(0, 0),  // Image origin
1238
									new google.maps.Point(2, 22)  // Image anchor
1239
								);
1240
		gicons["8"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon8.png")
1241
		gicons["8"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow50.png",
1242
									new google.maps.Size(37, 34), // Shadow size
1243
									new google.maps.Point(0, 0),  // Shadow origin
1244
									new google.maps.Point(10, 34) // Shadow anchor is base of image
1245
								);
1246
		gicons["8L"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon8L.png",
1247
									new google.maps.Size(32, 32), // Image size
1248
									new google.maps.Point(0, 0),  // Image origin
1249
									new google.maps.Point(28, 28) // Image anchor
1250
								);
1251
		gicons["8L"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-left-large.png",
1252
									new google.maps.Size(49, 32), // Shadow size
1253
									new google.maps.Point(0, 0),  // Shadow origin
1254
									new google.maps.Point(32, 27) // Shadow anchor is base of image
1255
								);
1256
		gicons["8R"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon8R.png",
1257
									new google.maps.Size(32, 32), // Image size
1258
									new google.maps.Point(0, 0),  // Image origin
1259
									new google.maps.Point(4, 28)  // Image anchor
1260
								);
1261
		gicons["8R"].shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/shadow-right-large.png",
1262
									new google.maps.Size(49, 32), // Shadow size
1263
									new google.maps.Point(0, 0),  // Shadow origin
1264
									new google.maps.Point(15, 27) // Shadow anchor is base of image
1265
								);
1266
		gicons["8Ls"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon8Ls.png",
1267
									new google.maps.Size(24, 24), // Image size
1268
									new google.maps.Point(0, 0),  // Image origin
1269
									new google.maps.Point(22, 22) // Image anchor
1270
								);
1271
		gicons["8Rs"] = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+"googlemap/images/icon8Rs.png",
1272
									new google.maps.Size(24, 24), // Image size
1273
									new google.maps.Point(0, 0),  // Image origin
1274
									new google.maps.Point(2, 22)  // Image anchor
1275
								);' .
1276
		// / A function to create the marker and set up the event window
1277
		'function createMarker(point, name, html, mhtml, icontype) {
1278
			var contentString = "<div id=\'iwcontent_edit\'>"+mhtml+"<\/div>";' .
1279
			// Create a marker with the requested icon
1280
			'var marker = new google.maps.Marker({
1281
				icon:     gicons[icontype],
1282
				shadow:   gicons[icontype].shadow,
1283
				map:      pm_map,
1284
				position: point,
1285
				zIndex:   0
1286
			});
1287
			var linkid = "link"+i;
1288
			google.maps.event.addListener(marker, "click", function() {
1289
				infowindow.close();
1290
				infowindow.setContent(contentString);
1291
				infowindow.open(pm_map, marker);
1292
				document.getElementById(linkid).className = "person_box";
1293
				if (document.getElementById(lastlinkid) != null) {
1294
					document.getElementById(lastlinkid).className = "person_box:target";
1295
				}
1296
				lastlinkid=linkid;
1297
			});' .
1298
			// save the info we need to use later for the side_bar
1299
			'gmarkers[i] = marker;' .
1300
			// add a line to the side_bar html
1301
			'side_bar_html += "<br><div id=\'"+linkid+"\' onclick=\'return myclick(" + i + ")\'>" + html +"<br></div>";
1302
			i++;
1303
			return marker;
1304
		};' .
1305
		// create the map
1306
		'var myOptions = {
1307
			zoom: 6,
1308
			center: new google.maps.LatLng(0, 0),
1309
			mapTypeId: google.maps.MapTypeId.TERRAIN,  // ROADMAP, SATELLITE, HYBRID, TERRAIN
1310
			mapTypeControlOptions: {
1311
				style: google.maps.MapTypeControlStyle.DROPDOWN_MENU  // DEFAULT, DROPDOWN_MENU, HORIZONTAL_BAR
1312
			},
1313
			navigationControlOptions: {
1314
				position: google.maps.ControlPosition.TOP_RIGHT,  // BOTTOM, BOTTOM_LEFT, LEFT, TOP, etc
1315
				style: google.maps.NavigationControlStyle.SMALL   // ANDROID, DEFAULT, SMALL, ZOOM_PAN
1316
			},
1317
			streetViewControl: false,  // Show Pegman or not
1318
			scrollwheel: true
1319
		};
1320
		var pm_map = new google.maps.Map(document.getElementById("pm_map"), myOptions);
1321
		google.maps.event.addListener(pm_map, "click", function() {
1322
			if (document.getElementById(lastlinkid) != null) {
1323
				document.getElementById(lastlinkid).className = "person_box:target";
1324
			}
1325
		infowindow.close();
1326
		});' .
1327
		// Create the DIV to hold the control and call HomeControl() passing in this DIV. --
1328
		'var homeControlDiv = document.createElement("DIV");
1329
		var homeControl = new HomeControl(homeControlDiv, pm_map);
1330
		homeControlDiv.index = 1;
1331
		pm_map.controls[google.maps.ControlPosition.TOP_RIGHT].push(homeControlDiv);' .
1332
		// create the map bounds
1333
		'var bounds = new google.maps.LatLngBounds();';
1334
		// add the points
1335
		$curgen       = 1;
1336
		$count        = 0;
1337
		$colored_line = array(
1338
			'1' => '#FF0000',
1339
			'2' => '#0000FF',
1340
			'3' => '#00FF00',
1341
			'4' => '#FFFF00',
1342
			'5' => '#00FFFF',
1343
			'6' => '#FF00FF',
1344
			'7' => '#C0C0FF',
1345
			'8' => '#808000',
1346
		);
1347
		$lat        = array();
1348
		$lon        = array();
1349
		$latlongval = array();
1350
		for ($i = 0; $i < $this->treesize; $i++) {
1351
			// moved up to grab the sex of the individuals
1352
			$person = $this->ancestors[$i];
1353
			if ($person) {
1354
				$name = $person->getFullName();
1355
1356
				// -- check to see if we have moved to the next generation
1357
				if ($i + 1 >= pow(2, $curgen)) {
1358
					$curgen++;
1359
				}
1360
1361
				$relationship = FunctionsCharts::getSosaName($i + 1);
1362
1363
				$event = '<img src="' . WT_STATIC_URL . WT_MODULES_DIR . 'googlemap/images/sq' . $curgen . '.png" width="10" height="10"> ' .
1364
					'<strong>' . $relationship . '</strong>';
1365
				// add thumbnail image
1366
				if ($person->getTree()->getPreference('SHOW_HIGHLIGHT_IMAGES')) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $person->getTree()->getP...SHOW_HIGHLIGHT_IMAGES') of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1367
					$image = $person->displayImage();
1368
				} else {
1369
					$image = '';
1370
				}
1371
				// end of add image
1372
1373
				$birth     = $person->getFirstFact('BIRT');
1374
				$dataleft  = Filter::escapeJs($image . $event . ' — ' . $name);
1375
				$datamid   = Filter::escapeJs(' <span><a href="' . $person->getHtmlUrl() . '">(' . I18N::translate('View individual') . ')</a></span>');
1376
				$dataright = $birth ? Filter::escapeJs($birth->summary()) : '';
1377
1378
				$latlongval[$i] = $this->getLatitudeAndLongitudeFromPlaceLocation($person->getBirthPlace());
1379
				if ($latlongval[$i]) {
1380
					$lat[$i] = (double) str_replace(array('N', 'S', ','), array('', '-', '.'), $latlongval[$i]->pl_lati);
1381
					$lon[$i] = (double) str_replace(array('E', 'W', ','), array('', '-', '.'), $latlongval[$i]->pl_long);
1382
					if ($lat[$i] || $lon[$i]) {
1383
						$marker_number = $curgen;
1384
						$dups          = 0;
1385
						for ($k = 0; $k < $i; $k++) {
1386
							if ($latlongval[$i] == $latlongval[$k]) {
1387
								$dups++;
1388
								switch ($dups) {
1389
								case 1:
1390
									$marker_number = $curgen . 'L';
1391
									break;
1392
								case 2:
1393
									$marker_number = $curgen . 'R';
1394
									break;
1395
								case 3:
1396
									$marker_number = $curgen . 'Ls';
1397
									break;
1398
								case 4:
1399
									$marker_number = $curgen . 'Rs';
1400
									break;
1401
								case 5: //adjust position where markers have same coodinates
1402
								default:
1403
									$marker_number = $curgen;
1404
									$lon[$i]       = $lon[$i] + 0.0025;
1405
									$lat[$i]       = $lat[$i] + 0.0025;
1406
									break;
1407
								}
1408
							}
1409
						}
1410
						$js .= 'var point = new google.maps.LatLng(' . $lat[$i] . ',' . $lon[$i] . ');';
1411
						$js .= "var marker = createMarker(point, \"" . Filter::escapeJs($name) . "\",\"<div>" . $dataleft . $datamid . $dataright . "</div>\", \"";
1412
						$js .= "<div class='iwstyle'>";
1413
						$js .= "<a href='module.php?ged=" . $person->getTree()->getNameUrl() . "&amp;mod=googlemap&amp;mod_action=pedigree_map&amp;rootid=" . $person->getXref() . "&amp;PEDIGREE_GENERATIONS={$PEDIGREE_GENERATIONS}";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $PEDIGREE_GENERATIONS instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
1414
						$js .= "' title='" . I18N::translate('Pedigree map') . "'>" . $dataleft . "</a>" . $datamid . $dataright . "</div>\", \"" . $marker_number . "\");";
1415
						// Construct the polygon lines
1416
						$to_child = (intval(($i - 1) / 2)); // Draw a line from parent to child
1417
						if (array_key_exists($to_child, $lat) && $lat[$to_child] != 0 && $lon[$to_child] != 0) {
1418
							$js .= '
1419
								var linecolor;
1420
								var plines;
1421
								var lines = [new google.maps.LatLng(' . $lat[$i] . ',' . $lon[$i] . '),
1422
									new google.maps.LatLng(' . $lat[$to_child] . ',' . $lon[$to_child] . ')];
1423
								linecolor = "' . $colored_line[$curgen] . '";
1424
								plines = new google.maps.Polygon({
1425
									paths: lines,
1426
									strokeColor: linecolor,
1427
									strokeOpacity: 0.8,
1428
									strokeWeight: 3,
1429
									fillColor: "#FF0000",
1430
									fillOpacity: 0.1
1431
								});
1432
								plines.setMap(pm_map);';
1433
						}
1434
						// Extend and fit marker bounds
1435
						$js .= 'bounds.extend(point);';
1436
						$js .= 'pm_map.fitBounds(bounds);';
1437
						$count++;
1438
					}
1439
				}
1440
			} else {
1441
				$latlongval[$i] = null;
1442
			}
1443
		}
1444
		$js .= 'pm_map.setCenter(bounds.getCenter());' .
1445
		// Close the sidebar highlight when the infowindow is closed
1446
		'google.maps.event.addListener(infowindow, "closeclick", function() {
1447
			document.getElementById(lastlinkid).className = "person_box:target";
1448
		});' .
1449
		// put the assembled side_bar_html contents into the side_bar div
1450
		'document.getElementById("side_bar").innerHTML = side_bar_html;' .
1451
		// create the context menu div
1452
		'var contextmenu = document.createElement("div");
1453
			contextmenu.style.visibility="hidden";
1454
			contextmenu.innerHTML = "<a href=\'#\' onclick=\'zoomIn()\'><div class=\'optionbox\'>&nbsp;&nbsp;' . I18N::translate('Zoom in') . '&nbsp;&nbsp;</div></a>"
1455
								+ "<a href=\'#\' onclick=\'zoomOut()\'><div class=\'optionbox\'>&nbsp;&nbsp;' . I18N::translate('Zoom out') . '&nbsp;&nbsp;</div></a>"
1456
								+ "<a href=\'#\' onclick=\'zoomInHere()\'><div class=\'optionbox\'>&nbsp;&nbsp;' . I18N::translate('Zoom in here') . '</div></a>"
1457
								+ "<a href=\'#\' onclick=\'zoomOutHere()\'><div class=\'optionbox\'>&nbsp;&nbsp;' . I18N::translate('Zoom out here') . '&nbsp;&nbsp;</div></a>"
1458
								+ "<a href=\'#\' onclick=\'centreMapHere()\'><div class=\'optionbox\'>&nbsp;&nbsp;' . I18N::translate('Center map here') . '&nbsp;&nbsp;</div></a>";' .
1459
		// listen for singlerightclick
1460
		'google.maps.event.addListener(pm_map,"singlerightclick", function(pixel,tile) {' .
1461
			// store the "pixel" info in case we need it later
1462
			// adjust the context menu location if near an egde
1463
			// create a GControlPosition
1464
			// apply it to the context menu, and make the context menu visible
1465
			'clickedPixel = pixel;
1466
			var x=pixel.x;
1467
			var y=pixel.y;
1468
			if (x > pm_map.getSize().width - 120) { x = pm_map.getSize().width - 120 }
1469
			if (y > pm_map.getSize().height - 100) { y = pm_map.getSize().height - 100 }
1470
			var pos = new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(x,y));
1471
			pos.apply(contextmenu);
1472
			contextmenu.style.visibility = "visible";
1473
		});
1474
		' .
1475
		// functions that perform the context menu options
1476
		'function zoomIn() {' .
1477
			// perform the requested operation
1478
			'pm_map.zoomIn();' .
1479
			// hide the context menu now that it has been used
1480
			'contextmenu.style.visibility="hidden";
1481
		}
1482
		function zoomOut() {' .
1483
			// perform the requested operation
1484
			'pm_map.zoomOut();' .
1485
			// hide the context menu now that it has been used
1486
			'contextmenu.style.visibility="hidden";
1487
		}
1488
		function zoomInHere() {' .
1489
			// perform the requested operation
1490
			'var point = pm_map.fromContainerPixelToLatLng(clickedPixel)
1491
			pm_map.zoomIn(point,true);' .
1492
			// hide the context menu now that it has been used
1493
			'contextmenu.style.visibility="hidden";
1494
		}
1495
		function zoomOutHere() {' .
1496
			// perform the requested operation
1497
			'var point = pm_map.fromContainerPixelToLatLng(clickedPixel)
1498
			pm_map.setCenter(point,pm_map.getZoom()-1);' .
1499
			// There is no pm_map.zoomOut() equivalent
1500
			// hide the context menu now that it has been used
1501
			'contextmenu.style.visibility="hidden";
1502
		}
1503
		function centreMapHere() {' .
1504
			// perform the requested operation
1505
			'var point = pm_map.fromContainerPixelToLatLng(clickedPixel)
1506
			pm_map.setCenter(point);' .
1507
			// hide the context menu now that it has been used
1508
			'contextmenu.style.visibility="hidden";
1509
		}' .
1510
		// If the user clicks on the map, close the context menu
1511
		'google.maps.event.addListener(pm_map, "click", function() {
1512
			contextmenu.style.visibility="hidden";
1513
		});';
1514
1515
		return $js;
1516
	}
1517
1518
	/**
1519
	 * Check places for missing data, etc.
1520
	 */
1521
	private function adminPlaceCheck() {
1522
		global $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1523
1524
		$gedcom_id = Filter::get('gedcom_id', null, $WT_TREE->getTreeId());
1525
		$country   = Filter::get('country', '.+', 'XYZ');
1526
		$state     = Filter::get('state', '.+', 'XYZ');
1527
		$matching  = Filter::getBool('matching');
1528
1529
		$controller = new PageController;
1530
		$controller
1531
			->restrictAccess(Auth::isAdmin())
1532
			->setPageTitle(I18N::translate('Google Maps™'))
1533
			->pageHeader();
1534
1535
		?>
1536
		<ol class="breadcrumb small">
1537
			<li><a href="admin.php"><?php echo I18N::translate('Control panel') ?></a></li>
1538
			<li><a href="admin_modules.php"><?php echo I18N::translate('Module administration') ?></a></li>
1539
			<li class="active"><?php echo $controller->getPageTitle() ?></li>
1540
		</ol>
1541
1542
		<ul class="nav nav-tabs nav-justified" role="tablist">
1543
			<li role="presentation">
1544
				<a href="?mod=googlemap&amp;mod_action=admin_config" role="tab">
1545
					<?php echo I18N::translate('Google Maps™ preferences') ?>
1546
				</a>
1547
			</li>
1548
			<li role="presentation">
1549
				<a href="?mod=googlemap&amp;mod_action=admin_places">
1550
					<?php echo I18N::translate('Geographic data') ?>
1551
				</a>
1552
			</li>
1553
			<li role="presentation" class="active">
1554
				<a href="#">
1555
					<?php echo I18N::translate('Place check') ?>
1556
				</a>
1557
			</li>
1558
		</ul>
1559
		<?php
1560
1561
		echo '<h2>', I18N::translate('Place check'), '</h2>';
1562
1563
		// User options
1564
		$rows = Database::prepare("SELECT pl_id, pl_place FROM `##placelocation` WHERE pl_level=0 ORDER BY pl_place")->fetchAssoc();
1565
1566
		echo '<form name="placecheck" class="form form-inline">';
1567
		echo '<input type="hidden" name="mod" value="', $this->getName(), '">';
1568
		echo '<input type="hidden" name="mod_action" value="admin_placecheck">';
1569
		echo '<div class="form-group">';
1570
		echo '<label for="gedcom_id">', I18N::translate('Family tree'), '</label> ';
1571
		echo FunctionsEdit::selectEditControl('gedcom_id', Tree::getIdList(), null, $gedcom_id, ' onchange="this.form.submit();" class="form-control"'), ' ';
1572
		echo '<label for="country">', I18N::translate('Country'), '</label> ';
1573
		echo '<select name="country" onchange="this.form.submit();" class="form-control"> ';
1574
		echo '<option value="XYZ">', I18N::translate('All'), '</option>';
1575
		foreach ($rows as $id => $place) {
1576
			echo '<option value="', Filter::escapeHtml($place), '" ';
1577
			if ($place == $country) {
1578
				echo 'selected';
1579
				$par_id = $id;
1580
			}
1581
			echo '>', Filter::escapeHtml($place), '</option>';
1582
		}
1583
		echo '</select> ';
1584
		if ($country != 'XYZ') {
1585
			echo '<label for="state">', /* I18N: Part of a country, state/region/county */ I18N::translate('Subdivision'), '</label> ';
1586
			echo '<select name="state" onchange="this.form.submit();" class="form-control">';
1587
			echo '<option value="XYZ">', I18N::translate('All'), '</option>';
1588
			$places = Database::prepare("SELECT pl_place FROM `##placelocation` WHERE pl_parent_id=? ORDER BY pl_place")
1589
				->execute(array($par_id))
0 ignored issues
show
Bug introduced by
The variable $par_id does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1590
				->fetchOneColumn();
1591
			foreach ($places as $place) {
1592
				echo '<option value="', Filter::escapeHtml($place), '" ', $place == $state ? 'selected' : '', '>', Filter::escapeHtml($place), '</option>';
1593
			}
1594
			echo '</select> ';
1595
		}
1596
		echo '<div class="checkbox-inline">';
1597
		echo '<label for="matching">';
1598
		echo '<input type="checkbox" name="matching" value="1" onchange="this.form.submit();" ', ($matching ? 'checked' : ''), '>';
1599
		echo I18N::translate('Include fully matched places');
1600
		echo '</label>';
1601
		echo '</div></div>';
1602
		echo '</form>';
1603
		echo '<hr>';
1604
1605
		//Select all '2 PLAC ' tags in the file and create array
1606
		$place_list = array();
1607
		$ged_data   = Database::prepare("SELECT i_gedcom FROM `##individuals` WHERE i_gedcom LIKE ? AND i_file=?")
1608
			->execute(array("%\n2 PLAC %", $gedcom_id))
1609
			->fetchOneColumn();
1610 View Code Duplication
		foreach ($ged_data as $ged_datum) {
1611
			preg_match_all('/\n2 PLAC (.+)/', $ged_datum, $matches);
1612
			foreach ($matches[1] as $match) {
1613
				$place_list[$match] = true;
1614
			}
1615
		}
1616
		$ged_data = Database::prepare("SELECT f_gedcom FROM `##families` WHERE f_gedcom LIKE ? AND f_file=?")
1617
			->execute(array("%\n2 PLAC %", $gedcom_id))
1618
			->fetchOneColumn();
1619 View Code Duplication
		foreach ($ged_data as $ged_datum) {
1620
			preg_match_all('/\n2 PLAC (.+)/', $ged_datum, $matches);
1621
			foreach ($matches[1] as $match) {
1622
				$place_list[$match] = true;
1623
			}
1624
		}
1625
		// Unique list of places
1626
		$place_list = array_keys($place_list);
1627
1628
		// Apply_filter
1629
		if ($country == 'XYZ') {
1630
			$filter = '.*$';
1631
		} else {
1632
			$filter = preg_quote($country) . '$';
1633
			if ($state != 'XYZ') {
1634
				$filter = preg_quote($state) . ', ' . $filter;
1635
			}
1636
		}
1637
		$place_list = preg_grep('/' . $filter . '/', $place_list);
1638
1639
		//sort the array, limit to unique values, and count them
1640
		usort($place_list, '\Fisharebest\Webtrees\I18N::strcasecmp');
1641
		$i = count($place_list);
1642
1643
		//calculate maximum no. of levels to display
1644
		$x   = 0;
1645
		$max = 0;
1646
		while ($x < $i) {
1647
			$levels                 = explode(",", $place_list[$x]);
1648
			$parts                  = count($levels);
1649
			if ($parts > $max) {
1650
				$max = $parts;
1651
			}
1652
			$x++; }
1653
		$x = 0;
1654
1655
		//scripts for edit, add and refresh
1656
		?>
1657
		<script>
1658
			function edit_place_location(placeid) {
1659
				window.open('module.php?mod=googlemap&mod_action=places_edit&action=update&placeid='+placeid, '_blank', gmap_window_specs);
1660
				return false;
1661
			}
1662
1663
			function add_place_location(placeid) {
1664
				window.open('module.php?mod=googlemap&mod_action=places_edit&action=add&placeid='+placeid, '_blank', gmap_window_specs);
1665
				return false;
1666
			}
1667
		</script>
1668
		<?php
1669
1670
		//start to produce the display table
1671
		echo '<table class="table table-bordered table-condensed table-hover"><thead><tr>';
1672
		echo '<th rowspan="3">', I18N::translate('Place'), '</th>';
1673
		echo '<th colspan="', $max * 3, '">', I18N::translate('Geographic data'), '</th></tr>';
1674
		echo '<tr>';
1675
		for ($cols = 0; $cols < $max; ++$cols) {
1676
			if ($cols == 0) {
1677
				echo '<th colspan="3">', I18N::translate('Country'), '</th>';
1678
			} else {
1679
				echo '<th colspan="3">', I18N::translate('Level'), ' ', $cols + 1, '</th>';
1680
			}
1681
		}
1682
		echo '</tr><tr>';
1683
		for ($cols = 0; $cols < $max; ++$cols) {
1684
			echo '<th>', GedcomTag::getLabel('PLAC'), '</th>';
1685
			echo '<th>', I18N::translate('Latitude'), '</th>';
1686
			echo '<th>', I18N::translate('Longitude'), '</th>';
1687
		}
1688
		echo '</tr></thead><tbody>';
1689
		$countrows = 0;
1690
		$matched   = array();
1691
		while ($x < $i) {
1692
			$placestr = '';
1693
			$levels   = explode(', ', $place_list[$x]);
1694
			$parts    = count($levels);
1695
			$levels   = array_reverse($levels);
1696
			$placestr .= '<a href="placelist.php?action=show';
1697
			foreach ($levels as $pindex => $ppart) {
1698
				$placestr .= '&amp;parent[' . $pindex . ']=' . urlencode($ppart);
1699
			}
1700
			$placestr .= '">' . $place_list[$x] . "</a>";
1701
			$gedplace    = '<tr><td>' . $placestr . '</td>';
1702
			$z           = 0;
1703
			$id          = 0;
1704
			$level       = 0;
1705
			$matched[$x] = 0; // used to exclude places where the gedcom place is matched at all levels
1706
			$mapstr_edit = '<a href="#" dir="auto" onclick="edit_place_location(\'';
1707
			$mapstr_add  = '<a href="#" dir="auto" onclick="add_place_location(\'';
1708
			$mapstr3     = '';
1709
			$mapstr4     = '';
1710
			$mapstr5     = '\')" title=\'';
1711
			$mapstr6     = '\' >';
1712
			$mapstr7     = '\')">';
1713
			$mapstr8     = '</a>';
1714
			$plac        = array();
1715
			$lati        = array();
1716
			$long        = array();
1717
			while ($z < $parts) {
1718
				if ($levels[$z] == '') {
1719
					$levels[$z] = 'unknown'; // GoogleMap module uses "unknown" while GEDCOM uses , ,
1720
				}
1721
1722
				$placelist = $this->createPossiblePlaceNames($levels[$z], $z + 1); // add the necessary prefix/postfix values to the place name
1723
				foreach ($placelist as $key => $placename) {
1724
					$row =
1725
						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")
1726
							->execute(array($z, $id, $placename))
1727
							->fetchOneRow(PDO::FETCH_ASSOC);
1728
					if (!empty($row['pl_id'])) {
1729
						$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
1730
						break;
1731
					}
1732
				}
1733
				if ($row['pl_id'] != '') {
1734
					$id = $row['pl_id'];
0 ignored issues
show
Bug introduced by
The variable $row does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1735
				}
1736
1737
				if ($row['pl_place'] != '') {
1738
					$placestr2 = $mapstr_edit . $id . "&amp;level=" . $level . $mapstr3 . $mapstr5 . I18N::translate('Zoom=') . $row['pl_zoom'] . $mapstr6 . $row['pl_placerequested'] . $mapstr8;
1739
					if ($row['pl_place'] === 'unknown')
1740
						$matched[$x]++;
1741
				} else {
1742
					if ($levels[$z] === 'unknown') {
1743
						$placestr2 = $mapstr_add . $id . "&amp;level=" . $level . $mapstr3 . $mapstr7 . "<strong>" . I18N::translate('unknown') . "</strong>" . $mapstr8; $matched[$x]++;
1744
					} else {
1745
						$placestr2 = $mapstr_add . $id . "&amp;place_name=" . urlencode($levels[$z]) . "&amp;level=" . $level . $mapstr3 . $mapstr7 . '<span class="danger">' . $levels[$z] . '</span>' . $mapstr8; $matched[$x]++;
1746
					}
1747
				}
1748
				$plac[$z] = '<td>' . $placestr2 . '</td>';
1749 View Code Duplication
				if ($row['pl_lati'] == '0' && $row['pl_long'] == '0') {
1750
					$lati[$z] = '<td class="danger">0</td>';
1751
				} elseif ($row['pl_lati'] != '') {
1752
					$lati[$z] = '<td>' . $row['pl_lati'] . '</td>';
1753
				} else {
1754
					$lati[$z] = '<td class="danger"><i class="fa fa-warning"></i></td>';
1755
					$matched[$x]++;
1756
				}
1757 View Code Duplication
				if ($row['pl_lati'] == '0' && $row['pl_long'] == '0') {
1758
					$long[$z] = '<td class="danger">0</td>';
1759
				} elseif ($row['pl_long'] != '') {
1760
					$long[$z] = '<td>' . $row['pl_long'] . '</td>';
1761
				} else {
1762
					$long[$z] = '<td class="danger"><i class="fa fa-warning"></i></td>';
1763
					$matched[$x]++;
1764
				}
1765
				$level++;
1766
				$mapstr3 = $mapstr3 . "&amp;parent[" . $z . "]=" . Filter::escapeJs($row['pl_placerequested']);
1767
				$mapstr4 = $mapstr4 . "&amp;parent[" . $z . "]=" . Filter::escapeJs($levels[$z]);
1768
				$z++;
1769
			}
1770
			if ($matching) {
1771
				$matched[$x] = 1;
1772
			}
1773
			if ($matched[$x] != 0) {
1774
				echo $gedplace;
1775
				$z = 0;
1776
				while ($z < $max) {
1777
					if ($z < $parts) {
1778
						echo $plac[$z];
1779
						echo $lati[$z];
1780
						echo $long[$z];
1781
					} else {
1782
						echo '<td></td>';
1783
						echo '<td></td>';
1784
						echo '<td></td>';
1785
					}
1786
					$z++;
1787
				}
1788
				echo '</tr>';
1789
				$countrows++;
1790
			}
1791
			$x++;
1792
		}
1793
		echo '</tbody>';
1794
		echo '<tfoot>';
1795
		echo '<tr>';
1796
		echo '<th colspan="', (1 + 3 * $max), '">', /* I18N: A count of places */ I18N::translate('Total places: %s', I18N::number($countrows)), '</th>';
1797
		echo '</tr>';
1798
		echo '</tfoot>';
1799
		echo '</table>';
1800
	}
1801
1802
	/**
1803
	 * Does an individual (or their spouse-families) have any facts with places?
1804
	 *
1805
	 * @param Individual $individual
1806
	 *
1807
	 * @return bool
1808
	 */
1809
	private function checkMapData(Individual $individual) {
1810
		$statement = Database::prepare(
1811
			"SELECT COUNT(*) FROM `##placelinks` WHERE pl_gid = :xref AND pl_file = :tree_id"
1812
		);
1813
		$args = array(
1814
			'xref'    => $individual->getXref(),
1815
			'tree_id' => $individual->getTree()->getTreeId(),
1816
		);
1817
1818
		if ($statement->execute($args)->fetchOne()) {
1819
			return true;
1820
		}
1821
1822
		foreach ($individual->getSpouseFamilies() as $family) {
1823
			$args['xref'] = $family->getXref();
1824
			if ($statement->execute($args)->fetchOne()) {
1825
				return true;
1826
			}
1827
		}
1828
1829
		return false;
1830
	}
1831
1832
	/**
1833
	 * Remove prefixes from a place name to allow it to be matched.
1834
	 *
1835
	 * @param string   $prefix_list
1836
	 * @param string   $place
1837
	 * @param string[] $placelist
1838
	 *
1839
	 * @return string[]
1840
	 */
1841
	private function removePrefixFromPlaceName($prefix_list, $place, $placelist) {
1842
		if ($prefix_list) {
1843
			foreach (explode(';', $prefix_list) as $prefix) {
1844
				if ($prefix && substr($place, 0, strlen($prefix) + 1) == $prefix . ' ') {
1845
					$placelist[] = substr($place, strlen($prefix) + 1);
1846
				}
1847
			}
1848
		}
1849
1850
		return $placelist;
1851
	}
1852
1853
	/**
1854
	 * Remove suffixes from a place name to allow it to be matched.
1855
	 *
1856
	 * @param string   $suffix_list
1857
	 * @param string   $place
1858
	 * @param string[] $placelist
1859
	 *
1860
	 * @return string[]
1861
	 */
1862
	private function removeSuffixFromPlaceName($suffix_list, $place, $placelist) {
1863
		if ($suffix_list) {
1864
			foreach (explode(';', $suffix_list) as $postfix) {
1865
				if ($postfix && substr($place, -strlen($postfix) - 1) == ' ' . $postfix) {
1866
					$placelist[] = substr($place, 0, strlen($place) - strlen($postfix) - 1);
1867
				}
1868
			}
1869
		}
1870
1871
		return $placelist;
1872
	}
1873
1874
	/**
1875
	 * Remove prefixes and sufixes to allow place names to be matched.
1876
	 *
1877
	 * @param string   $prefix_list
1878
	 * @param string   $suffix_list
1879
	 * @param string   $place
1880
	 * @param string[] $placelist
1881
	 *
1882
	 * @return string[]
1883
	 */
1884
	private function removePrefixAndSuffixFromPlaceName($prefix_list, $suffix_list, $place, $placelist) {
1885
		if ($prefix_list && $suffix_list) {
1886
			foreach (explode(';', $prefix_list) as $prefix) {
1887
				foreach (explode(';', $suffix_list) as $postfix) {
1888
					if ($prefix && $postfix && substr($place, 0, strlen($prefix) + 1) == $prefix . ' ' && substr($place, -strlen($postfix) - 1) == ' ' . $postfix) {
1889
						$placelist[] = substr($place, strlen($prefix) + 1, strlen($place) - strlen($prefix) - strlen($postfix) - 2);
1890
					}
1891
				}
1892
			}
1893
		}
1894
1895
		return $placelist;
1896
	}
1897
1898
	/**
1899
	 * Match placenames with different prefixes and suffixes.
1900
	 *
1901
	 * @param string $placename
1902
	 * @param int    $level
1903
	 *
1904
	 * @return string[]
1905
	 */
1906
	private function createPossiblePlaceNames($placename, $level) {
1907
		$retlist = array();
1908
		if ($level <= 9) {
1909
			$retlist = $this->removePrefixAndSuffixFromPlaceName($this->getSetting('GM_PREFIX_' . $level), $this->getSetting('GM_POSTFIX_' . $level), $placename, $retlist); // Remove both
1910
			$retlist = $this->removePrefixFromPlaceName($this->getSetting('GM_PREFIX_' . $level), $placename, $retlist); // Remove prefix
1911
			$retlist = $this->removeSuffixFromPlaceName($this->getSetting('GM_POSTFIX_' . $level), $placename, $retlist); // Remove suffix
1912
		}
1913
		$retlist[] = $placename; // Exact
1914
1915
		return $retlist;
1916
	}
1917
1918
	/**
1919
	 * Get the map co-ordinates of a place.
1920
	 *
1921
	 * @param string $place
1922
	 *
1923
	 * @return null|\stdClass
0 ignored issues
show
Documentation introduced by
Should the return type not be \stdClass|array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
1924
	 */
1925
	private function getLatitudeAndLongitudeFromPlaceLocation($place) {
1926
		$parent   = explode(',', $place);
1927
		$parent   = array_reverse($parent);
1928
		$place_id = 0;
1929
		for ($i = 0; $i < count($parent); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1930
			$parent[$i] = trim($parent[$i]);
1931
			if (empty($parent[$i])) {
1932
				$parent[$i] = 'unknown'; // GoogleMap module uses "unknown" while GEDCOM uses , ,
1933
			}
1934
			$placelist = $this->createPossiblePlaceNames($parent[$i], $i + 1);
1935
			foreach ($placelist as $placename) {
1936
				$pl_id = Database::prepare(
1937
					"SELECT pl_id FROM `##placelocation` WHERE pl_level=? AND pl_parent_id=? AND pl_place LIKE ? ORDER BY pl_place"
1938
				)->execute(array($i, $place_id, $placename))->fetchOne();
1939
				if (!empty($pl_id)) {
1940
					break;
1941
				}
1942
			}
1943
			if (empty($pl_id)) {
1944
				break;
1945
			}
1946
			$place_id = $pl_id;
1947
		}
1948
1949
		return Database::prepare(
1950
			"SELECT sv_lati, sv_long, sv_bearing, sv_elevation, sv_zoom, pl_lati, pl_long, pl_zoom, pl_icon, pl_level" .
1951
			" FROM `##placelocation`" .
1952
			" WHERE pl_id = ?" .
1953
			" ORDER BY pl_place"
1954
		)->execute(array($place_id))->fetchOneRow();
1955
	}
1956
1957
	/**
1958
	 * Build a map for an individual.
1959
	 *
1960
	 * @param Individual $indi
1961
	 */
1962
	private function buildIndividualMap(Individual $indi) {
1963
		$GM_MAX_ZOOM = $this->getSetting('GM_MAX_ZOOM');
1964
1965
		$indifacts = $indi->getFacts();
1966
		foreach ($indi->getSpouseFamilies() as $family) {
1967
			$indifacts = array_merge($indifacts, $family->getFacts());
1968
		}
1969
1970
		Functions::sortFacts($indifacts);
1971
1972
		// Create the markers list array
1973
		$gmarks = array();
1974
		$i      = 0;
1975
1976
		foreach ($indifacts as $fact) {
1977
			if (!$fact->getPlace()->isEmpty()) {
1978
				$ctla = preg_match("/\d LATI (.*)/", $fact->getGedcom(), $match1);
1979
				$ctlo = preg_match("/\d LONG (.*)/", $fact->getGedcom(), $match2);
1980
1981
				if ($fact->getParent() instanceof Family) {
1982
					$spouse = $fact->getParent()->getSpouse($indi);
1983
				} else {
1984
					$spouse = null;
1985
				}
1986
				if ($ctla && $ctlo) {
1987
					$i++;
1988
					$gmarks[$i] = array(
1989
						'class'        => 'optionbox',
1990
						'date'         => $fact->getDate()->display(true),
1991
						'fact_label'   => $fact->getLabel(),
1992
						'image'        => $spouse ? $spouse->displayImage() : Theme::theme()->icon($fact),
1993
						'info'         => $fact->getValue(),
1994
						'lat'          => str_replace(array('N', 'S', ','), array('', '-', '.'), $match1[1]),
1995
						'lng'          => str_replace(array('E', 'W', ','), array('', '-', '.'), $match2[1]),
1996
						'name'         => $spouse ? '<a href="' . $spouse->getHtmlUrl() . '"' . $spouse->getFullName() . '</a>' : '',
1997
						'pl_icon'      => '',
1998
						'place'        => $fact->getPlace()->getFullName(),
1999
						'sv_bearing'   => '0',
2000
						'sv_elevation' => '0',
2001
						'sv_lati'      => '0',
2002
						'sv_long'      => '0',
2003
						'sv_zoom'      => '0',
2004
						'tooltip'      => $fact->getPlace()->getGedcomName(),
2005
					);
2006
				} else {
2007
					$latlongval = $this->getLatitudeAndLongitudeFromPlaceLocation($fact->getPlace()->getGedcomName());
2008
					if ($latlongval && $latlongval->pl_lati && $latlongval->pl_long) {
2009
						$i++;
2010
						$gmarks[$i] = array(
2011
							'class'        => 'optionbox',
2012
							'date'         => $fact->getDate()->display(true),
2013
							'fact_label'   => $fact->getLabel(),
2014
							'image'        => $spouse ? $spouse->displayImage() : Theme::theme()->icon($fact),
2015
							'info'         => $fact->getValue(),
2016
							'lat'          => str_replace(array('N', 'S', ','), array('', '-', '.'), $latlongval->pl_lati),
2017
							'lng'          => str_replace(array('E', 'W', ','), array('', '-', '.'), $latlongval->pl_long),
2018
							'name'         => $spouse ? '<a href="' . $spouse->getHtmlUrl() . '"' . $spouse->getFullName() . '</a>' : '',
2019
							'pl_icon'      => $latlongval->pl_icon,
2020
							'place'        => $fact->getPlace()->getFullName(),
2021
							'sv_bearing'   => $latlongval->sv_bearing,
2022
							'sv_elevation' => $latlongval->sv_elevation,
2023
							'sv_lati'      => $latlongval->sv_lati,
2024
							'sv_long'      => $latlongval->sv_long,
2025
							'sv_zoom'      => $latlongval->sv_zoom,
2026
							'tooltip'      => $fact->getPlace()->getGedcomName(),
2027
						);
2028
						if ($GM_MAX_ZOOM > $latlongval->pl_zoom) {
2029
							$GM_MAX_ZOOM = $latlongval->pl_zoom;
2030
						}
2031
					}
2032
				}
2033
			}
2034
		}
2035
2036
		// Add children to the markers list array
2037
		foreach ($indi->getSpouseFamilies() as $family) {
2038
			foreach ($family->getChildren() as $child) {
2039
				$birth = $child->getFirstFact('BIRT');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $birth is correct as $child->getFirstFact('BIRT') (which targets Fisharebest\Webtrees\GedcomRecord::getFirstFact()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
2040
				if ($birth) {
2041
					$birthrec = $birth->getGedcom();
2042
					if (!$birth->getPlace()->isEmpty()) {
2043
						$ctla = preg_match('/\n4 LATI (.+)/', $birthrec, $match1);
2044
						$ctlo = preg_match('/\n4 LONG (.+)/', $birthrec, $match2);
2045
						if ($ctla && $ctlo) {
2046
							$i++;
2047
							$gmarks[$i] = array(
2048
								'date'         => $birth->getDate()->display(true),
2049
								'image'        => $child->displayImage(),
2050
								'info'         => '',
2051
								'lat'          => str_replace(array('N', 'S', ','), array('', '-', '.'), $match1[1]),
2052
								'lng'          => str_replace(array('E', 'W', ','), array('', '-', '.'), $match2[1]),
2053
								'name'         => '<a href="' . $child->getHtmlUrl() . '"' . $child->getFullName() . '</a>',
2054
								'pl_icon'      => '',
2055
								'place'        => $birth->getPlace()->getFullName(),
2056
								'sv_bearing'   => '0',
2057
								'sv_elevation' => '0',
2058
								'sv_lati'      => '0',
2059
								'sv_long'      => '0',
2060
								'sv_zoom'      => '0',
2061
								'tooltip'      => $birth->getPlace()->getGedcomName(),
2062
							);
2063 View Code Duplication
							switch ($child->getSex()) {
2064
							case'F':
0 ignored issues
show
Coding Style introduced by
As per coding-style, case should be followed by a single space.

As per the PSR-2 coding standard, there must be a space after the case keyword, instead of the test immediately following it.

switch (true) {
    case!isset($a):  //wrong
        doSomething();
        break;
    case !isset($b):  //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
2065
								$gmarks[$i]['fact_label'] = I18N::translate('daughter');
2066
								$gmarks[$i]['class']      = 'person_boxF';
2067
								break;
2068
							case 'M':
2069
								$gmarks[$i]['fact_label'] = I18N::translate('son');
2070
								$gmarks[$i]['class']      = 'person_box';
2071
								break;
2072
							default:
2073
								$gmarks[$i]['fact_label'] = I18N::translate('child');
2074
								$gmarks[$i]['class']      = 'person_boxNN';
2075
								break;
2076
							}
2077
						} else {
2078
							$latlongval = $this->getLatitudeAndLongitudeFromPlaceLocation($birth->getPlace()->getGedcomName());
2079
							if ($latlongval && $latlongval->pl_lati && $latlongval->pl_long) {
2080
								$i++;
2081
								$gmarks[$i] = array(
2082
									'date'         => $birth->getDate()->display(true),
2083
									'image'        => $child->displayImage(),
2084
									'info'         => '',
2085
									'lat'          => str_replace(array('N', 'S', ','), array('', '-', '.'), $latlongval->pl_lati),
2086
									'lng'          => str_replace(array('E', 'W', ','), array('', '-', '.'), $latlongval->pl_long),
2087
									'name'         => '<a href="' . $child->getHtmlUrl() . '"' . $child->getFullName() . '</a>',
2088
									'pl_icon'      => $latlongval->pl_icon,
2089
									'place'        => $birth->getPlace()->getFullName(),
2090
									'sv_bearing'   => $latlongval->sv_bearing,
2091
									'sv_elevation' => $latlongval->sv_elevation,
2092
									'sv_lati'      => $latlongval->sv_lati,
2093
									'sv_long'      => $latlongval->sv_long,
2094
									'sv_zoom'      => $latlongval->sv_zoom,
2095
									'tooltip'      => $birth->getPlace()->getGedcomName(),
2096
								);
2097 View Code Duplication
								switch ($child->getSex()) {
2098
								case 'M':
2099
									$gmarks[$i]['fact_label'] = I18N::translate('son');
2100
									$gmarks[$i]['class']      = 'person_box';
2101
									break;
2102
								case 'F':
2103
									$gmarks[$i]['fact_label'] = I18N::translate('daughter');
2104
									$gmarks[$i]['class']      = 'person_boxF';
2105
									break;
2106
								default:
2107
									$gmarks[$i]['fact_label'] = I18N::translate('child');
2108
									$gmarks[$i]['class']      = 'option_boxNN';
2109
									break;
2110
								}
2111
								if ($GM_MAX_ZOOM > $latlongval->pl_zoom) {
2112
									$GM_MAX_ZOOM = $latlongval->pl_zoom;
2113
								}
2114
							}
2115
						}
2116
					}
2117
				}
2118
			}
2119
		}
2120
2121
		// *** ENABLE STREETVIEW ***
2122
		$STREETVIEW = $this->getSetting('GM_USE_STREETVIEW');
2123
		?>
2124
2125
		<script>
2126
			// this variable will collect the html which will eventually be placed in the side_bar
2127
			var side_bar_html = '';
2128
			var map_center = new google.maps.LatLng(0,0);
2129
			var gmarkers = [];
2130
			var gicons = [];
2131
			var map = null;
2132
			var head = '';
2133
			var dir = '';
2134
			var svzoom = '';
2135
2136
			var infowindow = new google.maps.InfoWindow({});
2137
2138
			gicons["red"] = new google.maps.MarkerImage("https://maps.google.com/mapfiles/marker.png",
2139
				new google.maps.Size(20, 34),
2140
				new google.maps.Point(0,0),
2141
				new google.maps.Point(9, 34)
2142
			);
2143
2144
			var iconImage = new google.maps.MarkerImage("https://maps.google.com/mapfiles/marker.png",
2145
				new google.maps.Size(20, 34),
2146
				new google.maps.Point(0,0),
2147
				new google.maps.Point(9, 34)
2148
			);
2149
2150
			var iconShadow = new google.maps.MarkerImage("https://www.google.com/mapfiles/shadow50.png",
2151
				new google.maps.Size(37, 34),
2152
				new google.maps.Point(0,0),
2153
				new google.maps.Point(9, 34)
2154
			);
2155
2156
			var iconShape = {
2157
				coord: [9,0,6,1,4,2,2,4,0,8,0,12,1,14,2,16,5,19,7,23,8,26,9,30,9,34,11,34,11,30,12,26,13,24,14,21,16,18,18,16,20,12,20,8,18,4,16,2,15,1,13,0],
2158
				type: "poly"
2159
			};
2160
2161
			function getMarkerImage(iconColor) {
2162
				if (typeof(iconColor) === 'undefined' || iconColor === null) {
2163
					iconColor = 'red';
2164
				}
2165
				if (!gicons[iconColor]) {
2166
					gicons[iconColor] = new google.maps.MarkerImage('//maps.google.com/mapfiles/marker'+ iconColor +'.png',
2167
					new google.maps.Size(20, 34),
2168
					new google.maps.Point(0,0),
2169
					new google.maps.Point(9, 34));
2170
				}
2171
				return gicons[iconColor];
2172
			}
2173
2174
			var sv2_bear = null;
2175
			var sv2_elev = null;
2176
			var sv2_zoom = null;
2177
			var placer   = null;
2178
2179
			// A function to create the marker and set up the event window
2180
			function createMarker(latlng, html, tooltip, sv_lati, sv_long, sv_bearing, sv_elevation, sv_zoom, sv_point, marker_icon) {
2181
				var contentString = '<div id="iwcontent">'+html+'</div>';
2182
2183
				// Use flag icon (if defined) instead of regular marker icon
2184
				if (marker_icon) {
2185
					var icon_image = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+'googlemap/'+marker_icon,
2186
						new google.maps.Size(25, 15),
2187
						new google.maps.Point(0,0),
2188
						new google.maps.Point(12, 15));
2189
					var icon_shadow = new google.maps.MarkerImage(WT_STATIC_URL+WT_MODULES_DIR+'googlemap/images/flag_shadow.png',
2190
						new google.maps.Size(35, 45), // Shadow size
2191
						new google.maps.Point(0,0),   // Shadow origin
2192
						new google.maps.Point(1, 45)  // Shadow anchor is base of flagpole
2193
					);
2194
				} else {
2195
					var icon_image = getMarkerImage('red');
2196
					var icon_shadow = iconShadow;
2197
				}
2198
2199
				// Decide if marker point is Regular (latlng) or StreetView (sv_point) derived
2200
				if (sv_point == '(0, 0)' || sv_point == '(null, null)') {
2201
					placer = latlng;
2202
				} else {
2203
					placer = sv_point;
2204
				}
2205
2206
				// Define the marker
2207
				var marker = new google.maps.Marker({
2208
					position: placer,
2209
					icon:     icon_image,
2210
					shadow:   icon_shadow,
2211
					map:      map,
2212
					title:    tooltip,
2213
					zIndex:   Math.round(latlng.lat()*-100000)<<5
2214
				});
2215
2216
				// Store the tab and event info as marker properties
2217
				marker.sv_lati  = sv_lati;
2218
				marker.sv_long  = sv_long;
2219
				marker.sv_point = sv_point;
2220
2221
				if (sv_bearing == '') {
2222
					marker.sv_bearing = 0;
2223
				} else {
2224
					marker.sv_bearing = sv_bearing;
2225
				}
2226
				if (sv_elevation == '') {
2227
					marker.sv_elevation = 5;
2228
				} else {
2229
					marker.sv_elevation = sv_elevation;
2230
				}
2231
				if (sv_zoom == '' || sv_zoom == 0 || sv_zoom == 1) {
2232
					marker.sv_zoom = 1.2;
2233
				} else {
2234
					marker.sv_zoom = sv_zoom;
2235
				}
2236
2237
				marker.sv_latlng = new google.maps.LatLng(sv_lati, sv_long);
2238
				gmarkers.push(marker);
2239
2240
				// Open infowindow when marker is clicked
2241
				google.maps.event.addListener(marker, 'click', function() {
2242
					infowindow.close();
2243
					infowindow.setContent(contentString);
2244
					infowindow.open(map, marker);
2245
					var panoramaOptions = {
2246
						position:          marker.position,
2247
						mode:              'html5',
2248
						navigationControl: false,
2249
						linksControl:      false,
2250
						addressControl:    false,
2251
						pov: {
2252
							heading: sv_bearing,
2253
							pitch:   sv_elevation,
2254
							zoom:    sv_zoom
2255
						}
2256
					};
2257
2258
					// Use jquery for info window tabs
2259
					google.maps.event.addListener(infowindow, 'domready', function() {
2260
						//jQuery code here
2261
						jQuery('#EV').click(function() {
2262
							document.tabLayerEV = document.getElementById("EV");
2263
							document.tabLayerEV.style.background = '#ffffff';
2264
							document.tabLayerEV.style.paddingBottom = '1px';
2265
							<?php if ($STREETVIEW) { ?>
0 ignored issues
show
Bug Best Practice introduced by
The expression $STREETVIEW of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2266
							document.tabLayerSV = document.getElementById("SV");
2267
							document.tabLayerSV.style.background = '#cccccc';
2268
							document.tabLayerSV.style.paddingBottom = '0px';
2269
							<?php } ?>
2270
							document.panelLayer1 = document.getElementById("pane1");
2271
							document.panelLayer1.style.display = 'block';
2272
							<?php if ($STREETVIEW) { ?>
0 ignored issues
show
Bug Best Practice introduced by
The expression $STREETVIEW of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2273
							document.panelLayer2 = document.getElementById("pane2");
2274
							document.panelLayer2.style.display = 'none';
2275
							<?php } ?>
2276
						});
2277
2278
						jQuery('#SV').click(function() {
2279
							document.tabLayerEV = document.getElementById("EV");
2280
							document.tabLayerEV.style.background = '#cccccc';
2281
							document.tabLayerEV.style.paddingBottom = '0px';
2282
							<?php if ($STREETVIEW) { ?>
0 ignored issues
show
Bug Best Practice introduced by
The expression $STREETVIEW of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2283
							document.tabLayerSV = document.getElementById("SV");
2284
							document.tabLayerSV.style.background = '#ffffff';
2285
							document.tabLayerSV.style.paddingBottom = '1px';
2286
							<?php } ?>
2287
							document.panelLayer1 = document.getElementById("pane1");
2288
							document.panelLayer1.style.display = 'none';
2289
							<?php if ($STREETVIEW) { ?>
0 ignored issues
show
Bug Best Practice introduced by
The expression $STREETVIEW of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2290
							document.panelLayer2 = document.getElementById("pane2");
2291
							document.panelLayer2.style.display = 'block';
2292
							<?php } ?>
2293
							var panorama = new google.maps.StreetViewPanorama(document.getElementById("pano"), panoramaOptions);
2294
							setTimeout(function() { panorama.setVisible(true); }, 100);
2295
							setTimeout(function() { panorama.setVisible(true); }, 500);
2296
						});
2297
					});
2298
				});
2299
			}
2300
2301
			// Opens Marker infowindow when corresponding Sidebar item is clicked
2302
			function myclick(i) {
2303
				infowindow.close();
2304
				google.maps.event.trigger(gmarkers[i], 'click');
2305
				return false;
2306
			}
2307
2308
			// Home control
2309
			// returns the user to the original map position ... loadMap() function
2310
			// This constructor takes the control DIV as an argument.
2311
			function HomeControl(controlDiv, map) {
2312
				// Set CSS styles for the DIV containing the control
2313
				// Setting padding to 5 px will offset the control from the edge of the map
2314
				controlDiv.style.paddingTop = '5px';
2315
				controlDiv.style.paddingRight = '0px';
2316
2317
				// Set CSS for the control border
2318
				var controlUI = document.createElement('DIV');
2319
				controlUI.style.backgroundColor = 'white';
2320
				controlUI.style.borderStyle = 'solid';
2321
				controlUI.style.borderWidth = '2px';
2322
				controlUI.style.cursor = 'pointer';
2323
				controlUI.style.textAlign = 'center';
2324
				controlUI.title = '';
2325
				controlDiv.appendChild(controlUI);
2326
2327
				// Set CSS for the control interior
2328
				var controlText = document.createElement('DIV');
2329
				controlText.style.fontFamily = 'Arial,sans-serif';
2330
				controlText.style.fontSize = '12px';
2331
				controlText.style.paddingLeft = '15px';
2332
				controlText.style.paddingRight = '15px';
2333
				controlText.innerHTML = '<b><?php echo I18N::translate('Redraw map') ?></b>';
2334
				controlUI.appendChild(controlText);
2335
2336
				// Setup the click event listeners: simply set the map to original LatLng
2337
				google.maps.event.addDomListener(controlUI, 'click', function() {
2338
					loadMap();
2339
				});
2340
			}
2341
2342
			function loadMap() {
2343
				// Create the map and mapOptions
2344
				var mapOptions = {
2345
					zoom: 7,
2346
					center: map_center,
2347
					mapTypeId: google.maps.MapTypeId.<?php echo $this->getSetting('GM_MAP_TYPE') ?>,
2348
					mapTypeControlOptions: {
2349
						style: google.maps.MapTypeControlStyle.DROPDOWN_MENU  // DEFAULT, DROPDOWN_MENU, HORIZONTAL_BAR
2350
					},
2351
					navigationControl: true,
2352
					navigationControlOptions: {
2353
					position: google.maps.ControlPosition.TOP_RIGHT,  // BOTTOM, BOTTOM_LEFT, LEFT, TOP, etc
2354
					style: google.maps.NavigationControlStyle.SMALL  // ANDROID, DEFAULT, SMALL, ZOOM_PAN
2355
					},
2356
					streetViewControl: false,  // Show Pegman or not
2357
					scrollwheel: false
2358
				};
2359
				map = new google.maps.Map(document.getElementById('map_pane'), mapOptions);
2360
2361
				// Close any infowindow when map is clicked
2362
				google.maps.event.addListener(map, 'click', function() {
2363
					infowindow.close();
2364
				});
2365
2366
				// Create the Home DIV and call the HomeControl() constructor in this DIV.
2367
				var homeControlDiv = document.createElement('DIV');
2368
				var homeControl = new HomeControl(homeControlDiv, map);
2369
				homeControlDiv.index = 1;
2370
				map.controls[google.maps.ControlPosition.TOP_RIGHT].push(homeControlDiv);
2371
2372
				// Add the markers to the map from the $gmarks array
2373
				var locations = [
2374
					<?php foreach ($gmarks as $n => $gmark) { ?>
2375
					<?php echo $n ? ',' : '' ?>
2376
					{
2377
						"event":        "<?php echo Filter::escapeJs($gmark['fact_label']) ?>",
2378
						"lat":          "<?php echo Filter::escapeJs($gmark['lat']) ?>",
2379
						"lng":          "<?php echo Filter::escapeJs($gmark['lng']) ?>",
2380
						"date":         "<?php echo Filter::escapeJs($gmark['date']) ?>",
2381
						"info":         "<?php echo Filter::escapeJs($gmark['info']) ?>",
2382
						"name":         "<?php echo Filter::escapeJs($gmark['name']) ?>",
2383
						"place":        "<?php echo Filter::escapeJs($gmark['place']) ?>",
2384
						"tooltip":      "<?php echo Filter::escapeJs($gmark['tooltip']) ?>",
2385
						"image":        "<?php echo Filter::escapeJs($gmark['image']) ?>",
2386
						"pl_icon":      "<?php echo Filter::escapeJs($gmark['pl_icon']) ?>",
2387
						"sv_lati":      "<?php echo Filter::escapeJs($gmark['sv_lati']) ?>",
2388
						"sv_long":      "<?php echo Filter::escapeJs($gmark['sv_long']) ?>",
2389
						"sv_bearing":   "<?php echo Filter::escapeJs($gmark['sv_bearing']) ?>",
2390
						"sv_elevation": "<?php echo Filter::escapeJs($gmark['sv_elevation']) ?>",
2391
						"sv_zoom":      "<?php echo Filter::escapeJs($gmark['sv_zoom']) ?>"
2392
					}
2393
					<?php } ?>
2394
				];
2395
2396
				// Group the markers by location
2397
				var location_groups = [];
2398
				for (var key in locations) {
2399
					if (!location_groups.hasOwnProperty(locations[key].place)) {
2400
						location_groups[locations[key].place] = [];
2401
					}
2402
					location_groups[locations[key].place].push(locations[key]);
2403
				}
2404
2405
				// Set the Marker bounds
2406
				var bounds = new google.maps.LatLngBounds ();
2407
2408
				var key;
2409
				// Iterate over each location
2410
				for (key in location_groups) {
2411
					var locations = location_groups[key];
2412
					// Iterate over each marker at this location
2413
					var event_details = '';
2414
					for (var j in locations) {
2415
						var location = locations[j];
2416
						if (location.info && location.name) {
2417
							event_details += '<table><tr><td class="highlt_img">' + location.image + '</td><td><p><span id="sp1">' + location.event + '</span> ' + location.info + '<br><b>' + location.name + '</b><br>' + location.date + '<br></p></td></tr></table>';
2418
						} else if (location.name) {
2419
							event_details += '<table><tr><td class="highlt_img">' + location.image + '</td><td><p><span id="sp1">' + location.event + '</span><br><b>' + location.name + '</b><br>' + location.date + '<br></p></td></tr></table>';
2420
						} else if (location.info) {
2421
							event_details += '<table><tr><td class="highlt_img">' + location.image + '</td><td><p><span id="sp1">' + location.event + '</span> ' + location.info + '<br>' + location.date + '<br></p></td></tr></table>';
2422
						} else {
2423
							event_details += '<table><tr><td class="highlt_img">' + location.image + '</td><td><p><span id="sp1">' + location.event + '</span><br>' + location.date + '<br></p></td></tr></table>';
2424
						}
2425
					}
2426
					// All locations are the same in each group, so create a marker with the first
2427
					var location = location_groups[key][0];
2428
					var html =
2429
					'<div class="infowindow">' +
2430
						'<div id="gmtabs">' +
2431
							'<ul class="tabs" >' +
2432
								'<li><a href="#event" id="EV"><?php echo I18N::translate('Events') ?></a></li>' +
2433
								<?php if ($STREETVIEW) { ?>
0 ignored issues
show
Bug Best Practice introduced by
The expression $STREETVIEW of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2434
								'<li><a href="#sview" id="SV"><?php echo I18N::translate('Google Street View™') ?></a></li>' +
2435
								<?php } ?>
2436
							'</ul>' +
2437
							'<div class="panes">' +
2438
								'<div id="pane1">' +
2439
									'<h4 id="iwhead">' + location.place + '</h4>' +
2440
									event_details +
2441
								'</div>' +
2442
								<?php if ($STREETVIEW) { ?>
0 ignored issues
show
Bug Best Practice introduced by
The expression $STREETVIEW of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2443
								'<div id="pane2">' +
2444
									'<h4 id="iwhead">' + location.place + '</h4>' +
2445
									'<div id="pano"></div>' +
2446
								'</div>' +
2447
								<?php } ?>
2448
							'</div>' +
2449
						'</div>' +
2450
					'</div>';
2451
2452
					// create the marker
2453
					var point        = new google.maps.LatLng(location.lat,     location.lng);     // Place Latitude, Longitude
2454
					var sv_point     = new google.maps.LatLng(location.sv_lati, location.sv_long); // StreetView Latitude and Longitide
2455
2456
					var zoomLevel = <?php echo $GM_MAX_ZOOM ?>;
2457
					var marker    = createMarker(point, html, location.tooltip, location.sv_lati, location.sv_long, location.sv_bearing, location.sv_elevation, location.sv_zoom, sv_point, location.pl_icon);
2458
2459
					// if streetview coordinates are available, use them for marker,
2460
					// else use the place coordinates
2461
					if (sv_point && sv_point != "(0, 0)") {
2462
						var myLatLng = sv_point;
2463
					} else {
2464
						var myLatLng = point;
2465
					}
2466
2467
					// Correct zoom level when only one marker is present
2468
					if (location_groups.length == 1) {
2469
						bounds.extend(myLatLng);
2470
						map.setZoom(zoomLevel);
2471
						map.setCenter(myLatLng);
2472
					} else {
2473
						bounds.extend(myLatLng);
2474
						map.fitBounds(bounds);
2475
						// Correct zoom level when multiple markers have the same coordinates
2476
						var listener1 = google.maps.event.addListenerOnce(map, "idle", function() {
2477
							if (map.getZoom() > zoomLevel) {
2478
								map.setZoom(zoomLevel);
2479
							}
2480
							google.maps.event.removeListener(listener1);
2481
						});
2482
					}
2483
				} // end loop through location markers
2484
			} // end loadMap()
2485
2486
		</script>
2487
		<?php
2488
		// Create the normal googlemap sidebar of events and children
2489
		echo '<div style="overflow: auto; overflow-x: hidden; overflow-y: auto; height:', $this->getSetting('GM_YSIZE'), 'px;"><table class="facts_table">';
2490
2491
		foreach ($gmarks as $key => $gmark) {
2492
			echo '<tr>';
2493
			echo '<td class="facts_label">';
2494
			echo '<a href="#" onclick="return myclick(\'', Filter::escapeHtml($key), '\')">', $gmark['fact_label'], '</a></td>';
2495
			echo '<td class="', $gmark['class'], '" style="white-space: normal">';
2496
			if ($gmark['info']) {
2497
				echo '<span class="field">', Filter::escapeHtml($gmark['info']), '</span><br>';
2498
			}
2499
			if ($gmark['name']) {
2500
				echo $gmark['name'], '<br>';
2501
			}
2502
			echo $gmark['place'], '<br>';
2503
			if ($gmark['date']) {
2504
				echo $gmark['date'], '<br>';
2505
			}
2506
			echo '</td>';
2507
			echo '</tr>';
2508
		}
2509
		echo '</table></div><br>';
2510
	}
2511
2512
	/**
2513
	 * Get the Location ID.
2514
	 *
2515
	 * @param string $place
2516
	 *
2517
	 * @return int
2518
	 */
2519
	private function getPlaceLocationId($place) {
2520
		$par      = explode(',', strip_tags($place));
2521
		$par      = array_reverse($par);
2522
		$place_id = 0;
2523
		$pl_id    = 0;
2524
2525
		for ($i = 0; $i < count($par); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
2526
			$par[$i] = trim($par[$i]);
2527
			if (empty($par[$i])) {
2528
				$par[$i] = 'unknown';
2529
			}
2530
			$placelist = $this->createPossiblePlaceNames($par[$i], $i + 1);
2531 View Code Duplication
			foreach ($placelist as $key => $placename) {
2532
				$pl_id = (int) Database::prepare(
2533
					"SELECT pl_id FROM `##placelocation` WHERE pl_level = :level AND pl_parent_id = :parent_id AND pl_place LIKE :placename"
2534
				)->execute(array(
2535
					'level'     => $i,
2536
					'parent_id' => $place_id,
2537
					'placename' => $placename,
2538
				))->fetchOne();
2539
				if ($pl_id) {
2540
					break;
2541
				}
2542
			}
2543
			if (!$pl_id) {
2544
				break;
2545
			}
2546
			$place_id = $pl_id;
2547
		}
2548
2549
		return $place_id;
2550
	}
2551
2552
	/**
2553
	 * Get the place ID.
2554
	 *
2555
	 * @param string $place
2556
	 *
2557
	 * @return int
2558
	 */
2559
	private function getPlaceId($place) {
2560
		global $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2561
2562
		$par      = explode(',', $place);
2563
		$par      = array_reverse($par);
2564
		$place_id = 0;
2565
		$pl_id    = 0;
2566
2567
		for ($i = 0; $i < count($par); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
2568
			$par[$i]   = trim($par[$i]);
2569
			$placelist = $this->createPossiblePlaceNames($par[$i], $i + 1);
2570 View Code Duplication
			foreach ($placelist as $placename) {
2571
				$pl_id = (int) Database::prepare(
2572
					"SELECT p_id FROM `##places` WHERE p_parent_id = :place_id AND p_file = :tree_id AND p_place = :placename"
2573
				)->execute(array(
2574
					'place_id'  => $place_id,
2575
					'tree_id'   => $WT_TREE->getTreeId(),
2576
					'placename' => $placename,
2577
				))->fetchOne();
2578
				if ($pl_id) {
2579
					break;
2580
				}
2581
			}
2582
			if (!$pl_id) {
2583
				break;
2584
			}
2585
			$place_id = $pl_id;
2586
		}
2587
2588
		return $place_id;
2589
	}
2590
2591
	/**
2592
	 * Set the place IDs.
2593
	 *
2594
	 * @param int      $level
2595
	 * @param string[] $parent
2596
	 *
2597
	 * @return int
2598
	 */
2599
	private function setPlaceIdMap($level, $parent) {
2600
		$fullplace = '';
2601
		if ($level == 0) {
2602
			return 0;
2603
		} else {
2604
			for ($i = 1; $i <= $level; $i++) {
2605
				$fullplace .= $parent[$level - $i] . ', ';
2606
			}
2607
			$fullplace = substr($fullplace, 0, -2);
2608
2609
			return $this->getPlaceId($fullplace);
2610
		}
2611
	}
2612
2613
	/**
2614
	 * Set the map level.
2615
	 *
2616
	 * @param int      $level
2617
	 * @param string[] $parent
2618
	 *
2619
	 * @return int
2620
	 */
2621
	private function setLevelMap($level, $parent) {
2622
		$fullplace = '';
2623
		if ($level == 0) {
2624
			return 0;
2625
		} else {
2626
			for ($i = 1; $i <= $level; $i++) {
2627
				if ($parent[$level - $i] != '') {
2628
					$fullplace .= $parent[$level - $i] . ', ';
2629
				} else {
2630
					$fullplace .= 'Unknown, ';
2631
				}
2632
			}
2633
			$fullplace = substr($fullplace, 0, -2);
2634
2635
			return $this->getPlaceLocationId($fullplace);
2636
		}
2637
	}
2638
2639
	/**
2640
	 * Called by placelist.php
2641
	 *
2642
	 * @param string $placelevels
2643
	 */
2644
	public function createMap($placelevels) {
2645
		global $level, $levelm, $plzoom, $controller, $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2646
2647
		Database::updateSchema(self::SCHEMA_MIGRATION_PREFIX, self::SCHEMA_SETTING_NAME, self::SCHEMA_TARGET_VERSION);
2648
2649
		$STREETVIEW = $this->getSetting('GM_USE_STREETVIEW');
2650
		$parent     = Filter::getArray('parent');
2651
2652
		// create the map
2653
		echo '<table style="margin:20px auto 0 auto;"><tr><td>';
2654
		//<!-- start of map display -->
2655
		echo '<table><tr>';
2656
		echo '<td class="center" width="200px">';
2657
2658
		$levelm = $this->setLevelMap($level, $parent);
2659
		$latlng =
2660
			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=?")
2661
			->execute(array($levelm))
2662
			->fetch(PDO::FETCH_ASSOC);
2663
		if ($STREETVIEW && $level != 0) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $STREETVIEW of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2664
			echo '<div id="place_map" style="margin-top:20px; border:1px solid gray; width: ', $this->getSetting('GM_PH_XSIZE'), 'px; height: ', $this->getSetting('GM_PH_YSIZE'), 'px; ';
2665
		} else {
2666
			echo '<div id="place_map" style="border:1px solid gray; width:', $this->getSetting('GM_PH_XSIZE'), 'px; height:', $this->getSetting('GM_PH_YSIZE'), 'px; ';
2667
		}
2668
		echo "\"><i class=\"icon-loading-large\"></i></div>";
2669
		echo '</td>';
2670
		echo '<script src="', $this->googleMapsScript(), '"></script>';
2671
2672
		$plzoom = $latlng['pl_zoom']; // Map zoom level
2673
2674
		if (Auth::isAdmin()) {
2675
			$placecheck_url = 'module.php?mod=googlemap&amp;mod_action=admin_placecheck';
2676
			if ($parent && isset($parent[0])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parent of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2677
				$placecheck_url .= '&amp;country=' . $parent[0];
2678
				if (isset($parent[1])) {
2679
					$placecheck_url .= '&amp;state=' . $parent[1];
2680
				}
2681
			}
2682
			$adminplaces_url = 'module.php?mod=googlemap&amp;mod_action=admin_places';
2683
			if ($latlng && isset($latlng['pl_id'])) {
2684
				$adminplaces_url .= '&amp;parent=' . $latlng['pl_id'];
2685
			}
2686
			echo '</tr><tr><td>';
2687
			echo '<a href="module.php?mod=googlemap&amp;mod_action=admin_config">', I18N::translate('Google Maps™ preferences'), '</a>';
2688
			echo '&nbsp;|&nbsp;';
2689
			echo '<a href="' . $adminplaces_url . '">', I18N::translate('Geographic data'), '</a>';
2690
			echo '&nbsp;|&nbsp;';
2691
			echo '<a href="' . $placecheck_url . '">', I18N::translate('Place check'), '</a>';
2692
			if (Module::getModuleByName('batch_update')) {
2693
				$placelevels = preg_replace('/, ' . I18N::translate('unknown') . '/', ', ', $placelevels); // replace ", unknown" with ", "
2694
				$placelevels = substr($placelevels, 2); // remove the leading ", "
2695
				if ($placelevels) {
2696
					$batchupdate_url = 'module.php?mod=batch_update&amp;mod_action=admin_batch_update&amp;plugin=BatchUpdateSearchReplacePlugin&amp;method=exact&amp;ged=' . $WT_TREE->getNameHtml() . '&amp;search=' . urlencode($placelevels); // exact match
2697
					echo '&nbsp;|&nbsp;';
2698
					echo '<a href="' . $batchupdate_url . '">', I18N::translate('Batch update'), '</a>';
2699
				}
2700
			}
2701
		}
2702
		echo '</td></tr></table>';
2703
		echo '</td>';
2704
		echo '<td style="margin-left:15px; float:right;">';
2705
2706
		if ($STREETVIEW) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $STREETVIEW of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2707
			$controller->addInlineJavascript('
2708
				function update_sv_params(placeid) {
2709
					var svlati = document.getElementById("sv_latiText").value.slice(0, -1);
2710
					var svlong = document.getElementById("sv_longText").value.slice(0, -1);
2711
					var svbear = document.getElementById("sv_bearText").value.slice(0, -1);
2712
					var svelev = document.getElementById("sv_elevText").value.slice(0, -1);
2713
					var svzoom = document.getElementById("sv_zoomText").value;
2714
					win03 = window.open("module.php?mod=googlemap&mod_action=places_edit&action=update_sv_params&placeid="+placeid+"&svlati="+svlati+"&svlong="+svlong+"&svbear="+svbear+"&svelev="+svelev+"&svzoom="+svzoom, "win03", indx_window_specs);
2715
					if (window.focus) {win03.focus();}
2716
				}
2717
			');
2718
2719
			global $pl_lati, $pl_long;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2720
			if ($level >= 1) {
2721
				$pl_lati = str_replace(array('N', 'S', ','), array('', '-', '.'), $latlng['pl_lati']); // WT_placelocation lati
2722
				$pl_long = str_replace(array('E', 'W', ','), array('', '-', '.'), $latlng['pl_long']); // WT_placelocation long
2723
2724
				// Check if Streetview location parameters are stored in database
2725
				$placeid  = $latlng['pl_id']; // Placelocation place id
2726
				$sv_lat   = $latlng['sv_lati']; // StreetView Point of View Latitude
2727
				$sv_lng   = $latlng['sv_long']; // StreetView Point of View Longitude
2728
				$sv_dir   = $latlng['sv_bearing']; // StreetView Point of View Direction (degrees from North)
2729
				$sv_pitch = $latlng['sv_elevation']; // StreetView Point of View Elevation (+90 to -90 degrees (+=down, -=up)
2730
				$sv_zoom  = $latlng['sv_zoom']; // StreetView Point of View Zoom (0, 1, 2 or 3)
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2731
2732
				// 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
2733
				if ($latlng['sv_lati'] == 0 && $latlng['sv_long'] == 0) {
2734
						$sv_lat = $pl_lati;
2735
						$sv_lng = $pl_long;
2736
				}
2737
2738
				?>
2739
				<div>
2740
				<iframe style="background: transparent; margin-top: -3px; margin-left: 2px; width: 530px; height: 405px; padding: 0; border: 0;" src="module.php?mod=googlemap&amp;mod_action=wt_street_view&amp;x=<?php echo $sv_lng ?>&amp;y=<?php echo $sv_lat ?>&amp;z=18&amp;t=2&amp;c=1&amp;s=1&amp;b=<?php echo $sv_dir ?>&amp;p=<?php echo $sv_pitch ?>&amp;m=<?php echo $sv_zoom ?>&amp;j=1&amp;k=1&amp;v=1" marginwidth="0" marginheight="0" frameborder="0" scrolling="no"></iframe>
2741
				</div>
2742
2743
				<?php
2744
				$list_latlon = (
2745
					GedcomTag::getLabel('LATI') . "<input name='sv_latiText' id='sv_latiText' type='text' style='width:42px; background:none; border:none;' value='" . $sv_lat . "'>" .
2746
					GedcomTag::getLabel('LONG') . "<input name='sv_longText' id='sv_longText' type='text' style='width:42px; background:none; border:none;' value='" . $sv_lng . "'>" .
2747
					/* I18N: Compass bearing (in degrees), for street-view mapping */ I18N::translate('Bearing') . "<input name='sv_bearText' id='sv_bearText' type='text' style='width:46px; background:none; border:none;' value='" . $sv_dir . "'>" .
2748
					/* I18N: Angle of elevation (in degrees), for street-view mapping */ I18N::translate('Elevation') . "<input name='sv_elevText' id='sv_elevText' type='text' style='width:30px; background:none; border:none;' value='" . $sv_pitch . "'>" .
2749
					I18N::translate('Zoom') . "<input name='sv_zoomText' id='sv_zoomText' type='text' style='width:30px; background:none; border:none;' value='" . $sv_zoom . "'>
2750
				");
2751
				if (Auth::isAdmin()) {
2752
					echo '<table style="margin-left:6px; border:solid 1px black; width:522px; margin-top:-28px; background:#cccccc;">';
2753
				} else {
2754
					echo '<table style="display:none;">';
2755
				}
2756
				echo '<tr><td>';
2757
				echo '<form style="text-align:left; margin-left:5px; font:11px verdana; color:blue;" method="post" action="">';
2758
				echo $list_latlon;
2759
				echo '<input type="submit" name="Submit" onclick="update_sv_params(' . $placeid . ');" value="', I18N::translate('save'), '">';
2760
				echo '</form>';
2761
				echo '</td></tr>';
2762
				echo '</table>';
2763
			}
2764
			echo '</td></tr><tr>';
2765
		}
2766
	}
2767
2768
	/**
2769
	 * Find the current location.
2770
	 *
2771
	 * @param int $numls
2772
	 * @param int $levelm
2773
	 *
2774
	 * @return int[]
2775
	 */
2776
	private function checkWhereAmI($numls, $levelm) {
2777
		$where_am_i = $this->placeIdToHierarchy($levelm);
2778
		$i          = $numls + 1;
2779
		$levelo     = array(0 => 0);
2780
		foreach (array_reverse($where_am_i, true) as $id => $place2) {
2781
			$levelo[$i] = $id;
2782
			$i--;
2783
		}
2784
2785
		return $levelo;
2786
	}
2787
2788
	/**
2789
	 * Print the numbers of individuals.
2790
	 *
2791
	 * @param int      $level
2792
	 * @param string[] $parent
2793
	 */
2794
	private function printHowManyPeople($level, $parent) {
2795
		global $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2796
2797
		$stats = new Stats($WT_TREE);
2798
2799
		$place_count_indi = 0;
2800
		$place_count_fam  = 0;
2801
		if (!isset($parent[$level - 1])) {
2802
			$parent[$level - 1] = '';
2803
		}
2804
		$p_id = $this->setPlaceIdMap($level, $parent);
2805
		$indi = $stats->statsPlaces('INDI', false, $p_id);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2806
		$fam  = $stats->statsPlaces('FAM', false, $p_id);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2807
		foreach ($indi as $place) {
2808
			$place_count_indi = $place['tot'];
2809
		}
2810
		foreach ($fam as $place) {
2811
			$place_count_fam = $place['tot'];
2812
		}
2813
		echo '<br><br>', I18N::translate('Individuals'), ': ', $place_count_indi, ', ', I18N::translate('Families'), ': ', $place_count_fam;
2814
	}
2815
2816
	/**
2817
	 * Print the flags and markers.
2818
	 *
2819
	 * @param string[] $place2
2820
	 * @param int      $level
2821
	 * @param string[] $parent
2822
	 * @param int      $levelm
2823
	 * @param string   $linklevels
2824
	 */
2825
	private function printGoogleMapMarkers($place2, $level, $parent, $levelm, $linklevels) {
2826
		if (!$place2['lati'] || !$place2['long']) {
2827
			echo 'var icon_type = new google.maps.MarkerImage();';
2828
			echo 'icon_type.image = "', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/marker_yellow.png";';
2829
			echo 'icon_type.shadow = "', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/shadow50.png";';
2830
			echo 'icon_type.iconSize = google.maps.Size(20, 34);';
2831
			echo 'icon_type.shadowSize = google.maps.Size(37, 34);';
2832
			echo 'var point = new google.maps.LatLng(0, 0);';
2833
			echo "var marker = createMarker(point, \"<div class='iwstyle' style='width: 250px;'><a href='?action=find", $linklevels, "&amp;parent[{$level}]=";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $level instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
2834
			if ($place2['place'] == "Unknown") {
2835
				echo "'><br>";
2836
			} else {
2837
				echo addslashes($place2['place']), "'><br>";
2838
			}
2839 View Code Duplication
			if (($place2['icon'] !== null) && ($place2['icon'] !== '')) {
2840
				echo '<img src=\"', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/', $place2['icon'], '\">&nbsp;&nbsp;';
2841
			}
2842
			if ($place2['place'] == 'Unknown') {
2843
					echo I18N::translate('unknown');
2844
			} else {
2845
				echo addslashes($place2['place']);
2846
			}
2847
			echo '</a>';
2848
			$parent[$level] = $place2['place'];
2849
			$this->printHowManyPeople($level + 1, $parent);
2850
			echo '<br>', I18N::translate('This place has no coordinates');
2851
			if (Auth::isAdmin())
2852
				echo "<br><a href='module.php?mod=googlemap&amp;mod_action=admin_places&amp;parent=", $levelm, "&amp;display=inactive'>", I18N::translate('Geographic data'), "</a>";
2853
			echo "</div>\", icon_type, \"", str_replace(array('&lrm;', '&rlm;'), array(WT_UTF8_LRM, WT_UTF8_RLM), addslashes($place2['place'])), "\");\n";
2854
		} else {
2855
			$lati = str_replace(array('N', 'S', ','), array('', '-', '.'), $place2['lati']);
2856
			$long = str_replace(array('E', 'W', ','), array('', '-', '.'), $place2['long']);
2857
			//delete leading zero
2858 View Code Duplication
			if ($lati >= 0) {
2859
				$lati = abs($lati);
2860
			} elseif ($lati < 0) {
2861
				$lati = '-' . abs($lati);
2862
			}
2863 View Code Duplication
			if ($long >= 0) {
2864
				$long = abs($long);
2865
			} elseif ($long < 0) {
2866
				$long = '-' . abs($long);
2867
			}
2868
2869
			echo 'var icon_type = new google.maps.MarkerImage();';
2870
			if ($place2['icon'] !== null && $place2['icon'] !== '' && $this->getSetting('GM_PH_MARKER') === 'G_FLAG') {
2871
				echo ' icon_type.image = "', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/', $place2['icon'], '";';
2872
				echo ' icon_type.shadow = "', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/flag_shadow.png";';
2873
				echo ' icon_type.iconSize = new google.maps.Size(25, 15);';
2874
				echo ' icon_type.shadowSize = new google.maps.Size(35, 45);';
2875
			}
2876
			echo 'var point = new google.maps.LatLng(', $lati, ', ', $long, ');';
2877
			echo 'var marker = createMarker(point, "<div class=\"iwstyle\" style=\"width: 250px;\"><a href=\"?action=find', $linklevels;
2878
			echo '&amp;parent[', $level, ']=';
2879
			if ($place2['place'] !== 'Unknown') {
2880
				echo Filter::escapeJs($place2['place']);
2881
			}
2882
			echo '\"><br>';
2883 View Code Duplication
			if ($place2['icon'] !== null && $place2['icon'] !== '') {
2884
				echo '<img src=\"', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/', $place2['icon'], '\">&nbsp;&nbsp;';
2885
			}
2886 View Code Duplication
				if ($place2['place'] === 'Unknown') {
2887
					echo I18N::translate('unknown');
2888
				} else {
2889
					echo Filter::escapeJs($place2['place']);
2890
				}
2891
			echo '</a>';
2892
			$parent[$level] = $place2['place'];
2893
			$this->printHowManyPeople($level + 1, $parent);
2894
			echo '<br><br>';
2895
			if ($this->getSetting('GM_COORD')) {
2896
				echo '', $place2['lati'], ', ', $place2['long'];
2897
			}
2898
			echo '</div>", icon_type, "', Filter::escapeJs($place2['place']), '");';
2899
		}
2900
	}
2901
2902
	/**
2903
	 * Called by placelist.php
2904
	 *
2905
	 * @param int      $numfound
2906
	 * @param int      $level
2907
	 * @param string[] $parent
2908
	 * @param string   $linklevels
2909
	 * @param string[] $place_names
2910
	 */
2911
	public function mapScripts($numfound, $level, $parent, $linklevels, $place_names) {
2912
		global $plzoom, $controller;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2913
2914
		$controller->addInlineJavascript('
2915
			jQuery("head").append(\'<link rel="stylesheet" type="text/css" href="' . WT_STATIC_URL . WT_MODULES_DIR . 'googlemap/css/wt_v3_googlemap.css" />\');
2916
			var numMarkers = "' . $numfound . '";
2917
			var mapLevel   = "' . $level . '";
2918
			var placezoom  = "' . $plzoom . '";
2919
			var infowindow = new google.maps.InfoWindow({
2920
				// size: new google.maps.Size(150,50),
2921
				// maxWidth: 600
2922
			});
2923
2924
			var map_center = new google.maps.LatLng(0,0);
2925
			var map = "";
2926
			var bounds = new google.maps.LatLngBounds ();
2927
			var markers = [];
2928
			var gmarkers = [];
2929
			var i = 0;
2930
2931
			// Create the map and mapOptions
2932
			var mapOptions = {
2933
				zoom: 8,
2934
				center: map_center,
2935
				mapTypeId: google.maps.MapTypeId.' . $this->getSetting('GM_MAP_TYPE') . ',
2936
				mapTypeControlOptions: {
2937
					style: google.maps.MapTypeControlStyle.DROPDOWN_MENU // DEFAULT, DROPDOWN_MENU, HORIZONTAL_BAR
2938
				},
2939
				navigationControl: true,
2940
				navigationControlOptions: {
2941
					position: google.maps.ControlPosition.TOP_RIGHT, // BOTTOM, BOTTOM_LEFT, LEFT, TOP, etc
2942
					style: google.maps.NavigationControlStyle.SMALL  // ANDROID, DEFAULT, SMALL, ZOOM_PAN
2943
				},
2944
				streetViewControl: false, // Show Pegman or not
2945
				scrollwheel: false
2946
			};
2947
			map = new google.maps.Map(document.getElementById("place_map"), mapOptions);
2948
2949
			// Close any infowindow when map is clicked
2950
			google.maps.event.addListener(map, "click", function() {
2951
				infowindow.close();
2952
			});
2953
2954
			// If only one marker, set zoom level to that of place in database
2955
			if (mapLevel != 0) {
2956
				var pointZoom = placezoom;
2957
			} else {
2958
				var pointZoom = 1;
2959
			}
2960
2961
			// Creates a marker whose info window displays the given name
2962
			function createMarker(point, html, icon, name) {
2963
				// Choose icon and shadow ============
2964
				if (icon.image && ' . $level . '<=3) {
2965
					if (icon.image!="' . WT_STATIC_URL . WT_MODULES_DIR . 'googlemap/images/marker_yellow.png") {
2966
						var iconImage = new google.maps.MarkerImage(icon.image,
2967
						new google.maps.Size(25, 15),
2968
						new google.maps.Point(0,0),
2969
						new google.maps.Point(12, 15));
2970
						var iconShadow = new google.maps.MarkerImage("' . WT_STATIC_URL . WT_MODULES_DIR . 'googlemap/images/flag_shadow.png",
2971
						new google.maps.Size(35, 45),
2972
						new google.maps.Point(0,0),
2973
						new google.maps.Point(1, 45));
2974
					} else {
2975
						var iconImage = new google.maps.MarkerImage(icon.image,
2976
						new google.maps.Size(20, 34),
2977
						new google.maps.Point(0,0),
2978
						new google.maps.Point(9, 34));
2979
						var iconShadow = new google.maps.MarkerImage("https://www.google.com/mapfiles/shadow50.png",
2980
						new google.maps.Size(37, 34),
2981
						new google.maps.Point(0,0),
2982
						new google.maps.Point(9, 34));
2983
					}
2984
				} else {
2985
					var iconImage = new google.maps.MarkerImage("https://maps.google.com/mapfiles/marker.png",
2986
					new google.maps.Size(20, 34),
2987
					new google.maps.Point(0,0),
2988
					new google.maps.Point(9, 34));
2989
					var iconShadow = new google.maps.MarkerImage("https://www.google.com/mapfiles/shadow50.png",
2990
					new google.maps.Size(37, 34),
2991
					new google.maps.Point(0,0),
2992
					new google.maps.Point(9, 34));
2993
				}
2994
				var posn = new google.maps.LatLng(0,0);
2995
				var marker = new google.maps.Marker({
2996
					position: point,
2997
					icon: iconImage,
2998
					shadow: iconShadow,
2999
					map: map,
3000
					title: name
3001
				});
3002
				// Show this markers name in the info window when it is clicked
3003
				google.maps.event.addListener(marker, "click", function() {
3004
					infowindow.close();
3005
					infowindow.setContent(html);
3006
					infowindow.open(map, marker);
3007
				});
3008
				// === Store the tab, category and event info as marker properties ===
3009
				marker.mypoint = point;
3010
				marker.mytitle = name;
3011
				marker.myposn = posn;
3012
				gmarkers.push(marker);
3013
				bounds.extend(marker.position);
3014
3015
				// If only one marker use database place zoom level rather than fitBounds of markers
3016
				if (numMarkers > 1) {
3017
					map.fitBounds(bounds);
3018
				} else {
3019
					map.setCenter(bounds.getCenter());
3020
					map.setZoom(parseFloat(pointZoom));
3021
				}
3022
				return marker;
3023
			}
3024
		');
3025
3026
		$levelm                           = $this->setLevelMap($level, $parent);
3027
		if (isset($levelo[0])) {
0 ignored issues
show
Bug introduced by
The variable $levelo seems only to be defined at a later point. As such the call to isset() seems to always evaluate to false.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
3028
			$levelo[0] = 0;
3029
		}
3030
		$numls                            = count($parent) - 1;
3031
		$levelo                           = $this->checkWhereAmI($numls, $levelm);
3032
		if ($numfound < 2 && ($level == 1 || !isset($levelo[$level - 1]))) {
3033
			$controller->addInlineJavascript('map.maxZoom=6;');
3034
		} elseif ($numfound < 2 && !isset($levelo[$level - 2])) {
0 ignored issues
show
Unused Code introduced by
This elseif statement is empty, and could be removed.

This check looks for the bodies of elseif statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These elseif bodies can be removed. If you have an empty elseif but statements in the else branch, consider inverting the condition.

Loading history...
3035
		} elseif ($level == 2) {
3036
			$controller->addInlineJavascript('map.maxZoom=10;');
3037
		}
3038
		//create markers
3039
3040
		ob_start(); // TODO: rewrite print_gm_markers, and the functions called therein, to either return text or add JS directly.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
3041
3042
		if ($numfound == 0 && $level > 0) {
3043
			if (isset($levelo[($level - 1)])) {  // ** BH not sure yet what this if statement is for ... TODO **
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
3044
				// show the current place on the map
3045
3046
				$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=?")
3047
				->execute(array($levelm))
3048
				->fetch(PDO::FETCH_ASSOC);
3049
3050
				if ($place) {
3051
					// re-calculate the hierarchy information required to display the current place
3052
					$thisloc = $parent;
3053
					array_pop($thisloc);
3054
					$thislevel      = $level - 1;
3055
					$thislinklevels = substr($linklevels, 0, strrpos($linklevels, '&amp;'));
3056
3057
					$this->printGoogleMapMarkers($place, $thislevel, $thisloc, $place['place_id'], $thislinklevels);
0 ignored issues
show
Documentation introduced by
$place is of type object<stdClass>|array, but the function expects a array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3058
				}
3059
			}
3060
		}
3061
3062
		// display any sub-places
3063
		$placeidlist = array();
3064
		foreach ($place_names as $placename) {
3065
			$thisloc                         = $parent;
3066
			$thisloc[]                       = $placename;
3067
			$this_levelm                     = $this->setLevelMap($level + 1, $thisloc);
3068
			if ($this_levelm) {
3069
				$placeidlist[] = $this_levelm;
3070
			}
3071
		}
3072
3073
		if ($placeidlist) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $placeidlist of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
3074
			// flip the array (thus removing duplicates)
3075
			$placeidlist = array_flip($placeidlist);
3076
			// remove entry for parent location
3077
			unset($placeidlist[$levelm]);
3078
		}
3079
		if ($placeidlist) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $placeidlist of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
3080
			// the keys are all we care about (this reverses the earlier array_flip, and ensures there are no "holes" in the array)
3081
			$placeidlist = array_keys($placeidlist);
3082
			// note: this implode/array_fill code generates one '?' for each entry in the $placeidlist array
3083
			$placelist =
3084
				Database::prepare(
3085
					"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" .
3086
					" FROM `##placelocation` WHERE pl_id IN (" . implode(',', array_fill(0, count($placeidlist), '?')) . ')'
3087
				)->execute($placeidlist)
3088
				->fetchAll(PDO::FETCH_ASSOC);
3089
3090
			foreach ($placelist as $place) {
3091
				$this->printGoogleMapMarkers($place, $level, $parent, $place['place_id'], $linklevels);
0 ignored issues
show
Bug introduced by
It seems like $place defined by $place on line 3090 can also be of type object<stdClass>; however, Fisharebest\Webtrees\Mod...printGoogleMapMarkers() does only seem to accept array<integer,string>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
3092
			}
3093
		}
3094
		$controller->addInlineJavascript(ob_get_clean());
3095
	}
3096
3097
	/**
3098
	 * Take a place id and find its place in the hierarchy
3099
	 * Input: place ID
3100
	 * Output: ordered array of id=>name values, starting with the Top level
3101
	 * e.g. 0=>"Top level", 16=>"England", 19=>"London", 217=>"Westminster"
3102
	 * NB This function exists in both places.php and places_edit.php
3103
	 *
3104
	 * @param int $id
3105
	 *
3106
	 * @return string[]
3107
	 */
3108
	private function placeIdToHierarchy($id) {
3109
		$statement = Database::prepare("SELECT pl_parent_id, pl_place FROM `##placelocation` WHERE pl_id=?");
3110
		$arr       = array();
3111
		while ($id != 0) {
3112
			$row = $statement->execute(array($id))->fetchOneRow();
3113
			$arr = array($id => $row->pl_place) + $arr;
3114
			$id  = $row->pl_parent_id;
3115
		}
3116
3117
		return $arr;
3118
	}
3119
3120
	/**
3121
	 * Get the highest index.
3122
	 *
3123
	 * @return int
3124
	 */
3125
	private function getHighestIndex() {
3126
		return (int) Database::prepare("SELECT MAX(pl_id) FROM `##placelocation`")->fetchOne();
3127
	}
3128
3129
	/**
3130
	 * Get the highest level.
3131
	 *
3132
	 * @return int
3133
	 */
3134
	private function getHighestLevel() {
3135
		return (int) Database::prepare("SELECT MAX(pl_level) FROM `##placelocation`")->fetchOne();
3136
	}
3137
3138
	/**
3139
	 * Find all of the places in the hierarchy
3140
	 *
3141
	 * @param int  $parent_id
3142
	 * @param bool $inactive
3143
	 *
3144
	 * @return array[]
3145
	 */
3146
	private function getPlaceListLocation($parent_id, $inactive = false) {
3147
		if ($inactive) {
3148
			$rows = Database::prepare(
3149
					"SELECT pl_id, pl_place, pl_lati, pl_long, pl_zoom, pl_icon" .
3150
					" FROM `##placelocation`" .
3151
					" WHERE pl_parent_id = :parent_id" .
3152
					" ORDER BY pl_place COLLATE :collation"
3153
				)->execute(array(
3154
					'parent_id' => $parent_id,
3155
					'collation' => I18N::collation(),
3156
				))->fetchAll();
3157
		} else {
3158
			$rows = Database::prepare(
3159
				"SELECT DISTINCT pl_id, pl_place, pl_lati, pl_long, pl_zoom, pl_icon" .
3160
				" FROM `##placelocation`" .
3161
				" INNER JOIN `##places` ON `##placelocation`.pl_place=`##places`.p_place" .
3162
				" WHERE pl_parent_id = :parent_id" .
3163
				" ORDER BY pl_place COLLATE :collation"
3164
			)->execute(array(
3165
				'parent_id' => $parent_id,
3166
				'collation' => I18N::collation(),
3167
			))->fetchAll();
3168
		}
3169
3170
		$placelist = array();
3171
		foreach ($rows as $row) {
3172
			$placelist[] = array(
3173
				'place_id' => $row->pl_id,
3174
				'place'    => $row->pl_place,
3175
				'lati'     => $row->pl_lati,
3176
				'long'     => $row->pl_long,
3177
				'zoom'     => $row->pl_zoom,
3178
				'icon'     => $row->pl_icon,
3179
			);
3180
		}
3181
3182
		return $placelist;
3183
	}
3184
3185
	/**
3186
	 * Set the output level.
3187
	 *
3188
	 * @param int $parent_id
3189
	 */
3190
	private function outputLevel($parent_id) {
3191
		$tmp      = $this->placeIdToHierarchy($parent_id);
3192
		$maxLevel = $this->getHighestLevel();
3193
		if ($maxLevel > 8) {
3194
			$maxLevel = 8;
3195
		}
3196
		$prefix = implode(';', $tmp);
3197
		if ($prefix != '') {
3198
			$prefix .= ';';
3199
		}
3200
		$suffix = str_repeat(';', $maxLevel - count($tmp));
3201
		$level  = count($tmp);
3202
3203
		$rows = Database::prepare(
3204
			"SELECT pl_id, pl_place, pl_long, pl_lati, pl_zoom, pl_icon FROM `##placelocation` WHERE pl_parent_id=? ORDER BY pl_place"
3205
		)->execute(array($parent_id))->fetchAll();
3206
3207
		foreach ($rows as $row) {
3208
			echo $level, ';', $prefix, $row->pl_place, $suffix, ';', $row->pl_long, ';', $row->pl_lati, ';', $row->pl_zoom, ';', $row->pl_icon, "\r\n";
3209
			if ($level < $maxLevel) {
3210
				$this->outputLevel($row->pl_id);
3211
			}
3212
		}
3213
	}
3214
3215
	/**
3216
	 * recursively find all of the csv files on the server
3217
	 *
3218
	 * @param string $path
3219
	 *
3220
	 * @return string[]
3221
	 */
3222
	private function findFiles($path) {
3223
		$placefiles = array();
3224
3225
		if (file_exists($path)) {
3226
			$dir = dir($path);
3227
			while (false !== ($entry = $dir->read())) {
3228
				if ($entry !== '.' && $entry !== '..') {
3229
					if (is_dir($path . '/' . $entry)) {
3230
						$this->findFiles($path . '/' . $entry);
3231
					} elseif (strstr($entry, '.csv') !== false) {
3232
						$placefiles[] = preg_replace('~' . WT_MODULES_DIR . 'googlemap/extra~', '', $path) . '/' . $entry;
3233
					}
3234
				}
3235
			}
3236
			$dir->close();
3237
		}
3238
3239
		return $placefiles;
3240
	}
3241
3242
	/**
3243
	 * Edit places.
3244
	 */
3245
	private function placesEdit() {
0 ignored issues
show
Coding Style introduced by
placesEdit uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
3246
		$GM_MAX_ZOOM = $this->getSetting('GM_MAX_ZOOM');
3247
3248
		$action     = Filter::post('action', null, Filter::get('action'));
3249
		$placeid    = Filter::post('placeid', null, Filter::get('placeid'));
3250
		$place_name = Filter::post('place_name', null, Filter::get('place_name'));
3251
3252
		$placeid = (int) $placeid; // Convert empty string to zero
3253
3254
		$controller = new SimpleController;
3255
		$controller
3256
			->restrictAccess(Auth::isAdmin())
3257
			->setPageTitle(I18N::translate('Geographic data'))
3258
			->addInlineJavascript('$("<link>", {rel: "stylesheet", type: "text/css", href: "' . WT_STATIC_URL . WT_MODULES_DIR . 'googlemap/css/wt_v3_googlemap.css"}).appendTo("head");')
3259
			->pageHeader();
3260
3261
		$where_am_i = $this->placeIdToHierarchy($placeid);
3262
		$level      = count($where_am_i);
3263
3264
		if ($action == 'addrecord' && Auth::isAdmin()) {
3265
			$statement =
3266
				Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_long, pl_lati, pl_zoom, pl_icon) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
3267
3268
			if (($_POST['LONG_CONTROL'] == '') || ($_POST['NEW_PLACE_LONG'] == '') || ($_POST['NEW_PLACE_LATI'] == '')) {
3269
				$statement->execute(array($this->getHighestIndex() + 1, $placeid, $level, $_POST['NEW_PLACE_NAME'], null, null, $_POST['NEW_ZOOM_FACTOR'], $_POST['icon']));
3270 View Code Duplication
			} else {
3271
				$statement->execute(array($this->getHighestIndex() + 1, $placeid, $level, $_POST['NEW_PLACE_NAME'], $_POST['LONG_CONTROL'][3] . $_POST['NEW_PLACE_LONG'], $_POST['LATI_CONTROL'][3] . $_POST['NEW_PLACE_LATI'], $_POST['NEW_ZOOM_FACTOR'], $_POST['icon']));
3272
			}
3273
3274
			$controller->addInlineJavascript('closePopupAndReloadParent();');
3275
3276
			return;
3277
		}
3278
3279
		if ($action == 'updaterecord' && Auth::isAdmin()) {
3280
			$statement =
3281
				Database::prepare("UPDATE `##placelocation` SET pl_place=?, pl_lati=?, pl_long=?, pl_zoom=?, pl_icon=? WHERE pl_id=?");
3282
3283
			if (($_POST['LONG_CONTROL'] == '') || ($_POST['NEW_PLACE_LONG'] == '') || ($_POST['NEW_PLACE_LATI'] == '')) {
3284
				$statement->execute(array($_POST['NEW_PLACE_NAME'], null, null, $_POST['NEW_ZOOM_FACTOR'], $_POST['icon'], $placeid));
3285 View Code Duplication
			} else {
3286
				$statement->execute(array($_POST['NEW_PLACE_NAME'], $_POST['LATI_CONTROL'][3] . $_POST['NEW_PLACE_LATI'], $_POST['LONG_CONTROL'][3] . $_POST['NEW_PLACE_LONG'], $_POST['NEW_ZOOM_FACTOR'], $_POST['icon'], $placeid));
3287
			}
3288
3289
			$controller->addInlineJavascript('closePopupAndReloadParent();');
3290
3291
			return;
3292
		}
3293
3294
		// Update placelocation STREETVIEW fields
3295
		// TODO: This ought to be a POST request, rather than a GET request
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
3296
		if ($action == 'update_sv_params' && Auth::isAdmin()) {
3297
			Database::prepare(
3298
				"UPDATE `##placelocation` SET sv_lati=?, sv_long=?, sv_bearing=?, sv_elevation=?, sv_zoom=? WHERE pl_id=?"
3299
			)->execute(array(
3300
				Filter::get('svlati'),
3301
				Filter::get('svlong'),
3302
				Filter::get('svbear'),
3303
				Filter::get('svelev'),
3304
				Filter::get('svzoom'),
3305
				$placeid,
3306
			));
3307
			$controller->addInlineJavascript('window.close();');
3308
3309
			return;
3310
		}
3311
3312
		if ($action === 'update') {
3313
			// --- find the place in the file
3314
			$row =
3315
				Database::prepare("SELECT pl_place, pl_lati, pl_long, pl_icon, pl_parent_id, pl_level, pl_zoom FROM `##placelocation` WHERE pl_id=?")
3316
				->execute(array($placeid))
3317
				->fetchOneRow();
3318
			$place_name       = $row->pl_place;
3319
			$place_icon       = $row->pl_icon;
3320
			$selected_country = explode("/", $place_icon);
3321
			if (isset($selected_country[1]) && $selected_country[1] != "flags")
3322
				$selected_country = $selected_country[1];
3323
			else
3324
				$selected_country = "Countries";
3325
			$parent_id         = $row->pl_parent_id;
3326
			$level             = $row->pl_level;
3327
			$zoomfactor        = $row->pl_zoom;
3328
			$parent_lati       = 0.0;
3329
			$parent_long       = 0.0;
3330
			if ($row->pl_lati !== null && $row->pl_long !== null) {
3331
				$place_lati = (float) (str_replace(array('N', 'S', ','), array('', '-', '.'), $row->pl_lati));
3332
				$place_long = (float) (str_replace(array('E', 'W', ','), array('', '-', '.'), $row->pl_long));
3333
			} else {
3334
				$place_lati = 0.0;
3335
				$place_long = 0.0;
3336
				$zoomfactor = 1;
3337
			}
3338
3339
			do {
3340
				$row =
3341
					Database::prepare("SELECT pl_lati, pl_long, pl_parent_id, pl_zoom FROM `##placelocation` WHERE pl_id=?")
3342
					->execute(array($parent_id))
3343
					->fetchOneRow();
3344
				if (!$row) {
3345
					break;
3346
				}
3347
				if ($row->pl_lati !== null && $row->pl_long !== null) {
3348
					$parent_lati = (float) (str_replace(array('N', 'S', ','), array('', '-', '.'), $row->pl_lati));
3349
					$parent_long = (float) (str_replace(array('E', 'W', ','), array('', '-', '.'), $row->pl_long));
3350
					if ($zoomfactor == 1) {
3351
						$zoomfactor = $row->pl_zoom;
3352
					}
3353
				}
3354
				$parent_id = $row->pl_parent_id;
3355
			} while ($row->pl_parent_id != 0 && $row->pl_lati === null && $row->pl_long === null);
3356
3357
			echo '<b>', Filter::escapeHtml(str_replace('Unknown', I18N::translate('unknown'), implode(I18N::$list_separator, array_reverse($where_am_i, true)))), '</b><br>';
3358
		}
3359
3360
		if ($action === 'add') {
3361
			// --- find the parent place in the file
3362
			if ($placeid != 0) {
3363
				$place_lati  = 0.0;
3364
				$place_long  = 0.0;
3365
				$zoomfactor  = 1;
3366
				$parent_lati = 0.0;
3367
				$parent_long = 0.0;
3368
				$place_icon  = '';
3369
				$parent_id   = $placeid;
3370
				do {
3371
					$row =
3372
						Database::prepare("SELECT pl_lati, pl_long, pl_parent_id, pl_zoom, pl_level FROM `##placelocation` WHERE pl_id=?")
3373
						->execute(array($parent_id))
3374
						->fetchOneRow();
3375
					if ($row->pl_lati !== null && $row->pl_long !== null) {
3376
						$parent_lati = str_replace(array('N', 'S', ','), array('', '-', '.'), $row->pl_lati);
3377
						$parent_long = str_replace(array('E', 'W', ','), array('', '-', '.'), $row->pl_long);
3378
						$zoomfactor  = $row->pl_zoom;
3379
						if ($zoomfactor > $GM_MAX_ZOOM) {
3380
							$zoomfactor = $GM_MAX_ZOOM;
3381
						}
3382
						$level = $row->pl_level + 1;
3383
					}
3384
					$parent_id = $row->pl_parent_id;
3385
				} while ($row->pl_parent_id != 0 && $row->pl_lati === null && $row->pl_long === null);
3386
			} else {
3387
				$place_lati  = 0.0;
3388
				$place_long  = 0.0;
3389
				$parent_lati = 0.0;
3390
				$parent_long = 0.0;
3391
				$place_icon  = '';
3392
				$parent_id   = 0;
3393
				$level       = 0;
3394
				$zoomfactor  = $this->getSetting('GM_MIN_ZOOM');
3395
			}
3396
			$selected_country = 'Countries';
3397
3398
			if ($place_name == '') {
3399
				echo '<b>', I18N::translate('unknown');
3400
			} else {
3401
				echo '<b>', $place_name;
3402
			}
3403
			if (count($where_am_i) > 0)
3404
				echo ', ', Filter::escapeHtml(str_replace('Unknown', I18N::translate('unknown'), implode(I18N::$list_separator, array_reverse($where_am_i, true)))), '</b><br>';
3405
			echo '</b><br>';
3406
		}
3407
3408
		?>
3409
3410
			<script src="<?php echo $this->googleMapsScript() ?>"></script>
3411
			<script>
3412
			var map;
3413
			var marker;
3414
			var zoom;
3415
			var pl_name = '<?php echo Filter::escapeJs($place_name) ?>';
3416
			if (<?php echo $place_lati ?> !== 0.0 && <?php echo $place_long ?> !== 0.0) {
0 ignored issues
show
Bug introduced by
The variable $place_lati does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $place_long does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3417
				var latlng = new google.maps.LatLng(<?php echo $place_lati ?>, <?php echo $place_long ?>);
3418
			} else {
3419
				var latlng = new google.maps.LatLng(<?php echo $parent_lati ?>, <?php echo $parent_long ?>);
0 ignored issues
show
Bug introduced by
The variable $parent_lati does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $parent_long does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3420
			}
3421
			var pl_zoom = <?php echo $zoomfactor ?>;
0 ignored issues
show
Bug introduced by
The variable $zoomfactor does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3422
			var polygon1;
3423
			var polygon2;
3424
			var geocoder;
3425
			var mapType;
3426
3427
			var infowindow = new google.maps.InfoWindow({
3428
				//
3429
			});
3430
3431
			function geocodePosition(pos) {
3432
				geocoder.geocode({
3433
					latLng: pos
3434
				}, function(responses) {
3435
					if (responses && responses.length > 0) {
3436
						updateMarkerAddress(responses[0].formatted_address);
3437
					} else {
3438
						updateMarkerAddress('Cannot determine address at this location.');
3439
					}
3440
				});
3441
			}
3442
3443
			function updateMap(event) {
3444
				var point;
3445
				var zoom = parseInt(document.editplaces.NEW_ZOOM_FACTOR.value);
3446
				var latitude;
3447
				var longitude;
3448
				var i;
3449
				var prec = 20;
3450
3451
				for (i=0;i<document.editplaces.NEW_PRECISION.length;i++) {
3452
					if (document.editplaces.NEW_PRECISION[i].checked) {
3453
						prec = document.editplaces.NEW_PRECISION[i].value;
3454
					}
3455
				}
3456
				if ((document.editplaces.NEW_PLACE_LATI.value == '') ||
3457
					(document.editplaces.NEW_PLACE_LONG.value == '')) {
3458
					latitude = parseFloat(document.editplaces.parent_lati.value).toFixed(prec);
3459
					longitude = parseFloat(document.editplaces.parent_long.value).toFixed(prec);
3460
					point = new google.maps.LatLng(latitude, longitude);
3461
				} else {
3462
					latitude = parseFloat(document.editplaces.NEW_PLACE_LATI.value).toFixed(prec);
3463
					longitude = parseFloat(document.editplaces.NEW_PLACE_LONG.value).toFixed(prec);
3464
					document.editplaces.NEW_PLACE_LATI.value = latitude;
3465
					document.editplaces.NEW_PLACE_LONG.value = longitude;
3466
3467
					if (event == 'flag_drag') {
3468
						if (longitude < 0.0 ) {
3469
							longitude = longitude * -1;
3470
							document.editplaces.NEW_PLACE_LONG.value = longitude;
3471
							document.editplaces.LONG_CONTROL.value = 'PL_W';
3472
						} else {
3473
							document.editplaces.NEW_PLACE_LONG.value = longitude;
3474
							document.editplaces.LONG_CONTROL.value = 'PL_E';
3475
						}
3476
						if (latitude < 0.0 ) {
3477
							latitude = latitude * -1;
3478
							document.editplaces.NEW_PLACE_LATI.value = latitude;
3479
							document.editplaces.LATI_CONTROL.value = 'PL_S';
3480
						} else {
3481
							document.editplaces.NEW_PLACE_LATI.value = latitude;
3482
							document.editplaces.LATI_CONTROL.value = 'PL_N';
3483
						}
3484
3485
						if (document.editplaces.LATI_CONTROL.value == 'PL_S') {
3486
							latitude = latitude * -1;
3487
						}
3488
						if (document.editplaces.LONG_CONTROL.value == 'PL_W') {
3489
							longitude = longitude * -1;
3490
						}
3491
						point = new google.maps.LatLng(latitude, longitude);
3492
					} else {
3493
						if (latitude < 0.0) {
3494
							latitude = latitude * -1;
3495
							document.editplaces.NEW_PLACE_LATI.value = latitude;
3496
						}
3497
						if (longitude < 0.0) {
3498
							longitude = longitude * -1;
3499
							document.editplaces.NEW_PLACE_LONG.value = longitude;
3500
						}
3501
						if (document.editplaces.LATI_CONTROL.value == 'PL_S') {
3502
							latitude = latitude * -1;
3503
						}
3504
						if (document.editplaces.LONG_CONTROL.value == 'PL_W') {
3505
							longitude = longitude * -1;
3506
						}
3507
						point = new google.maps.LatLng(latitude, longitude);
3508
					}
3509
				}
3510
3511
				map.setCenter(point);
3512
				map.setZoom(zoom);
3513
				marker.setPosition(point);
3514
3515
			}
3516
3517
			// === Create Borders for the UK Countries =========================================================
3518
			function overlays() {
3519
3520
				// Define place LatLng arrays
3521
				var polygon1;
3522
				var num_arrays = "";
3523
				if (pl_name == 'Scotland') {
3524
					var returnGeom1 = '-2.02166,55.80611|-2.07972,55.86722|-2.13028,55.88583|-2.26028,55.91861|-2.37528,55.95694|-2.65722,56.05972|-2.82028,56.05694|-2.86618,56.02840|-2.89555,55.98861|-2.93500,55.96944|-3.01805,55.94944|-3.06750,55.94444|-3.25472,55.97166|-3.45472,55.99194|-3.66416,56.00652|-3.73722,56.05555|-3.57139,56.05360|-3.44111,56.01916|-3.39584,56.01083|-3.34403,56.02333|-3.13903,56.11084|-2.97611,56.19472|-2.91666,56.20499|-2.84695,56.18638|-2.78805,56.18749|-2.67937,56.21465|-2.58403,56.28264|-2.67208,56.32277|-2.76861,56.33180|-2.81528,56.37360|-2.81208,56.43958|-2.91653,56.45014|-2.99555,56.41416|-3.19042,56.35958|-3.27805,56.35750|-3.04055,56.45472|-2.95861,56.45611|-2.72084,56.48888|-2.64084,56.52250|-2.53126,56.57611|-2.48861,56.61416|-2.47805,56.71527|-2.39000,56.77166|-2.31986,56.79638|-2.21972,56.86777|-2.19708,56.94388|-2.16695,57.00055|-2.09334,57.07027|-2.05416,57.21861|-1.95889,57.33250|-1.85584,57.39889|-1.77334,57.45805|-1.78139,57.50555|-1.82195,57.57861|-1.86000,57.62138|-1.92972,57.67777|-2.02222,57.69388|-2.07555,57.69944|-2.14028,57.69056|-2.18611,57.66861|-2.39626,57.66638|-2.51000,57.67166|-2.78639,57.70222|-2.89806,57.70694|-2.96750,57.68027|-3.03847,57.66249|-3.12334,57.67166|-3.22334,57.69166|-3.28625,57.72499|-3.33972,57.72333|-3.48805,57.70945|-3.52222,57.66333|-3.59542,57.63666|-3.64063,57.63881|-3.75414,57.62504|-4.03986,57.55569|-4.19666,57.48584|-4.22889,57.51554|-4.17945,57.56249|-4.11139,57.59833|-4.08078,57.66533|-4.19139,57.67139|-4.25945,57.65527|-4.34361,57.60777|-4.41639,57.60166|-4.29666,57.67444|-4.08528,57.72611|-4.01908,57.70226|-3.96861,57.70250|-3.86556,57.76861|-3.81945,57.80458|-3.80681,57.85819|-3.85055,57.82000|-3.92639,57.80749|-4.04322,57.81438|-4.14973,57.82527|-4.29750,57.84638|-4.36250,57.89777|-4.24306,57.87028|-4.10666,57.85195|-4.01500,57.86777|-3.99166,57.90611|-3.99695,57.95056|-3.84500,58.02000|-3.56611,58.13916|-3.51319,58.16374|-3.45916,58.20305|-3.42028,58.24361|-3.33750,58.27694|-3.20555,58.30625|-3.10972,58.38166|-3.05792,58.45083|-3.02264,58.64653|-3.17639,58.64944|-3.35389,58.66055|-3.36931,58.59555|-3.57611,58.62194|-3.66028,58.61972|-3.71166,58.60374|-3.78264,58.56750|-3.84834,58.56000|-4.08056,58.55527|-4.27722,58.53361|-4.43653,58.54902|-4.50666,58.56777|-4.56055,58.57584|-4.59910,58.53027|-4.66805,58.48833|-4.76146,58.44604|-4.70195,58.50999|-4.70166,58.55861|-4.77014,58.60264|-5.00153,58.62416|-5.10945,58.50833|-5.16472,58.32527|-5.12639,58.28750|-5.07166,58.26472|-5.20361,58.25083|-5.39764,58.25055|-5.27389,58.11722|-5.31514,58.06416|-5.38416,58.08361|-5.45285,58.07416|-5.39805,58.03111|-5.26278,57.97111|-5.19334,57.95069|-5.12750,57.86944|-5.21750,57.90084|-5.33861,57.92083|-5.42876,57.90104|-5.45750,57.85889|-5.64445,57.89972|-5.62555,57.85222|-5.58153,57.81945|-5.60674,57.76618|-5.66305,57.78889|-5.71695,57.86944|-5.76695,57.86472|-5.81708,57.81944|-5.81084,57.63958|-5.69555,57.55944|-5.64361,57.55222|-5.53084,57.52833|-5.65305,57.50875|-5.75000,57.54834|-5.81569,57.57923|-5.85042,57.54972|-5.86695,57.46777|-5.81806,57.36250|-5.75111,57.34333|-5.50334,57.40111|-5.45126,57.41805|-5.49250,57.37083|-5.59884,57.33049|-5.57116,57.28411|-5.51266,57.27745|-5.40514,57.23097|-5.44972,57.22138|-5.49472,57.23888|-5.56066,57.25477|-5.64611,57.23499|-5.64751,57.16161|-5.55028,57.11639|-5.48166,57.11222|-5.40305,57.11062|-5.55945,57.09250|-5.65111,57.11611|-5.72472,57.11306|-5.77361,57.04556|-5.63139,56.98499|-5.56916,56.98972|-5.52403,56.99735|-5.57916,56.98000|-5.64611,56.97222|-5.73374,57.00909|-5.82584,57.00346|-5.91958,56.88708|-5.86528,56.87944|-5.74278,56.89374|-5.66292,56.86924|-5.73306,56.83916|-5.78584,56.83955|-5.85590,56.81430|-5.80208,56.79180|-5.84958,56.74444|-5.90500,56.75666|-5.96694,56.78027|-6.14000,56.75777|-6.19208,56.74888|-6.23452,56.71673|-6.19139,56.67972|-5.91916,56.67388|-5.82622,56.69156|-5.73945,56.71166|-5.55240,56.68886|-5.64861,56.68027|-5.69916,56.68278|-5.88261,56.65666|-5.97472,56.65138|-5.99584,56.61138|-5.93056,56.56972|-5.88416,56.55333|-5.79056,56.53805|-5.67695,56.49389|-5.56389,56.54056|-5.36334,56.66195|-5.23416,56.74333|-5.13236,56.79403|-5.31473,56.65666|-5.37405,56.55925|-5.31826,56.55633|-5.25080,56.55753|-5.37718,56.52112|-5.39866,56.47866|-5.19111,56.46194|-5.11556,56.51277|-5.07014,56.56069|-5.13555,56.48499|-5.22084,56.43583|-5.32764,56.43574|-5.42439,56.43091|-5.52611,56.37360|-5.57139,56.32833|-5.59653,56.25695|-5.57389,56.16000|-5.52000,56.16485|-5.56334,56.11333|-5.60139,56.07638|-5.64222,56.04305|-5.66039,55.98263|-5.62555,56.02055|-5.58014,56.01319|-5.63361,55.96611|-5.67697,55.88844|-5.64750,55.78139|-5.60986,55.75930|-5.66916,55.66166|-5.70166,55.58861|-5.71805,55.51500|-5.75916,55.41750|-5.79528,55.36027|-5.78166,55.29902|-5.73778,55.29222|-5.56694,55.31666|-5.51528,55.36347|-5.55520,55.41440|-5.48639,55.64306|-5.44597,55.70680|-5.38000,55.75027|-5.41889,55.90666|-5.39924,55.99972|-5.33895,56.03456|-5.30594,56.06922|-5.23889,56.11889|-5.03222,56.23250|-4.92229,56.27111|-4.97416,56.23333|-5.07222,56.18695|-5.20069,56.11861|-5.30906,56.00570|-5.34000,55.90201|-5.29250,55.84750|-5.20805,55.84444|-5.22458,55.90175|-5.17334,55.92916|-5.11000,55.90306|-5.01222,55.86694|-4.96195,55.88000|-4.89824,55.98145|-4.84623,56.08632|-4.86636,56.03178|-4.85461,55.98648|-4.77659,55.97977|-4.62723,55.94555|-4.52305,55.91861|-4.70972,55.93403|-4.75166,55.94611|-4.82406,55.94950|-4.87826,55.93653|-4.91639,55.70083|-4.87584,55.68194|-4.81361,55.64555|-4.68722,55.59750|-4.61361,55.49069|-4.63958,55.44264|-4.68250,55.43388|-4.74847,55.41055|-4.83715,55.31882|-4.84778,55.26944|-4.86542,55.22340|-4.93500,55.17860|-5.01250,55.13347|-5.05361,55.04902|-5.17834,54.98888|-5.18563,54.93622|-5.17000,54.89111|-5.11666,54.83180|-5.00500,54.76333|-4.96229,54.68125|-4.92250,54.64055|-4.85723,54.62958|-4.96076,54.79687|-4.92431,54.83708|-4.85222,54.86861|-4.80125,54.85556|-4.74055,54.82166|-4.68084,54.79972|-4.59861,54.78027|-4.55792,54.73903|-4.49639,54.69888|-4.37584,54.67666|-4.34569,54.70916|-4.35973,54.77111|-4.41111,54.82583|-4.42445,54.88152|-4.38479,54.90555|-4.35056,54.85903|-4.09555,54.76777|-3.95361,54.76749|-3.86972,54.80527|-3.81222,54.84888|-3.69250,54.88110|-3.61584,54.87527|-3.57111,54.99083|-3.44528,54.98638|-3.36056,54.97138|-3.14695,54.96500|-3.05103,54.97986|-3.01500,55.05222|-2.96278,55.03889|-2.69945,55.17722|-2.63055,55.25500|-2.46305,55.36111|-2.21236,55.42777|-2.18278,55.45985|-2.21528,55.50583|-2.27416,55.57527|-2.27916,55.64472|-2.22000,55.66499|-2.08361,55.78054|-2.02166,55.80611';
3525
					num_arrays = 1;
3526
				} else if (pl_name == 'England') {
3527
					// England
3528
					var returnGeom1 = '-4.74361,50.66750|-4.78361,50.59361|-4.91584,50.57722|-5.01750,50.54264|-5.02569,50.47271|-5.04729,50.42750|-5.15208,50.34374|-5.26805,50.27389|-5.43194,50.19326|-5.49584,50.21695|-5.54639,50.20527|-5.71000,50.12916|-5.71681,50.06083|-5.66174,50.03631|-5.58278,50.04777|-5.54166,50.07055|-5.53416,50.11569|-5.47055,50.12499|-5.33361,50.09138|-5.27666,50.05972|-5.25674,50.00514|-5.19306,49.95527|-5.16070,50.00319|-5.06555,50.03750|-5.07090,50.08166|-5.04806,50.17111|-4.95278,50.19333|-4.85750,50.23166|-4.76250,50.31138|-4.67861,50.32583|-4.54334,50.32222|-4.48278,50.32583|-4.42972,50.35139|-4.38000,50.36388|-4.16555,50.37028|-4.11139,50.33027|-4.05708,50.29791|-3.94389,50.31346|-3.87764,50.28139|-3.83653,50.22972|-3.78944,50.21222|-3.70666,50.20972|-3.65195,50.23111|-3.55139,50.43833|-3.49416,50.54639|-3.46181,50.58792|-3.41139,50.61610|-3.24416,50.67444|-3.17347,50.68833|-3.09445,50.69222|-2.97806,50.70638|-2.92750,50.73125|-2.88278,50.73111|-2.82305,50.72027|-2.77139,50.70861|-2.66195,50.67334|-2.56305,50.63222|-2.45861,50.57500|-2.44666,50.62639|-2.39097,50.64166|-2.19722,50.62611|-2.12195,50.60722|-2.05445,50.58569|-1.96437,50.59674|-1.95441,50.66536|-2.06681,50.71430|-1.93416,50.71277|-1.81639,50.72306|-1.68445,50.73888|-1.59278,50.72416|-1.33139,50.79138|-1.11695,50.80694|-1.15889,50.84083|-1.09445,50.84584|-0.92842,50.83966|-0.86584,50.79965|-0.90826,50.77396|-0.78187,50.72722|-0.74611,50.76583|-0.67528,50.78111|-0.57722,50.79527|-0.25500,50.82638|-0.19084,50.82583|-0.13805,50.81833|0.05695,50.78083|0.12334,50.75944|0.22778,50.73944|0.28695,50.76500|0.37195,50.81638|0.43084,50.83111|0.56722,50.84777|0.67889,50.87681|0.71639,50.90500|0.79334,50.93610|0.85666,50.92556|0.97125,50.98111|0.99778,51.01903|1.04555,51.04944|1.10028,51.07361|1.26250,51.10166|1.36889,51.13583|1.41111,51.20111|1.42750,51.33111|1.38556,51.38777|1.19195,51.37861|1.05278,51.36722|0.99916,51.34777|0.90806,51.34069|0.70416,51.37749|0.61972,51.38304|0.55945,51.40596|0.64236,51.44042|0.69750,51.47084|0.59195,51.48777|0.53611,51.48806|0.48916,51.48445|0.45215,51.45562|0.38894,51.44822|0.46500,51.50306|0.65195,51.53680|0.76695,51.52138|0.82084,51.53556|0.87528,51.56110|0.95250,51.60923|0.94695,51.72556|0.90257,51.73465|0.86306,51.71166|0.76140,51.69164|0.70111,51.71847|0.86211,51.77361|0.93236,51.80583|0.98278,51.82527|1.03569,51.77416|1.08834,51.77056|1.13222,51.77694|1.18139,51.78972|1.22361,51.80888|1.26611,51.83916|1.28097,51.88096|1.20834,51.95083|1.16347,52.02361|1.27750,51.98555|1.33125,51.92875|1.39028,51.96999|1.58736,52.08388|1.63000,52.19527|1.68576,52.32630|1.73028,52.41138|1.74945,52.45583|1.74590,52.62021|1.70250,52.71583|1.64528,52.77111|1.50361,52.83749|1.43222,52.87472|1.35250,52.90972|1.28222,52.92750|1.18389,52.93889|0.99472,52.95111|0.94222,52.95083|0.88472,52.96638|0.66722,52.97611|0.54778,52.96618|0.49139,52.93430|0.44431,52.86569|0.42903,52.82403|0.36334,52.78027|0.21778,52.80694|0.16125,52.86250|0.05778,52.88916|0.00211,52.87985|0.03222,52.91722|0.20389,53.02805|0.27666,53.06694|0.33916,53.09236|0.35389,53.18722|0.33958,53.23472|0.23555,53.39944|0.14347,53.47527|0.08528,53.48638|0.02694,53.50972|-0.10084,53.57306|-0.20722,53.63083|-0.26445,53.69083|-0.30166,53.71319|-0.39022,53.70794|-0.51972,53.68527|-0.71653,53.69638|-0.65445,53.72527|-0.60584,53.72972|-0.54916,53.70611|-0.42261,53.71755|-0.35728,53.73056|-0.29389,53.73666|-0.23139,53.72166|-0.10584,53.63166|-0.03472,53.62555|0.04416,53.63916|0.08916,53.62666|0.14945,53.58847|0.12639,53.64527|0.06264,53.70389|-0.12750,53.86388|-0.16916,53.91847|-0.21222,54.00833|-0.20569,54.05153|-0.16111,54.08806|-0.11694,54.13222|-0.20053,54.15171|-0.26250,54.17444|-0.39334,54.27277|-0.42166,54.33222|-0.45750,54.37694|-0.51847,54.44749|-0.56472,54.48000|-0.87584,54.57027|-1.06139,54.61722|-1.16528,54.64972|-1.30445,54.77138|-1.34556,54.87138|-1.41278,54.99944|-1.48292,55.08625|-1.51500,55.14972|-1.56584,55.28722|-1.58097,55.48361|-1.63597,55.58194|-1.69000,55.60556|-1.74695,55.62499|-1.81764,55.63306|-1.97681,55.75416|-2.02166,55.80611|-2.08361,55.78054|-2.22000,55.66499|-2.27916,55.64472|-2.27416,55.57527|-2.21528,55.50583|-2.18278,55.45985|-2.21236,55.42777|-2.46305,55.36111|-2.63055,55.25500|-2.69945,55.17722|-2.96278,55.03889|-3.01500,55.05222|-3.05103,54.97986|-3.13292,54.93139|-3.20861,54.94944|-3.28931,54.93792|-3.39166,54.87639|-3.42916,54.81555|-3.56916,54.64249|-3.61306,54.48861|-3.49305,54.40333|-3.43389,54.34806|-3.41056,54.28014|-3.38055,54.24444|-3.21472,54.09555|-3.15222,54.08194|-2.93097,54.15333|-2.81361,54.22277|-2.81750,54.14277|-2.83361,54.08500|-2.93250,53.95055|-3.05264,53.90764|-3.03708,53.74944|-2.99278,53.73277|-2.89979,53.72499|-2.97729,53.69382|-3.07306,53.59805|-3.10563,53.55993|-3.00678,53.41738|-2.95389,53.36027|-2.85736,53.32083|-2.70493,53.35062|-2.77639,53.29250|-2.89972,53.28916|-2.94250,53.31056|-3.02889,53.38191|-3.07248,53.40936|-3.16695,53.35708|-3.12611,53.32500|-3.08860,53.26001|-3.02000,53.24722|-2.95528,53.21555|-2.91069,53.17014|-2.89389,53.10416|-2.85695,53.03249|-2.77792,52.98514|-2.73109,52.96873|-2.71945,52.91902|-2.79278,52.90207|-2.85069,52.93875|-2.99389,52.95361|-3.08639,52.91611|-3.13014,52.88486|-3.13708,52.79312|-3.06806,52.77027|-3.01111,52.71166|-3.06666,52.63527|-3.11750,52.58666|-3.07089,52.55702|-3.00792,52.56902|-2.98028,52.53083|-3.02736,52.49792|-3.11916,52.49194|-3.19514,52.46722|-3.19611,52.41027|-3.02195,52.34027|-2.95486,52.33117|-2.99750,52.28139|-3.05125,52.23347|-3.07555,52.14804|-3.12222,52.11805|-3.11250,52.06945|-3.08500,52.01930|-3.04528,51.97639|-2.98889,51.92555|-2.91757,51.91569|-2.86639,51.92889|-2.77861,51.88583|-2.65944,51.81806|-2.68334,51.76957|-2.68666,51.71889|-2.66500,51.61500|-2.62916,51.64416|-2.57889,51.67777|-2.46056,51.74666|-2.40389,51.74041|-2.47166,51.72445|-2.55305,51.65722|-2.65334,51.56389|-2.77055,51.48916|-2.85278,51.44472|-2.96000,51.37499|-3.00695,51.30722|-3.01278,51.25632|-3.02834,51.20611|-3.30139,51.18111|-3.39361,51.18138|-3.43729,51.20638|-3.50722,51.22333|-3.57014,51.23027|-3.63222,51.21805|-3.70028,51.23000|-3.79250,51.23916|-3.88389,51.22416|-3.98472,51.21695|-4.11666,51.21222|-4.22805,51.18777|-4.22028,51.11054|-4.23702,51.04659|-4.30361,51.00416|-4.37639,50.99110|-4.42736,51.00958|-4.47445,51.01416|-4.52132,51.01424|-4.54334,50.92694|-4.56139,50.77625|-4.65139,50.71527|-4.74361,50.66750'; //|-3.08860,53.26001|-3.33639,53.34722|-3.38806,53.34361|-3.60986,53.27944|-3.73014,53.28944|-3.85445,53.28444|-4.01861,53.23750|-4.06639,53.22639|-4.15334,53.22556|-4.19639,53.20611|-4.33028,53.11222|-4.36097,53.02888|-4.55278,52.92889|-4.61889,52.90916|-4.72195,52.83611|-4.72778,52.78139|-4.53945,52.79306|-4.47722,52.85500|-4.41416,52.88472|-4.31292,52.90499|-4.23334,52.91499|-4.13569,52.87888|-4.13056,52.77777|-4.05334,52.71666|-4.10639,52.65084|-4.12597,52.60375|-4.08056,52.55333|-4.05972,52.48584|-4.09666,52.38583|-4.14305,52.32027|-4.19361,52.27638|-4.23166,52.24888|-4.52722,52.13083|-4.66945,52.13027|-4.73695,52.10361|-4.76778,52.06444|-4.84445,52.01388|-5.09945,51.96056|-5.23916,51.91638|-5.25889,51.87056|-5.18500,51.86958|-5.11528,51.83333|-5.10257,51.77895|-5.16111,51.76222|-5.24694,51.73027|-5.19111,51.70888|-5.00739,51.70349|-4.90875,51.71249|-4.86111,51.71334|-4.97061,51.67577|-5.02128,51.66861|-5.05139,51.62028|-5.00528,51.60638|-4.94139,51.59416|-4.89028,51.62694|-4.83569,51.64534|-4.79063,51.63340|-4.69028,51.66666|-4.64584,51.72666|-4.57445,51.73416|-4.43611,51.73722|-4.26222,51.67694|-4.19750,51.67916|-4.06614,51.66804|-4.11639,51.63416|-4.17750,51.62235|-4.25055,51.62861|-4.29208,51.60743|-4.27778,51.55666|-4.20486,51.53527|-3.94972,51.61278|-3.83792,51.61999|-3.78166,51.56750|-3.75160,51.52931|-3.67194,51.47388|-3.54250,51.39777|-3.40334,51.37972|-3.27097,51.38014|-3.16458,51.40909|-3.15166,51.45305|-3.11875,51.48750|-3.02111,51.52527|-2.95472,51.53972|-2.89278,51.53861|-2.84778,51.54500|-2.71472,51.58083|-2.66500,51.61500|-2.68666,51.71889|-2.68334,51.76957|-2.65944,51.81806|-2.77861,51.88583|-2.86639,51.92889|-2.91757,51.91569|-2.98889,51.92555|-3.04528,51.97639|-3.08500,52.01930|-3.11250,52.06945|-3.12222,52.11805|-3.07555,52.14804|-3.05125,52.23347|-2.99750,52.28139|-2.95486,52.33117|-3.02195,52.34027|-3.19611,52.41027|-3.19514,52.46722|-3.11916,52.49194|-3.02736,52.49792|-2.98028,52.53083|-3.00792,52.56902|-3.07089,52.55702|-3.11750,52.58666|-3.06666,52.63527|-3.01111,52.71166|-3.06806,52.77027|-3.13708,52.79312|-3.13014,52.88486|-3.08639,52.91611|-2.99389,52.95361|-2.85069,52.93875|-2.79278,52.90207|-2.71945,52.91902|-2.73109,52.96873|-2.77792,52.98514|-2.85695,53.03249|-2.89389,53.10416|-2.91069,53.17014|-2.95528,53.21555|-3.02000,53.24722|-3.08860,53.26001';
3529
			// Wales Test
3530
			// var returnGeom2 = '-3.08860,53.26001|-3.33639,53.34722|-3.38806,53.34361|-3.60986,53.27944|-3.73014,53.28944|-3.85445,53.28444|-4.01861,53.23750|-4.06639,53.22639|-4.15334,53.22556|-4.19639,53.20611|-4.33028,53.11222|-4.36097,53.02888|-4.55278,52.92889|-4.61889,52.90916|-4.72195,52.83611|-4.72778,52.78139|-4.53945,52.79306|-4.47722,52.85500|-4.41416,52.88472|-4.31292,52.90499|-4.23334,52.91499|-4.13569,52.87888|-4.13056,52.77777|-4.05334,52.71666|-4.10639,52.65084|-4.12597,52.60375|-4.08056,52.55333|-4.05972,52.48584|-4.09666,52.38583|-4.14305,52.32027|-4.19361,52.27638|-4.23166,52.24888|-4.52722,52.13083|-4.66945,52.13027|-4.73695,52.10361|-4.76778,52.06444|-4.84445,52.01388|-5.09945,51.96056|-5.23916,51.91638|-5.25889,51.87056|-5.18500,51.86958|-5.11528,51.83333|-5.10257,51.77895|-5.16111,51.76222|-5.24694,51.73027|-5.19111,51.70888|-5.00739,51.70349|-4.90875,51.71249|-4.86111,51.71334|-4.97061,51.67577|-5.02128,51.66861|-5.05139,51.62028|-5.00528,51.60638|-4.94139,51.59416|-4.89028,51.62694|-4.83569,51.64534|-4.79063,51.63340|-4.69028,51.66666|-4.64584,51.72666|-4.57445,51.73416|-4.43611,51.73722|-4.26222,51.67694|-4.19750,51.67916|-4.06614,51.66804|-4.11639,51.63416|-4.17750,51.62235|-4.25055,51.62861|-4.29208,51.60743|-4.27778,51.55666|-4.20486,51.53527|-3.94972,51.61278|-3.83792,51.61999|-3.78166,51.56750|-3.75160,51.52931|-3.67194,51.47388|-3.54250,51.39777|-3.40334,51.37972|-3.27097,51.38014|-3.16458,51.40909|-3.15166,51.45305|-3.11875,51.48750|-3.02111,51.52527|-2.95472,51.53972|-2.89278,51.53861|-2.84778,51.54500|-2.71472,51.58083|-2.66500,51.61500|-2.68666,51.71889|-2.68334,51.76957|-2.65944,51.81806|-2.77861,51.88583|-2.86639,51.92889|-2.91757,51.91569|-2.98889,51.92555|-3.04528,51.97639|-3.08500,52.01930|-3.11250,52.06945|-3.12222,52.11805|-3.07555,52.14804|-3.05125,52.23347|-2.99750,52.28139|-2.95486,52.33117|-3.02195,52.34027|-3.19611,52.41027|-3.19514,52.46722|-3.11916,52.49194|-3.02736,52.49792|-2.98028,52.53083|-3.00792,52.56902|-3.07089,52.55702|-3.11750,52.58666|-3.06666,52.63527|-3.01111,52.71166|-3.06806,52.77027|-3.13708,52.79312|-3.13014,52.88486|-3.08639,52.91611|-2.99389,52.95361|-2.85069,52.93875|-2.79278,52.90207|-2.71945,52.91902|-2.73109,52.96873|-2.77792,52.98514|-2.85695,53.03249|-2.89389,53.10416|-2.91069,53.17014|-2.95528,53.21555|-3.02000,53.24722|-3.08860,53.26001';
3531
					num_arrays = 2;
3532
				} else if (pl_name == 'Wales') {
3533
					var returnGeom1 = '-3.08860,53.26001|-3.33639,53.34722|-3.38806,53.34361|-3.60986,53.27944|-3.73014,53.28944|-3.85445,53.28444|-4.01861,53.23750|-4.06639,53.22639|-4.15334,53.22556|-4.19639,53.20611|-4.33028,53.11222|-4.36097,53.02888|-4.55278,52.92889|-4.61889,52.90916|-4.72195,52.83611|-4.72778,52.78139|-4.53945,52.79306|-4.47722,52.85500|-4.41416,52.88472|-4.31292,52.90499|-4.23334,52.91499|-4.13569,52.87888|-4.13056,52.77777|-4.05334,52.71666|-4.10639,52.65084|-4.12597,52.60375|-4.08056,52.55333|-4.05972,52.48584|-4.09666,52.38583|-4.14305,52.32027|-4.19361,52.27638|-4.23166,52.24888|-4.52722,52.13083|-4.66945,52.13027|-4.73695,52.10361|-4.76778,52.06444|-4.84445,52.01388|-5.09945,51.96056|-5.23916,51.91638|-5.25889,51.87056|-5.18500,51.86958|-5.11528,51.83333|-5.10257,51.77895|-5.16111,51.76222|-5.24694,51.73027|-5.19111,51.70888|-5.00739,51.70349|-4.90875,51.71249|-4.86111,51.71334|-4.97061,51.67577|-5.02128,51.66861|-5.05139,51.62028|-5.00528,51.60638|-4.94139,51.59416|-4.89028,51.62694|-4.83569,51.64534|-4.79063,51.63340|-4.69028,51.66666|-4.64584,51.72666|-4.57445,51.73416|-4.43611,51.73722|-4.26222,51.67694|-4.19750,51.67916|-4.06614,51.66804|-4.11639,51.63416|-4.17750,51.62235|-4.25055,51.62861|-4.29208,51.60743|-4.27778,51.55666|-4.20486,51.53527|-3.94972,51.61278|-3.83792,51.61999|-3.78166,51.56750|-3.75160,51.52931|-3.67194,51.47388|-3.54250,51.39777|-3.40334,51.37972|-3.27097,51.38014|-3.16458,51.40909|-3.15166,51.45305|-3.11875,51.48750|-3.02111,51.52527|-2.95472,51.53972|-2.89278,51.53861|-2.84778,51.54500|-2.71472,51.58083|-2.66500,51.61500|-2.68666,51.71889|-2.68334,51.76957|-2.65944,51.81806|-2.77861,51.88583|-2.86639,51.92889|-2.91757,51.91569|-2.98889,51.92555|-3.04528,51.97639|-3.08500,52.01930|-3.11250,52.06945|-3.12222,52.11805|-3.07555,52.14804|-3.05125,52.23347|-2.99750,52.28139|-2.95486,52.33117|-3.02195,52.34027|-3.19611,52.41027|-3.19514,52.46722|-3.11916,52.49194|-3.02736,52.49792|-2.98028,52.53083|-3.00792,52.56902|-3.07089,52.55702|-3.11750,52.58666|-3.06666,52.63527|-3.01111,52.71166|-3.06806,52.77027|-3.13708,52.79312|-3.13014,52.88486|-3.08639,52.91611|-2.99389,52.95361|-2.85069,52.93875|-2.79278,52.90207|-2.71945,52.91902|-2.73109,52.96873|-2.77792,52.98514|-2.85695,53.03249|-2.89389,53.10416|-2.91069,53.17014|-2.95528,53.21555|-3.02000,53.24722|-3.08860,53.26001';
3534
					num_arrays = 1;
3535
				} else if (pl_name == 'Ireland') {
3536
					var returnGeom1 = '-8.17166,54.46388|-8.06555,54.37277|-7.94139,54.29944|-7.87576,54.28499|-7.86834,54.22764|-7.81805,54.19916|-7.69972,54.20250|-7.55945,54.12694|-7.31334,54.11250|-7.14584,54.22527|-7.17555,54.28916|-7.16084,54.33666|-7.05834,54.41000|-6.97445,54.40166|-6.92695,54.37916|-6.87305,54.34208|-6.85111,54.28972|-6.73473,54.18361|-6.65556,54.06527|-6.60584,54.04444|-6.44750,54.05833|-6.33889,54.11555|-6.26697,54.09983|-6.17403,54.07222|-6.10834,54.03638|-6.04389,54.03139|-5.96834,54.06389|-5.88500,54.11639|-5.87347,54.20916|-5.82500,54.23958|-5.74611,54.24806|-5.65556,54.22701|-5.60834,54.24972|-5.55916,54.29084|-5.57334,54.37704|-5.64502,54.49267|-5.70472,54.53361|-5.68055,54.57306|-5.59972,54.54194|-5.55097,54.50083|-5.54216,54.44903|-5.54643,54.40527|-5.50672,54.36444|-5.46111,54.38555|-5.43132,54.48596|-5.47945,54.53638|-5.53521,54.65090|-5.57431,54.67722|-5.62916,54.67945|-5.73674,54.67383|-5.80305,54.66138|-5.88257,54.60652|-5.92445,54.63180|-5.86681,54.68972|-5.81903,54.70972|-5.74672,54.72452|-5.68775,54.76335|-5.70931,54.83166|-5.74694,54.85361|-5.79139,54.85139|-6.03611,55.05778|-6.04250,55.10277|-6.03444,55.15458|-6.10125,55.20945|-6.14584,55.22069|-6.25500,55.21194|-6.37639,55.23916|-6.51556,55.23305|-6.61334,55.20722|-6.73028,55.18027|-6.82472,55.16806|-6.88972,55.16777|-6.96695,55.15611|-6.99416,55.11027|-7.05139,55.04680|-7.09500,55.03694|-7.25251,55.07059|-7.32639,55.04527|-7.40639,54.95333|-7.45805,54.85777|-7.55334,54.76277|-7.73916,54.71054|-7.82576,54.73416|-7.92639,54.70054|-7.85236,54.63388|-7.77750,54.62694|-7.83361,54.55389|-7.95084,54.53222|-8.04695,54.50722|-8.17166,54.46388';
3537
					num_arrays = 1;
3538
				} else if (pl_name == 'NC') {
3539
					var returnGeom1 = '-81.65876,36.60938|-81.70390,36.55513|-81.70639,36.50804|-81.74665,36.39777|-81.90723,36.30804|-82.03195,36.12694|-82.08416,36.10146|-82.12826,36.11020|-82.21500,36.15833|-82.36375,36.11347|-82.43472,36.06013|-82.46236,36.01708|-82.56006,35.96263|-82.60042,35.99638|-82.62308,36.06121|-82.73500,36.01833|-82.84612,35.94944|-82.90451,35.88819|-82.93555,35.83846|-83.16000,35.76236|-83.24222,35.71944|-83.49222,35.57111|-83.56847,35.55861|-83.64416,35.56471|-83.73499,35.56638|-83.88222,35.51791|-83.98361,35.44944|-84.03639,35.35444|-84.04964,35.29117|-84.09042,35.25986|-84.15084,35.25388|-84.20521,35.25722|-84.29284,35.22596|-84.32471,34.98701|-83.09778,35.00027|-82.77722,35.09138|-82.59639,35.14972|-82.37999,35.21500|-82.27362,35.20583|-81.41306,35.17416|-81.05915,35.15333|-80.92666,35.10695|-80.78751,34.95610|-80.79334,34.82555|-79.66777,34.80694|-79.11555,34.34527|-78.57222,33.88166|-78.51806,33.87999|-78.43721,33.89804|-78.23735,33.91986|-78.15389,33.91471|-78.06974,33.89500|-78.02597,33.88936|-77.97611,33.94276|-77.95299,33.99243|-77.94499,34.06499|-77.92728,34.11756|-77.92250,33.99194|-77.92264,33.93715|-77.88215,34.06166|-77.86222,34.15083|-77.83501,34.19194|-77.75724,34.28527|-77.68222,34.36555|-77.63667,34.39805|-77.57363,34.43694|-77.45527,34.50403|-77.38173,34.51646|-77.37905,34.56294|-77.38572,34.61260|-77.40944,34.68916|-77.38847,34.73304|-77.33097,34.63992|-77.35024,34.60099|-77.30958,34.55972|-77.09424,34.67742|-76.75994,34.76659|-76.68325,34.79749|-76.66097,34.75781|-76.62611,34.71014|-76.50063,34.73617|-76.48138,34.77638|-76.38305,34.86423|-76.34326,34.88194|-76.27181,34.96263|-76.35125,35.02221|-76.32354,34.97429|-76.45319,34.93524|-76.43395,34.98782|-76.45356,35.06676|-76.52917,35.00444|-76.63382,34.98242|-76.69722,34.94887|-76.75306,34.90526|-76.81636,34.93944|-76.89000,34.95388|-76.93180,34.96957|-76.96501,34.99777|-77.06816,35.14978|-76.97639,35.06806|-76.86722,35.00000|-76.80531,34.98559|-76.72708,35.00152|-76.60402,35.07416|-76.56555,35.11486|-76.57305,35.16013|-76.66489,35.16694|-76.56361,35.23361|-76.48750,35.22582|-76.46889,35.27166|-76.50298,35.30791|-76.83251,35.39222|-77.02305,35.48694|-77.04958,35.52694|-76.91292,35.46166|-76.65250,35.41499|-76.61611,35.45888|-76.63195,35.52249|-76.58820,35.55104|-76.51556,35.53194|-76.56711,35.48494|-76.52251,35.40416|-76.46195,35.37221|-76.13319,35.35986|-76.04111,35.42416|-76.00223,35.46610|-75.97958,35.51666|-75.89362,35.57555|-75.83834,35.56694|-75.78944,35.57138|-75.74076,35.61846|-75.72084,35.69263|-75.72084,35.81451|-75.74917,35.87791|-75.78333,35.91972|-75.85083,35.97527|-75.94333,35.91777|-75.98944,35.88054|-75.98854,35.79110|-75.99388,35.71027|-76.02875,35.65409|-76.10320,35.66041|-76.13563,35.69239|-76.04475,35.68436|-76.04167,35.74916|-76.05305,35.79361|-76.05305,35.87375|-76.02653,35.96222|-76.07751,35.99319|-76.17472,35.99596|-76.27917,35.91915|-76.37986,35.95763|-76.42014,35.97874|-76.55375,35.93971|-76.66222,35.93305|-76.72952,35.93984|-76.73392,36.04760|-76.75384,36.09477|-76.76028,36.14513|-76.74610,36.22818|-76.70458,36.24673|-76.72764,36.16736|-76.71021,36.11752|-76.69117,36.07165|-76.65979,36.03312|-76.49527,36.00958|-76.37138,36.07694|-76.37084,36.14999|-76.21417,36.09471|-76.07591,36.17910|-76.18361,36.26915|-76.19965,36.31739|-76.13986,36.28805|-76.04274,36.21974|-76.00465,36.18110|-75.95287,36.19241|-75.97604,36.31138|-75.93895,36.28381|-75.85271,36.11069|-75.79315,36.07385|-75.79639,36.11804|-75.88333,36.29554|-75.94665,36.37194|-75.98694,36.41166|-76.03473,36.49666|-76.02899,36.55000|-78.44234,36.54986|-78.56594,36.55799|-80.27556,36.55110|-81.15361,36.56499|-81.38722,36.57695|-81.65876,36.60938';
3540
					num_arrays = 1;
3541
				} else {
3542
					// show nothing
3543
				}
3544
3545
				// If showing one country only (num_arrays == 1)
3546
				// Calculate polygon
3547
				if (num_arrays == 1 ) {
3548
					var geomAry1 = returnGeom1.split('|');
3549
					var XY1 = [];
3550
					var points1 = [];
3551
					for (var i = 0; i < geomAry1.length; i++) {
3552
						XY1 = geomAry1[i].split(',');
3553
						points1.push( new google.maps.LatLng(parseFloat(XY1[1]),parseFloat(XY1[0]))) ;
3554
					}
3555
					// Construct the polygon
3556
					polygon1 = new google.maps.Polygon({
3557
						paths: points1,
3558
						strokeColor: "#888888",
3559
						strokeOpacity: 0.8,
3560
						strokeWeight: 1,
3561
						fillColor: "#ff0000",
3562
						fillOpacity: 0.15
3563
					});
3564
					polygon1.setMap(map);
3565
				}
3566
3567
				// If showing two countries at the same time (num_arrays == 2)
3568
				if (num_arrays == 2) {
3569
					// Calculate polygon1
3570
					var geomAry1 = returnGeom1.split('|');
3571
					var XY1 = [];
3572
					var points1 = [];
3573
					for (var i = 0; i < geomAry1.length; i++) {
3574
						XY1 = geomAry1[i].split(',');
3575
						points1.push( new google.maps.LatLng(parseFloat(XY1[1]),parseFloat(XY1[0]))) ;
3576
					}
3577
3578
					// Construct polygon1
3579
					polygon1 = new google.maps.Polygon({
3580
						paths: points1,
3581
						strokeColor: "#888888",
3582
						strokeOpacity: 0.8,
3583
						strokeWeight: 1,
3584
						fillColor: "#ff0000",
3585
						fillOpacity: 0.15
3586
					});
3587
					polygon1.setMap(map);
3588
3589
					// Calculate polygon2
3590
					var geomAry2 = returnGeom2.split('|');
3591
					var XY2 = [];
3592
					var points2 = [];
3593
					for (var i = 0; i < geomAry2.length; i++) {
3594
						XY2 = geomAry2[i].split(',');
3595
						points2.push( new google.maps.LatLng(parseFloat(XY2[1]),parseFloat(XY2[0]))) ;
3596
					}
3597
3598
					// Construct polygon2
3599
					polygon2 = new google.maps.Polygon({
3600
						paths: points2,
3601
						strokeColor: "#888888",
3602
						strokeOpacity: 0.8,
3603
						strokeWeight: 1,
3604
						fillColor: "#ff0000",
3605
						fillOpacity: 0.15
3606
					});
3607
					polygon2.setMap(map);
3608
				}
3609
			}
3610
3611
			// The HomeControl returns user to original position and style =================
3612
			function HomeControl(controlDiv, map) {
3613
				// Set CSS styles for the DIV containing the control
3614
				// Setting padding to 5 px will offset the control from the edge of the map
3615
				controlDiv.style.paddingTop = '5px';
3616
				controlDiv.style.paddingRight = '0px';
3617
3618
				// Set CSS for the control border
3619
				var controlUI = document.createElement('DIV');
3620
				controlUI.style.backgroundColor = 'white';
3621
				controlUI.style.color = 'black';
3622
				controlUI.style.borderColor = 'black';
3623
				controlUI.style.borderColor = 'black';
3624
				controlUI.style.borderStyle = 'solid';
3625
				controlUI.style.borderWidth = '2px';
3626
				controlUI.style.cursor = 'pointer';
3627
				controlUI.style.textAlign = 'center';
3628
				controlUI.title = '';
3629
				controlDiv.appendChild(controlUI);
3630
3631
				// Set CSS for the control interior
3632
				var controlText = document.createElement('DIV');
3633
				controlText.style.fontFamily = 'Arial,sans-serif';
3634
				controlText.style.fontSize = '12px';
3635
				controlText.style.paddingLeft = '15px';
3636
				controlText.style.paddingRight = '15px';
3637
				controlText.innerHTML = '<b><?php echo I18N::translate('Redraw map') ?><\/b>';
3638
				controlUI.appendChild(controlText);
3639
3640
				// Setup the click event listeners: simply set the map to original LatLng
3641
				google.maps.event.addDomListener(controlUI, 'click', function() {
3642
					map.setCenter(latlng);
3643
					map.setZoom(pl_zoom);
3644
					map.setMapTypeId(google.maps.MapTypeId.ROADMAP);
3645
				});
3646
			}
3647
3648
			function loadMap(zoom, mapType) {
3649
				var mapTyp;
3650
3651
				if (mapType) {
3652
					mapTyp = mapType;
3653
				} else {
3654
					mapTyp = google.maps.MapTypeId.ROADMAP;
3655
				}
3656
				geocoder = new google.maps.Geocoder();
3657
				if (!zoom) {
3658
					zoom = pl_zoom;
3659
				}
3660
				// Define map
3661
				var myOptions = {
3662
					zoom: zoom,
3663
					center: latlng,
3664
					mapTypeId: mapTyp,// ROADMAP, SATELLITE, HYBRID, TERRAIN
3665
					// mapTypeId: google.maps.MapTypeId.ROADMAP, // ROADMAP, SATELLITE, HYBRID, TERRAIN
3666
					mapTypeControlOptions: {
3667
						style: google.maps.MapTypeControlStyle.DROPDOWN_MENU // DEFAULT, DROPDOWN_MENU, HORIZONTAL_BAR
3668
					},
3669
					navigationControlOptions: {
3670
					position: google.maps.ControlPosition.TOP_RIGHT, // BOTTOM, BOTTOM_LEFT, LEFT, TOP, etc
3671
					style: google.maps.NavigationControlStyle.SMALL // ANDROID, DEFAULT, SMALL, ZOOM_PAN
3672
					},
3673
					streetViewControl: false, // Show Pegman or not
3674
					scrollwheel: true
3675
				};
3676
3677
				map = new google.maps.Map(document.getElementById('map_pane'), myOptions);
3678
3679
				overlays();
3680
3681
				// Close any infowindow when map is clicked
3682
				google.maps.event.addListener(map, 'click', function() {
3683
					infowindow.close();
3684
				});
3685
3686
				// Create the DIV to hold the control and call HomeControl() passing in this DIV. --
3687
				var homeControlDiv = document.createElement('DIV');
3688
				var homeControl = new HomeControl(homeControlDiv, map);
3689
				homeControlDiv.index = 1;
3690
				map.controls[google.maps.ControlPosition.TOP_RIGHT].push(homeControlDiv);
3691
				// ---------------------------------------------------------------------------------
3692
3693
				// Check for zoom changes
3694
				google.maps.event.addListener(map, 'zoom_changed', function() {
3695
					document.editplaces.NEW_ZOOM_FACTOR.value = map.zoom;
3696
				});
3697
3698
				// Create the Main Location Marker
3699
				<?php
3700
				if ($level < 3 && $place_icon != '') {
3701
					echo 'var image = new google.maps.MarkerImage("', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/', $place_icon, '",';
0 ignored issues
show
Bug introduced by
The variable $place_icon does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3702
						echo 'new google.maps.Size(25, 15),'; // Image size
3703
						echo 'new google.maps.Point(0, 0),'; // Image origin
3704
						echo 'new google.maps.Point(12, 15)'; // Image anchor
3705
					echo ');';
3706
					echo 'var iconShadow = new google.maps.MarkerImage("', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/flag_shadow.png",';
3707
						echo 'new google.maps.Size(35, 45),'; // Shadow size
3708
						echo 'new google.maps.Point(0,0),'; // Shadow origin
3709
						echo 'new google.maps.Point(1, 45)'; // Shadow anchor is base of flagpole
3710
					echo ');';
3711
					echo 'marker = new google.maps.Marker({';
3712
						echo 'icon: image,';
3713
						echo 'shadow: iconShadow,';
3714
						echo 'position: latlng,';
3715
						echo 'map: map,';
3716
						echo 'title: pl_name,';
3717
						echo 'draggable: true,';
3718
						echo 'zIndex:1';
3719
					echo '});';
3720
				} else {
3721
					echo 'marker = new google.maps.Marker({';
3722
						echo 'position: latlng,';
3723
						echo 'map: map,';
3724
						echo 'title: pl_name,';
3725
						echo 'draggable: true,';
3726
						echo 'zIndex: 1';
3727
					echo '});';
3728
				}
3729
				?>
3730
3731
				var prec = 20;
3732
				for (var i=0;i<document.editplaces.NEW_PRECISION.length;i++) {
3733
					if (document.editplaces.NEW_PRECISION[i].checked) {
3734
						prec = document.editplaces.NEW_PRECISION[i].value;
3735
					}
3736
				}
3737
3738
				// Set marker by clicking on map ---
3739
				google.maps.event.addListener(map, 'click', function(event) {
3740
					clearMarks();
3741
					latlng = event.latLng;
3742
					<?php
3743
						echo 'marker = new google.maps.Marker({';
3744
						echo 'position: latlng,';
3745
						echo 'map: map,';
3746
						echo 'title: pl_name,';
3747
						echo 'draggable: true,';
3748
						echo 'zIndex: 1';
3749
					echo '});';
3750
					?>
3751
					var pos3 = marker.getPosition();
3752
					document.getElementById('NEW_PLACE_LATI').value = parseFloat(pos3.lat()).toFixed(prec);
3753
					document.getElementById('NEW_PLACE_LONG').value = parseFloat(pos3.lng()).toFixed(prec);
3754
					updateMap('flag_drag');
3755
					var currzoom = parseInt(document.editplaces.NEW_ZOOM_FACTOR.value);
3756
					mapType = map.getMapTypeId();
3757
					loadMap(currzoom, mapType);
3758
				});
3759
3760
				// Set marker by drag-n-drop on map ---
3761
				google.maps.event.addListener(marker, 'drag', function() {
3762
					var pos1 = marker.getPosition();
3763
					document.getElementById('NEW_PLACE_LATI').value = parseFloat(pos1.lat()).toFixed(prec);
3764
					document.getElementById('NEW_PLACE_LONG').value = parseFloat(pos1.lng()).toFixed(prec);
3765
				});
3766
				google.maps.event.addListener(marker, 'dragend', function() {
3767
					var pos2 = marker.getPosition();
3768
					document.getElementById('NEW_PLACE_LATI').value = parseFloat(pos2.lat()).toFixed(prec);
3769
					document.getElementById('NEW_PLACE_LONG').value = parseFloat(pos2.lng()).toFixed(prec);
3770
					updateMap('flag_drag');
3771
				});
3772
			}
3773
3774
			function clearMarks() {
3775
				marker.setMap(null);
3776
			}
3777
3778
			function setLoc(lat, lng) {
3779
				var prec = 20;
3780
				for (var i=0;i<document.editplaces.NEW_PRECISION.length;i++) {
3781
					if (document.editplaces.NEW_PRECISION[i].checked) {
3782
						prec = document.editplaces.NEW_PRECISION[i].value;
3783
					}
3784
				}
3785
				if (lat < 0.0) {
3786
					document.editplaces.NEW_PLACE_LATI.value = (lat.toFixed(prec) * -1);
3787
					document.editplaces.LATI_CONTROL.value = 'PL_S';
3788
				} else {
3789
					document.editplaces.NEW_PLACE_LATI.value = lat.toFixed(prec);
3790
					document.editplaces.LATI_CONTROL.value = 'PL_N';
3791
				}
3792
				if (lng < 0.0) {
3793
					document.editplaces.NEW_PLACE_LONG.value = (lng.toFixed(prec) * -1);
3794
					document.editplaces.LONG_CONTROL.value = 'PL_W';
3795
				} else {
3796
					document.editplaces.NEW_PLACE_LONG.value = lng.toFixed(prec);
3797
					document.editplaces.LONG_CONTROL.value = 'PL_E';
3798
				}
3799
				new google.maps.LatLng (lat.toFixed(prec), lng.toFixed(prec));
3800
				updateMap();
3801
			}
3802
3803
			function createMarker(i, point, name) {
3804
				var contentString = '<div id="iwcontent_edit">'+name+'<\/div>';
3805
				<?php
3806
				echo 'var image = new google.maps.MarkerImage("', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/marker_yellow.png",';
3807
					echo 'new google.maps.Size(20, 34),'; // Image size
3808
					echo 'new google.maps.Point(0, 0),'; // Image origin
3809
					echo 'new google.maps.Point(10, 34)'; // Image anchor
3810
				echo ');';
3811
				echo 'var iconShadow = new google.maps.MarkerImage("', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/shadow50.png",';
3812
					echo 'new google.maps.Size(37, 34),'; // Shadow size
3813
					echo 'new google.maps.Point(0, 0),'; // Shadow origin
3814
					echo 'new google.maps.Point(10, 34)'; // Shadow anchor is base of image
3815
				echo ');';
3816
				?>
3817
				var marker = new google.maps.Marker({
3818
					icon: image,
3819
					shadow: iconShadow,
3820
					map: map,
3821
					position: point,
3822
					zIndex: 0
3823
				});
3824
3825
				google.maps.event.addListener(marker, 'click', function() {
3826
					infowindow.close();
3827
					infowindow.setContent(contentString);
3828
					infowindow.open(map, marker);
3829
				});
3830
3831
				google.maps.event.addListener(map, 'click', function() {
3832
					infowindow.close();
3833
				});
3834
3835
				return marker;
3836
			}
3837
3838
			function change_icon() {
3839
				window.open('module.php?mod=googlemap&mod_action=flags&countrySelected=<?php echo $selected_country ?>', '_blank', indx_window_specs);
0 ignored issues
show
Bug introduced by
The variable $selected_country does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3840
				return false;
3841
			}
3842
3843
			function remove_icon() {
3844
				document.editplaces.icon.value = '';
3845
				document.getElementById('flagsDiv').innerHTML = '<a href="#" onclick="change_icon();return false;"><?php echo I18N::translate('Change flag') ?></a>';
3846
			}
3847
3848
			function addAddressToMap(response) {
3849
				var bounds = new google.maps.LatLngBounds();
3850
				if (!response ) {
3851
					alert('<?php echo I18N::translate('No places found') ?>');
3852
				} else {
3853
					if (response.length > 0) {
3854
						for (var i=0; i<response.length; i++) {
3855
							// 5 decimal places is approx 1 metre accuracy.
3856
							var name  = '<div id="gname" class="iwstyle">'+response[i].address_components[0].short_name+'<br>('+response[i].geometry.location.lng().toFixed(5)+','+response[i].geometry.location.lat().toFixed(5)+'';
3857
								name += '<br><a href="#" onclick="setLoc(' + response[i].geometry.location.lat() + ', ' + response[i].geometry.location.lng() + ');"><div id="namelink"><?php echo I18N::translate('Use this value') ?></div></a>';
3858
								name += '</div>';
3859
							var point = response[i].geometry.location;
3860
							var marker = createMarker(i, point, name);
3861
							bounds.extend(response[i].geometry.location);
3862
						}
3863
3864
						<?php if ($level > 0) { ?>
3865
							map.fitBounds(bounds);
3866
						<?php } ?>
3867
						var zoomlevel = map.getZoom();
3868
3869
						if (zoomlevel < <?php echo $this->getSetting('GM_MIN_ZOOM') ?>) {
3870
							zoomlevel = <?php echo $this->getSetting('GM_MIN_ZOOM') ?>;
3871
						}
3872
						if (zoomlevel > <?php echo $this->getSetting('GM_MAX_ZOOM') ?>) {
3873
							zoomlevel = <?php echo $this->getSetting('GM_MAX_ZOOM') ?>;
3874
						}
3875
						if (document.editplaces.NEW_ZOOM_FACTOR.value < zoomlevel) {
3876
							zoomlevel = document.editplaces.NEW_ZOOM_FACTOR.value;
3877
							if (zoomlevel < <?php echo $this->getSetting('GM_MIN_ZOOM') ?>) {
3878
								zoomlevel = <?php echo $this->getSetting('GM_MIN_ZOOM') ?>;
3879
							}
3880
							if (zoomlevel > <?php echo $this->getSetting('GM_MAX_ZOOM') ?>) {
3881
								zoomlevel = <?php echo $this->getSetting('GM_MAX_ZOOM') ?>;
3882
							}
3883
						}
3884
						map.setCenter(bounds.getCenter());
3885
						map.setZoom(zoomlevel);
3886
					}
3887
				}
3888
			}
3889
3890
			function showLocation_level(address) {
3891
				address += '<?php if ($level > 0) echo ', ', addslashes(implode(', ', array_reverse($where_am_i, true))) ?>';
3892
				geocoder.geocode({'address': address}, addAddressToMap);
3893
			}
3894
3895
			function showLocation_all(address) {
3896
				geocoder.geocode({'address': address}, addAddressToMap);
3897
			}
3898
3899
			function paste_char(value) {
3900
				document.editplaces.NEW_PLACE_NAME.value += value;
3901
			}
3902
			window.onload = function() { loadMap(); };
3903
		</script>
3904
		<form method="post" id="editplaces" name="editplaces" action="module.php?mod=googlemap&amp;mod_action=places_edit">
3905
			<input type="hidden" name="action" value="<?php echo $action ?>record">
3906
			<input type="hidden" name="placeid" value="<?php echo $placeid ?>">
3907
			<input type="hidden" name="level" value="<?php echo $level ?>">
3908
			<input type="hidden" name="icon" value="<?php echo $place_icon ?>">
3909
			<input type="hidden" name="parent_id" value="<?php echo $parent_id ?>">
0 ignored issues
show
Bug introduced by
The variable $parent_id does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3910
			<input type="hidden" name="place_long" value="<?php echo $place_long ?>">
3911
			<input type="hidden" name="place_lati" value="<?php echo $place_lati ?>">
3912
			<input type="hidden" name="parent_long" value="<?php echo $parent_long ?>">
3913
			<input type="hidden" name="parent_lati" value="<?php echo $parent_lati ?>">
3914
3915
			<table class="facts_table">
3916
			<tr>
3917
				<td class="optionbox" colspan="3">
3918
					<div id="map_pane" style="width: 100%; height: 300px;"></div>
3919
				</td>
3920
			</tr>
3921
			<tr>
3922
				<td class="descriptionbox"><?php echo GedcomTag::getLabel('PLAC') ?></td>
3923
				<td class="optionbox"><input type="text" id="new_pl_name" name="NEW_PLACE_NAME" value="<?php echo Filter::escapeHtml($place_name) ?>" size="25" class="address_input">
3924
					<div id="INDI_PLAC_pop" style="display: inline;">
3925
					<?php echo FunctionsPrint::printSpecialCharacterLink('new_pl_name') ?></div></td><td class="optionbox">
3926
					<label for="new_pl_name"><a href="#" onclick="showLocation_all(document.getElementById('new_pl_name').value); return false"><?php echo I18N::translate('Search globally') ?></a></label>
3927
					|
3928
					<label for="new_pl_name"><a href="#" onclick="showLocation_level(document.getElementById('new_pl_name').value); return false"><?php echo I18N::translate('Search locally') ?></a></label>
3929
				</td>
3930
			</tr>
3931
			<tr>
3932
				<td class="descriptionbox">
3933
					<?php echo I18N::translate('Precision') ?>
3934
				</td>
3935
				<?php
3936
					$exp = explode(".", $place_lati);
3937
					if (isset($exp[1])) {
3938
						$precision1 = strlen($exp[1]);
3939
					} else {
3940
						$precision1 = -1;
3941
					}
3942
					$exp = explode(".", $place_long);
3943
					if (isset($exp[1])) {
3944
						$precision2 = strlen($exp[1]);
3945
					} else {
3946
						$precision2 = -1;
3947
					}
3948
					($precision1 > $precision2) ? ($precision = $precision1) : ($precision = $precision2);
3949
					if ($precision == -1) ($level > 3) ? ($precision = 3) : ($precision = $level);
3950
					elseif ($precision > 5) {
3951
						$precision = 5;
3952
					}
3953
				?>
3954
				<td class="optionbox" colspan="2">
3955
					<input type="radio" id="new_prec_0" name="NEW_PRECISION" onchange="updateMap();" <?php if ($precision == $this->getSetting('GM_PRECISION_0')) echo 'checked' ?> value="<?php echo $this->getSetting('GM_PRECISION_0') ?>">
3956
					<label for="new_prec_0"><?php echo I18N::translate('Country') ?></label>
3957
					<input type="radio" id="new_prec_1" name="NEW_PRECISION" onchange="updateMap();" <?php if ($precision == $this->getSetting('GM_PRECISION_1')) echo 'checked' ?> value="<?php echo $this->getSetting('GM_PRECISION_1') ?>">
3958
					<label for="new_prec_1"><?php echo I18N::translate('State') ?></label>
3959
					<input type="radio" id="new_prec_2" name="NEW_PRECISION" onchange="updateMap();" <?php if ($precision == $this->getSetting('GM_PRECISION_2')) echo 'checked' ?> value="<?php echo $this->getSetting('GM_PRECISION_2') ?>">
3960
					<label for="new_prec_2"><?php echo I18N::translate('City') ?></label>
3961
					<input type="radio" id="new_prec_3" name="NEW_PRECISION" onchange="updateMap();" <?php if ($precision == $this->getSetting('GM_PRECISION_3')) echo 'checked' ?> value="<?php echo $this->getSetting('GM_PRECISION_3') ?>">
3962
					<label for="new_prec_3"><?php echo I18N::translate('Neighborhood') ?></label>
3963
					<input type="radio" id="new_prec_4" name="NEW_PRECISION" onchange="updateMap();" <?php if ($precision == $this->getSetting('GM_PRECISION_4')) echo 'checked' ?> value="<?php echo $this->getSetting('GM_PRECISION_4') ?>">
3964
					<label for="new_prec_4"><?php echo I18N::translate('House') ?></label>
3965
					<input type="radio" id="new_prec_5" name="NEW_PRECISION" onchange="updateMap();" <?php if ($precision >= $this->getSetting('GM_PRECISION_5')) echo 'checked' ?> value="<?php echo $this->getSetting('GM_PRECISION_5') ?>">
3966
					<label for="new_prec_5"><?php echo I18N::translate('Max') ?></label>
3967
					<p class="small text-muted">
3968
						<?php echo I18N::translate('Here you can enter the precision. Based on this setting the number of digits that will be used in the latitude and longitude is determined.') ?>
3969
					</p>
3970
				</td>
3971
			</tr>
3972
			<tr>
3973
				<td class="descriptionbox"><?php echo GedcomTag::getLabel('LATI') ?></td>
3974
				<td class="optionbox" colspan="2">
3975
					<input type="text" id="NEW_PLACE_LATI" name="NEW_PLACE_LATI" placeholder="<?php echo /* I18N: Measure of latitude/longitude */ I18N::translate('degrees') ?>" value="<?php echo abs($place_lati) ?>" size="20" onchange="updateMap();">
3976
					<select name="LATI_CONTROL" onchange="updateMap();">
3977
						<option value="PL_N" <?php if ($place_lati >= 0) echo "selected"; echo ">", I18N::translate('north') ?></option>
3978
						<option value="PL_S" <?php if ($place_lati < 0) echo "selected"; echo ">", I18N::translate('south') ?></option>
3979
					</select>
3980
				</td>
3981
			</tr>
3982
			<tr>
3983
				<td class="descriptionbox"><?php echo GedcomTag::getLabel('LONG') ?></td>
3984
				<td class="optionbox" colspan="2">
3985
					<input type="text" id="NEW_PLACE_LONG" name="NEW_PLACE_LONG" placeholder="<?php echo I18N::translate('degrees') ?>" value="<?php echo abs($place_long) ?>" size="20" onchange="updateMap();">
3986
					<select name="LONG_CONTROL" onchange="updateMap();">
3987
						<option value="PL_E" <?php if ($place_long >= 0) echo "selected"; echo ">", I18N::translate('east') ?></option>
3988
						<option value="PL_W" <?php if ($place_long < 0) echo "selected"; echo ">", I18N::translate('west') ?></option>
3989
					</select>
3990
				</td>
3991
			</tr>
3992
			<tr>
3993
				<td class="descriptionbox">
3994
					<?php echo I18N::translate('Zoom level') ?>
3995
				</td>
3996
				<td class="optionbox" colspan="2">
3997
					<input type="text" id="NEW_ZOOM_FACTOR" name="NEW_ZOOM_FACTOR" value="<?php echo $zoomfactor ?>" size="20" onchange="updateMap();">
3998
					<p class="small text-muted">
3999
						<?php echo I18N::translate('Here the zoom level can be entered. This value will be used as the minimal value when displaying this geographic location on a map.') ?>
4000
					</p>
4001
				</td>
4002
			</tr>
4003
			<tr>
4004
				<td class="descriptionbox">
4005
					<?php echo I18N::translate('Flag') ?>
4006
				</td>
4007
				<td class="optionbox" colspan="2">
4008
					<div id="flagsDiv">
4009
						<?php if ($place_icon) { ?>
4010
						<img alt="<?php echo /* I18N: The emblem of a country or region */ I18N::translate('Flag') ?>" src="<?php echo WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/', $place_icon ?>">
4011
						<a href="#" onclick="change_icon();return false;"><?php echo I18N::translate('Change flag') ?></a>
4012
						<a href="#" onclick="remove_icon();return false;"><?php echo I18N::translate('Remove flag') ?></a>
4013
						<?php } else { ?>
4014
						<a href="#" onclick="change_icon();return false;"><?php echo I18N::translate('Change flag') ?></a>
4015
						<?php } ?>
4016
					</div>
4017
					<p class="small text-muted">
4018
						<?php echo I18N::translate('Here an icon can be set or removed. Using this link a flag can be selected. When this geographic location is shown, this flag will be displayed.') ?>
4019
					</p>
4020
				</td>
4021
			</tr>
4022
			</table>
4023
			<p id="save-cancel">
4024
				<input type="submit" class="save" value="<?php echo I18N::translate('save') ?>">
4025
				<input type="button" class="cancel" value="<?php echo I18N::translate('close') ?>" onclick="window.close();">
4026
			</p>
4027
		</form>
4028
		<?php
4029
	}
4030
4031
	/**
4032
	 * Places administration.
4033
	 */
4034
	private function adminPlaces() {
0 ignored issues
show
Coding Style introduced by
adminPlaces uses the super-global variable $_FILES which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
adminPlaces uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
adminPlaces uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
4035
		global $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
4036
4037
		$action       = Filter::get('action');
4038
		$parent       = Filter::get('parent');
4039
		$inactive     = Filter::getBool('inactive');
4040
		$deleteRecord = Filter::get('deleteRecord');
4041
4042
		if (!isset($parent)) {
4043
			$parent = 0;
4044
		}
4045
4046
		$controller = new PageController;
4047
		$controller->restrictAccess(Auth::isAdmin());
4048
4049
		if ($action == 'ExportFile' && Auth::isAdmin()) {
4050
			$tmp                         = $this->placeIdToHierarchy($parent);
4051
			$maxLevel                    = $this->getHighestLevel();
4052
			if ($maxLevel > 8) {
4053
				$maxLevel = 8;
4054
			}
4055
			$tmp[0]                      = 'places';
4056
			$outputFileName              = preg_replace('/[:;\/\\\(\)\{\}\[\] $]/', '_', implode('-', $tmp)) . '.csv';
4057
			header('Content-Type: application/octet-stream');
4058
			header('Content-Disposition: attachment; filename="' . $outputFileName . '"');
4059
			echo '"', I18N::translate('Level'), '";"', I18N::translate('Country'), '";';
4060
			if ($maxLevel > 0) {
4061
				echo '"', I18N::translate('State'), '";';
4062
			}
4063
			if ($maxLevel > 1) {
4064
				echo '"', I18N::translate('County'), '";';
4065
			}
4066
			if ($maxLevel > 2) {
4067
				echo '"', I18N::translate('City'), '";';
4068
			}
4069
			if ($maxLevel > 3) {
4070
				echo '"', I18N::translate('Place'), '";';
4071
			}
4072
			if ($maxLevel > 4) {
4073
				echo '"', I18N::translate('Place'), '";';
4074
			}
4075
			if ($maxLevel > 5) {
4076
				echo '"', I18N::translate('Place'), '";';
4077
			}
4078
			if ($maxLevel > 6) {
4079
				echo '"', I18N::translate('Place'), '";';
4080
			}
4081
			if ($maxLevel > 7) {
4082
				echo '"', I18N::translate('Place'), '";';
4083
			}
4084
			echo '"', I18N::translate('Longitude'), '";"', I18N::translate('Latitude'), '";';
4085
			echo '"', I18N::translate('Zoom level'), '";"', I18N::translate('Icon'), '";', WT_EOL;
4086
			$this->outputLevel($parent);
4087
4088
			return;
4089
		}
4090
4091
		$controller
4092
			->setPageTitle(I18N::translate('Google Maps™'))
4093
			->pageHeader();
4094
4095
		?>
4096
		<ol class="breadcrumb small">
4097
			<li><a href="admin.php"><?php echo I18N::translate('Control panel') ?></a></li>
4098
			<li><a href="admin_modules.php"><?php echo I18N::translate('Module administration') ?></a></li>
4099
			<li class="active"><?php echo $controller->getPageTitle() ?></li>
4100
		</ol>
4101
4102
		<ul class="nav nav-tabs nav-justified" role="tablist">
4103
			<li role="presentation">
4104
				<a href="?mod=googlemap&amp;mod_action=admin_config" role="tab">
4105
					<?php echo I18N::translate('Google Maps™ preferences') ?>
4106
				</a>
4107
			</li>
4108
			<li role="presentation" class="active">
4109
				<a href="#">
4110
					<?php echo I18N::translate('Geographic data') ?>
4111
				</a>
4112
			</li>
4113
			<li role="presentation">
4114
				<a href="?mod=googlemap&amp;mod_action=admin_placecheck">
4115
					<?php echo I18N::translate('Place check') ?>
4116
				</a>
4117
			</li>
4118
		</ul>
4119
4120
		<h2><?php echo I18N::translate('Geographic data') ?></h2>
4121
		<?php
4122
4123
		if ($action == 'ImportGedcom') {
4124
			$placelist      = array();
4125
			$j              = 0;
4126
			$gedcom_records =
4127
				Database::prepare("SELECT i_gedcom FROM `##individuals` WHERE i_file=? UNION ALL SELECT f_gedcom FROM `##families` WHERE f_file=?")
4128
				->execute(array($WT_TREE->getTreeId(), $WT_TREE->getTreeId()))
4129
				->fetchOneColumn();
4130
			foreach ($gedcom_records as $gedrec) {
4131
				$i        = 1;
4132
				$placerec = Functions::getSubRecord(2, '2 PLAC', $gedrec, $i);
4133
				while (!empty($placerec)) {
4134
					if (preg_match("/2 PLAC (.+)/", $placerec, $match)) {
4135
						$placelist[$j]          = array();
4136
						$placelist[$j]['place'] = trim($match[1]);
4137 View Code Duplication
						if (preg_match("/4 LATI (.*)/", $placerec, $match)) {
4138
							$placelist[$j]['lati'] = trim($match[1]);
4139
							if (($placelist[$j]['lati'][0] != 'N') && ($placelist[$j]['lati'][0] != 'S')) {
4140
								if ($placelist[$j]['lati'] < 0) {
4141
									$placelist[$j]['lati'][0] = 'S';
4142
								} else {
4143
									$placelist[$j]['lati'] = 'N' . $placelist[$j]['lati'];
4144
								}
4145
							}
4146
						} else {
4147
							$placelist[$j]['lati'] = null;
4148
						}
4149 View Code Duplication
						if (preg_match("/4 LONG (.*)/", $placerec, $match)) {
4150
							$placelist[$j]['long'] = trim($match[1]);
4151
							if (($placelist[$j]['long'][0] != 'E') && ($placelist[$j]['long'][0] != 'W')) {
4152
								if ($placelist[$j]['long'] < 0) {
4153
									$placelist[$j]['long'][0] = 'W';
4154
								} else {
4155
									$placelist[$j]['long'] = 'E' . $placelist[$j]['long'];
4156
								}
4157
							}
4158
						} else {
4159
							$placelist[$j]['long'] = null;
4160
						}
4161
						$j                          = $j + 1;
4162
					}
4163
					$i        = $i + 1;
4164
					$placerec = Functions::getSubRecord(2, '2 PLAC', $gedrec, $i);
4165
				}
4166
			}
4167
			asort($placelist);
4168
4169
			$prevPlace     = '';
4170
			$prevLati      = '';
4171
			$prevLong      = '';
4172
			$placelistUniq = array();
4173
			$j             = 0;
4174
			foreach ($placelist as $k => $place) {
4175
				if ($place['place'] != $prevPlace) {
4176
					$placelistUniq[$j]          = array();
4177
					$placelistUniq[$j]['place'] = $place['place'];
4178
					$placelistUniq[$j]['lati']  = $place['lati'];
4179
					$placelistUniq[$j]['long']  = $place['long'];
4180
					$j                          = $j + 1;
4181
				} elseif (($place['place'] == $prevPlace) && (($place['lati'] != $prevLati) || ($place['long'] != $prevLong))) {
4182
					if (($placelistUniq[$j - 1]['lati'] == 0) || ($placelistUniq[$j - 1]['long'] == 0)) {
4183
						$placelistUniq[$j - 1]['lati'] = $place['lati'];
4184
						$placelistUniq[$j - 1]['long'] = $place['long'];
4185 View Code Duplication
					} elseif (($place['lati'] != '0') || ($place['long'] != '0')) {
4186
						echo 'Difference: previous value = ', $prevPlace, ', ', $prevLati, ', ', $prevLong, ' current = ', $place['place'], ', ', $place['lati'], ', ', $place['long'], '<br>';
4187
					}
4188
				}
4189
				$prevPlace = $place['place'];
4190
				$prevLati  = $place['lati'];
4191
				$prevLong  = $place['long'];
4192
			}
4193
4194
			$highestIndex = $this->getHighestIndex();
4195
4196
			$default_zoom_level = array(4, 7, 10, 12);
4197
			foreach ($placelistUniq as $k => $place) {
4198
				$parent    = preg_split('/ *, */', $place['place']);
4199
				$parent    = array_reverse($parent);
4200
				$parent_id = 0;
4201
				for ($i = 0; $i < count($parent); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
4202
					if (!isset($default_zoom_level[$i]))
4203
						$default_zoom_level[$i] = $default_zoom_level[$i - 1];
4204
					$escparent               = $parent[$i];
4205
					if ($escparent == '') {
4206
						$escparent = 'Unknown';
4207
					}
4208
					$row =
4209
						Database::prepare("SELECT pl_id, pl_long, pl_lati, pl_zoom FROM `##placelocation` WHERE pl_level=? AND pl_parent_id=? AND pl_place LIKE ?")
4210
						->execute(array($i, $parent_id, $escparent))
4211
						->fetchOneRow();
4212
					if ($i < count($parent) - 1) {
4213
						// Create higher-level places, if necessary
4214
						if (empty($row)) {
4215
							$highestIndex++;
4216
							Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_zoom) VALUES (?, ?, ?, ?, ?)")
4217
								->execute(array($highestIndex, $parent_id, $i, $escparent, $default_zoom_level[$i]));
4218
							echo Filter::escapeHtml($escparent), '<br>';
4219
							$parent_id = $highestIndex;
4220
						} else {
4221
							$parent_id = $row->pl_id;
4222
						}
4223
					} else {
4224
						// Create lowest-level place, if necessary
4225
						if (empty($row->pl_id)) {
4226
							$highestIndex++;
4227
							Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_long, pl_lati, pl_zoom) VALUES (?, ?, ?, ?, ?, ?, ?)")
4228
								->execute(array($highestIndex, $parent_id, $i, $escparent, $place['long'], $place['lati'], $default_zoom_level[$i]));
4229
							echo Filter::escapeHtml($escparent), '<br>';
4230
						} else {
4231
							if (empty($row->pl_long) && empty($row->pl_lati) && $place['lati'] != '0' && $place['long'] != '0') {
4232
								Database::prepare("UPDATE `##placelocation` SET pl_lati=?, pl_long=? WHERE pl_id=?")
4233
									->execute(array($place['lati'], $place['long'], $row->pl_id));
4234
								echo Filter::escapeHtml($escparent), '<br>';
4235
							}
4236
						}
4237
					}
4238
				}
4239
			}
4240
			$parent = 0;
4241
		}
4242
4243
		if ($action === 'ImportFile') {
4244
			$placefiles = $this->findFiles(WT_MODULES_DIR . 'googlemap/extra');
4245
			sort($placefiles);
4246
		?>
4247
		<form class="form-horizontal" method="post" enctype="multipart/form-data" id="importfile" name="importfile" action="module.php?mod=googlemap&amp;mod_action=admin_places&amp;action=ImportFile2">
4248
4249
			<!-- PLACES FILE -->
4250
			<div class="form-group">
4251
				<label class="control-label col-sm-4" for="placesfile">
4252
					<?php echo I18N::translate('File containing places (CSV)') ?>
4253
				</label>
4254
				<div class="col-sm-8">
4255
					<div class="btn btn-default">
4256
					<input id="placesfile" type="file" name="placesfile">
4257
					</div>
4258
				</div>
4259
			</div>
4260
4261
			<!-- LOCAL FILE -->
4262
			<?php if (count($placefiles) > 0): ?>
4263
			<div class="form-group">
4264
				<label class="control-label col-sm-4" for="localfile">
4265
					<?php echo I18N::translate('Server file containing places (CSV)') ?>
4266
				</label>
4267
				<div class="col-sm-8">
4268
					<div class="input-group">
4269
						<span class="input-group-addon">
4270
							<?php echo WT_MODULES_DIR . 'googlemap/extra/' ?>
4271
						</span>
4272
						<?php
4273
						foreach ($placefiles as $p => $placefile) {
4274
							unset($placefiles[$p]);
4275
							$p = Filter::escapeHtml($placefile);
4276
							if (substr($placefile, 0, 1) == "/") {
4277
								$placefiles[$p] = substr($placefile, 1);
4278
							} else {
4279
								$placefiles[$p] = $placefile;
4280
							}
4281
						}
4282
						echo FunctionsEdit::selectEditControl('localfile', $placefiles, '', '', 'class="form-control"');
4283
						?>
4284
					</div>
4285
				</div>
4286
			</div>
4287
			<?php endif ?>
4288
4289
			<!-- CLEAR DATABASE -->
4290
			<fieldset class="form-group">
4291
				<legend class="control-label col-sm-4">
4292
					<?php echo I18N::translate('Delete all existing geographic data before importing the file.') ?>
4293
				</legend>
4294
				<div class="col-sm-8">
4295
					<?php echo FunctionsEdit::editFieldYesNo('cleardatabase', 0, 'class="radio-inline"') ?>
0 ignored issues
show
Documentation introduced by
0 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
4296
				</div>
4297
			</fieldset>
4298
4299
			<!-- UPDATE ONLY -->
4300
			<fieldset class="form-group">
4301
				<legend class="control-label col-sm-4">
4302
					<?php echo I18N::translate('Do not create new locations, just import coordinates for existing locations.') ?>
4303
				</legend>
4304
				<div class="col-sm-8">
4305
					<?php echo FunctionsEdit::editFieldYesNo('updateonly', 0, 'class="radio-inline"') ?>
0 ignored issues
show
Documentation introduced by
0 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
4306
				</div>
4307
			</fieldset>
4308
4309
			<!-- OVERWRITE DATA -->
4310
			<fieldset class="form-group">
4311
				<legend class="control-label col-sm-4">
4312
					<?php echo I18N::translate('Overwrite existing coordinates.') ?>
4313
				</legend>
4314
				<div class="col-sm-8">
4315
					<?php echo FunctionsEdit::editFieldYesNo('overwritedata', 0, 'class="radio-inline"') ?>
0 ignored issues
show
Documentation introduced by
0 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
4316
				</div>
4317
			</fieldset>
4318
4319
			<!-- SAVE BUTTON -->
4320
			<div class="form-group">
4321
				<div class="col-sm-offset-4 col-sm-8">
4322
					<button type="submit" class="btn btn-primary">
4323
						<i class="fa fa-check"></i>
4324
						<?php echo I18N::translate('Continue adding') ?>
4325
					</button>
4326
				</div>
4327
			</div>
4328
		</form>
4329
		<?php
4330
			return;
4331
		}
4332
4333
		if ($action === 'ImportFile2') {
4334
			$country_names = array();
4335
			$stats         = new Stats($WT_TREE);
4336
			foreach ($stats->iso3166() as $key => $value) {
4337
				$country_names[$key] = I18N::translate($key);
4338
			}
4339
			if (Filter::postBool('cleardatabase')) {
4340
				Database::exec("DELETE FROM `##placelocation` WHERE 1=1");
4341
			}
4342
			if (!empty($_FILES['placesfile']['tmp_name'])) {
4343
				$lines = file($_FILES['placesfile']['tmp_name']);
4344
			} elseif (!empty($_REQUEST['localfile'])) {
4345
				$lines = file(WT_MODULES_DIR . 'googlemap/extra' . $_REQUEST['localfile']);
0 ignored issues
show
Security File Exposure introduced by
WT_MODULES_DIR . 'google... $_REQUEST['localfile'] can contain request data and is used in file inclusion context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
4346
			}
4347
			// Strip BYTE-ORDER-MARK, if present
4348
			if (!empty($lines[0]) && substr($lines[0], 0, 3) === WT_UTF8_BOM) {
4349
				$lines[0] = substr($lines[0], 3);
4350
			}
4351
			asort($lines);
4352
			$highestIndex = $this->getHighestIndex();
4353
			$placelist    = array();
4354
			$j            = 0;
4355
			$maxLevel     = 0;
4356
			foreach ($lines as $p => $placerec) {
4357
				$fieldrec                               = explode(';', $placerec);
4358
				if ($fieldrec[0] > $maxLevel) {
4359
					$maxLevel = $fieldrec[0];
4360
				}
4361
			}
4362
			$fields   = count($fieldrec);
0 ignored issues
show
Bug introduced by
The variable $fieldrec does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
4363
			$set_icon = true;
4364
			if (!is_dir(WT_MODULES_DIR . 'googlemap/places/flags/')) {
4365
				$set_icon = false;
4366
			}
4367
			foreach ($lines as $p => $placerec) {
4368
				$fieldrec = explode(';', $placerec);
4369
				if (is_numeric($fieldrec[0]) && $fieldrec[0] <= $maxLevel) {
4370
					$placelist[$j]          = array();
4371
					$placelist[$j]['place'] = '';
4372
					for ($ii = $fields - 4; $ii > 1; $ii--) {
4373
						if ($fieldrec[0] > $ii - 2) {
4374
							$placelist[$j]['place'] .= $fieldrec[$ii] . ',';
4375
						}
4376
					}
4377
					foreach ($country_names as $countrycode => $countryname) {
4378
						if ($countrycode == strtoupper($fieldrec[1])) {
4379
							$fieldrec[1] = $countryname;
4380
							break;
4381
						}
4382
					}
4383
					$placelist[$j]['place'] .= $fieldrec[1];
4384
					$placelist[$j]['long'] = $fieldrec[$fields - 4];
4385
					$placelist[$j]['lati'] = $fieldrec[$fields - 3];
4386
					$placelist[$j]['zoom'] = $fieldrec[$fields - 2];
4387
					if ($set_icon) {
4388
						$placelist[$j]['icon'] = trim($fieldrec[$fields - 1]);
4389
					} else {
4390
						$placelist[$j]['icon'] = '';
4391
					}
4392
					$j = $j + 1;
4393
				}
4394
			}
4395
4396
			$prevPlace     = '';
4397
			$prevLati      = '';
4398
			$prevLong      = '';
4399
			$placelistUniq = array();
4400
			$j             = 0;
4401
			foreach ($placelist as $k => $place) {
4402
				if ($place['place'] != $prevPlace) {
4403
					$placelistUniq[$j]          = array();
4404
					$placelistUniq[$j]['place'] = $place['place'];
4405
					$placelistUniq[$j]['lati']  = $place['lati'];
4406
					$placelistUniq[$j]['long']  = $place['long'];
4407
					$placelistUniq[$j]['zoom']  = $place['zoom'];
4408
					$placelistUniq[$j]['icon']  = $place['icon'];
4409
					$j                          = $j + 1;
4410
				} elseif (($place['place'] == $prevPlace) && (($place['lati'] != $prevLati) || ($place['long'] != $prevLong))) {
4411
					if (($placelistUniq[$j - 1]['lati'] == 0) || ($placelistUniq[$j - 1]['long'] == 0)) {
4412
						$placelistUniq[$j - 1]['lati'] = $place['lati'];
4413
						$placelistUniq[$j - 1]['long'] = $place['long'];
4414
						$placelistUniq[$j - 1]['zoom'] = $place['zoom'];
4415
						$placelistUniq[$j - 1]['icon'] = $place['icon'];
4416 View Code Duplication
					} elseif (($place['lati'] != '0') || ($place['long'] != '0')) {
4417
						echo 'Difference: previous value = ', $prevPlace, ', ', $prevLati, ', ', $prevLong, ' current = ', $place['place'], ', ', $place['lati'], ', ', $place['long'], '<br>';
0 ignored issues
show
Security Cross-Site Scripting introduced by
$prevPlace can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Cross-Site Scripting introduced by
$prevLati can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Cross-Site Scripting introduced by
$prevLong can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Cross-Site Scripting introduced by
$place['place'] can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Cross-Site Scripting introduced by
$place['lati'] can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Cross-Site Scripting introduced by
$place['long'] can contain request data and is used in output context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
4418
					}
4419
				}
4420
				$prevPlace = $place['place'];
4421
				$prevLati  = $place['lati'];
4422
				$prevLong  = $place['long'];
4423
			}
4424
4425
			$default_zoom_level    = array();
4426
			$default_zoom_level[0] = 4;
4427
			$default_zoom_level[1] = 7;
4428
			$default_zoom_level[2] = 10;
4429
			$default_zoom_level[3] = 12;
4430
			foreach ($placelistUniq as $k => $place) {
4431
				$parent    = explode(',', $place['place']);
4432
				$parent    = array_reverse($parent);
4433
				$parent_id = 0;
4434
				for ($i = 0; $i < count($parent); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
4435
					$escparent = $parent[$i];
4436
					if ($escparent == '') {
4437
						$escparent = 'Unknown';
4438
					}
4439
					$row =
4440
						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")
4441
						->execute(array($i, $parent_id, $escparent))
4442
						->fetchOneRow();
4443
					if (empty($row)) {       // this name does not yet exist: create entry
4444
						if (!Filter::postBool('updateonly')) {
4445
							$highestIndex = $highestIndex + 1;
4446
							if (($i + 1) == count($parent)) {
4447
								$zoomlevel = $place['zoom'];
4448
							} elseif (isset($default_zoom_level[$i])) {
4449
								$zoomlevel = $default_zoom_level[$i];
4450
							} else {
4451
								$zoomlevel = $this->getSetting('GM_MAX_ZOOM');
4452
							}
4453
							if (($place['lati'] == '0') || ($place['long'] == '0') || (($i + 1) < count($parent))) {
4454
								Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_zoom, pl_icon) VALUES (?, ?, ?, ?, ?, ?)")
4455
									->execute(array($highestIndex, $parent_id, $i, $escparent, $zoomlevel, $place['icon']));
4456
							} else {
4457
								//delete leading zero
4458
								$pl_lati = str_replace(array('N', 'S', ','), array('', '-', '.'), $place['lati']);
4459
								$pl_long = str_replace(array('E', 'W', ','), array('', '-', '.'), $place['long']);
4460
								if ($pl_lati >= 0) {
4461
									$place['lati'] = 'N' . abs($pl_lati);
4462
								} elseif ($pl_lati < 0) {
4463
									$place['lati'] = 'S' . abs($pl_lati);
4464
								}
4465
								if ($pl_long >= 0) {
4466
									$place['long'] = 'E' . abs($pl_long);
4467
								} elseif ($pl_long < 0) {
4468
									$place['long'] = 'W' . abs($pl_long);
4469
								}
4470
								Database::prepare("INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_long, pl_lati, pl_zoom, pl_icon) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
4471
									->execute(array($highestIndex, $parent_id, $i, $escparent, $place['long'], $place['lati'], $zoomlevel, $place['icon']));
4472
							}
4473
							$parent_id = $highestIndex;
4474
						}
4475
					} else {
4476
						$parent_id = $row->pl_id;
4477
						if (Filter::postBool('overwritedata') && ($i + 1 == count($parent))) {
4478
							Database::prepare("UPDATE `##placelocation` SET pl_lati = ?, pl_long = ?, pl_zoom = ?, pl_icon = ? WHERE pl_id = ?")
4479
								->execute(array($place['lati'], $place['long'], $place['zoom'], $place['icon'], $parent_id));
4480
						} else {
4481
							// Update only if existing data is missing
4482
							if (!$row->pl_long && !$row->pl_lati) {
4483
								Database::prepare("UPDATE `##placelocation` SET pl_lati = ?, pl_long = ? WHERE pl_id = ?")
4484
									->execute(array($place['lati'], $place['long'], $parent_id));
4485
							}
4486
							if (!$row->pl_icon && $place['icon']) {
4487
								Database::prepare("UPDATE `##placelocation` SET pl_icon = ? WHERE pl_id = ?")
4488
									->execute(array($place['icon'], $parent_id));
4489
							}
4490
						}
4491
					}
4492
				}
4493
			}
4494
			$parent = 0;
4495
		}
4496
4497
		if ($action == 'DeleteRecord') {
4498
			$exists =
4499
				Database::prepare("SELECT 1 FROM `##placelocation` WHERE pl_parent_id=?")
4500
				->execute(array($deleteRecord))
4501
				->fetchOne();
4502
4503
			if (!$exists) {
4504
				Database::prepare("DELETE FROM `##placelocation` WHERE pl_id=?")
4505
					->execute(array($deleteRecord));
4506
			} else {
4507
				echo '<table class="facts_table"><tr><td>', I18N::translate('Location not removed: this location contains sub-locations'), '</td></tr></table>';
4508
			}
4509
		}
4510
4511
		?>
4512
		<script>
4513
		function updateList(inactive) {
4514
			window.location.href='<?php if (strstr($_SERVER['REQUEST_URI'], '&inactive', true)) { $uri = strstr($_SERVER['REQUEST_URI'], '&inactive', true); } else { $uri = $_SERVER['REQUEST_URI']; } echo $uri, '&inactive=' ?>'+inactive;
0 ignored issues
show
Security Cross-Site Scripting introduced by
$uri can contain request data and is used in html attribute with single-quotes context(s) leading to a potential security vulnerability.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
4515
		}
4516
4517
		function edit_place_location(placeid) {
4518
			window.open('module.php?mod=googlemap&mod_action=places_edit&action=update&placeid='+placeid, '_blank', gmap_window_specs);
4519
			return false;
4520
		}
4521
4522
		function add_place_location(placeid) {
4523
			window.open('module.php?mod=googlemap&mod_action=places_edit&action=add&placeid='+placeid, '_blank', gmap_window_specs);
4524
			return false;
4525
		}
4526
4527
		function delete_place(placeid) {
4528
			var answer=confirm('<?php echo I18N::translate('Remove this location?') ?>');
4529
			if (answer == true) {
4530
				window.location = '<?php echo Functions::getQueryUrl(array('action' => 'DeleteRecord')) ?>&action=DeleteRecord&deleteRecord=' + placeid;
4531
			}
4532
		}
4533
		</script>
4534
		<p id="gm_breadcrumb">
4535
			<?php
4536
			$where_am_i = $this->placeIdToHierarchy($parent);
4537
			foreach (array_reverse($where_am_i, true) as $id => $place) {
4538
				if ($id == $parent) {
4539
					if ($place != 'Unknown') {
4540
						echo Filter::escapeHtml($place);
4541
					} else {
4542
						echo I18N::translate('unknown');
4543
					}
4544
				} else {
4545
					echo '<a href="module.php?mod=googlemap&mod_action=admin_places&parent=', $id, '&inactive=', $inactive, '">';
4546
					if ($place != 'Unknown') {
4547
						echo Filter::escapeHtml($place), '</a>';
4548
					} else {
4549
						echo I18N::translate('unknown'), '</a>';
4550
					}
4551
				}
4552
				echo ' - ';
4553
			}
4554
			?>
4555
			<a href="module.php?mod=googlemap&mod_action=admin_places&parent=0&inactive=', $inactive, '"><?php echo I18N::translate('Top level') ?></a>
4556
		</p>
4557
4558
		<form class="form-inline" name="active" method="post" action="module.php?mod=googlemap&mod_action=admin_places&parent=', $parent, '&inactive=', $inactive, '">
4559
			<div class="checkbox">
4560
				<label for="inactive">
4561
				   <?php echo FunctionsEdit::checkbox('inactive', $inactive, 'onclick="updateList(this.checked)"') ?>
4562
				   <?php echo I18N::translate('Show inactive places') ?>
4563
				</label>
4564
			</div>
4565
			<p class="small text-muted">
4566
				<?php echo I18N::translate('By default, the list shows only those places which can be found in your family trees. You may have details for other places, such as those imported in bulk from an external file. Selecting this option will show all places, including ones that are not currently used.') ?>
4567
				<?php echo I18N::translate('If you have a large number of inactive places, it can be slow to generate the list.') ?>
4568
			</p>
4569
		</form>
4570
4571
		<?php
4572
		$placelist = $this->getPlaceListLocation($parent, $inactive);
4573
		echo '<div class="gm_plac_edit">';
4574
		echo '<table class="table table-bordered table-condensed table-hover"><tr>';
4575
		echo '<th>', GedcomTag::getLabel('PLAC'), '</th>';
4576
		echo '<th>', GedcomTag::getLabel('LATI'), '</th>';
4577
		echo '<th>', GedcomTag::getLabel('LONG'), '</th>';
4578
		echo '<th>', I18N::translate('Zoom level'), '</th>';
4579
		echo '<th>', I18N::translate('Icon'), '</th>';
4580
		echo '<th>';
4581
		echo I18N::translate('Edit'), '</th><th>', I18N::translate('Delete'), '</th></tr>';
4582
		if (count($placelist) == 0)
4583
			echo '<tr><td colspan="7">', I18N::translate('No places found'), '</td></tr>';
4584
		foreach ($placelist as $place) {
4585
			echo '<tr><td><a href="module.php?mod=googlemap&mod_action=admin_places&parent=', $place['place_id'], '&inactive=', $inactive, '">';
4586 View Code Duplication
			if ($place['place'] != 'Unknown')
4587
					echo Filter::escapeHtml($place['place']), '</a></td>';
4588
				else
4589
					echo I18N::translate('unknown'), '</a></td>';
4590
			echo '<td>', $place['lati'], '</td>';
4591
			echo '<td>', $place['long'], '</td>';
4592
			echo '<td>', $place['zoom'], '</td>';
4593
			echo '<td>';
4594
			if ($place['icon']) {
4595
				echo '<img src="', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/', $place['icon'], '" width="25" height="15">';
4596
			} else {
4597
				if ($place['lati'] || $place['long']) {
4598
					echo '<img src="', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/mm_20_red.png">';
4599
				} else {
4600
					echo '<img src="', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/images/mm_20_yellow.png">';
4601
				}
4602
			}
4603
			echo '</td>';
4604
			echo '<td class="narrow"><a href="#" onclick="edit_place_location(', $place['place_id'], ');return false;" class="icon-edit" title="', I18N::translate('Edit'), '"></a></td>';
4605
			$noRows =
4606
				Database::prepare("SELECT COUNT(pl_id) FROM `##placelocation` WHERE pl_parent_id=?")
4607
				->execute(array($place['place_id']))
4608
				->fetchOne();
4609
			if ($noRows == 0) { ?>
4610
				<td><a href="#" onclick="delete_place(<?php echo $place['place_id'] ?>);return false;" class="icon-delete" title="<?php echo I18N::translate('Remove') ?>"></a></td>
4611
		<?php       } else { ?>
4612
				<td><i class="icon-delete-grey"></i></td>
4613
		<?php       } ?>
4614
			</tr>
4615
			<?php
4616
		}
4617
		?>
4618
		</table>
4619
		</div>
4620
4621
		<hr>
4622
		<form class="form-horizontal" action="?" onsubmit="add_place_location(this.parent_id.options[this.parent_id.selectedIndex].value); return false;">
4623
			<div class="form-group">
4624
				<label class="form-control-static col-sm-4" for="parent_id">
4625
					<?php echo I18N::translate('Add a new geographic location') ?>
4626
				</label>
4627
				<div class="col-sm-8">
4628
					<div class="col-sm-6">
4629
						<?php echo FunctionsEdit::selectEditControl('parent_id', $where_am_i, I18N::translate('Top level'), $parent, 'class="form-control"') ?>
4630
					</div>
4631
					<button type="submit" class="btn btn-default">
4632
						<i class="fa fa-plus"></i>
4633
						<?php echo I18N::translate('Add') ?>
4634
					</button>
4635
				</div>
4636
			</div>
4637
		</form>
4638
4639
		<form class="form-horizontal" action="module.php" method="get">
4640
			<input type="hidden" name="mod" value="googlemap">
4641
			<input type="hidden" name="mod_action" value="admin_places">
4642
			<input type="hidden" name="action" value="ImportGedcom">
4643
			<div class="form-group">
4644
				<label class="form-control-static col-sm-4" for="ged">
4645
					<?php echo I18N::translate('Import all places from a family tree') ?>
4646
				</label>
4647
				<div class="col-sm-8">
4648
					<div class="col-sm-6">
4649
						<?php echo FunctionsEdit::selectEditControl('ged', Tree::getNameList(), null, $WT_TREE->getName(), 'class="form-control"') ?>
4650
					</div>
4651
					<button type="submit" class="btn btn-default">
4652
						<i class="fa fa-upload"></i>
4653
						<?php echo I18N::translate('Import') ?>
4654
					</button>
4655
				</div>
4656
			</div>
4657
		</form>
4658
4659
		<form class="form-horizontal" action="module.php" method="get">
4660
			<input type="hidden" name="mod" value="googlemap">
4661
			<input type="hidden" name="mod_action" value="admin_places">
4662
			<input type="hidden" name="action" value="ImportFile">
4663
			<div class="form-group">
4664
				<label class="form-control-static col-sm-4">
4665
					<?php echo I18N::translate('Upload geographic data') ?>
4666
				</label>
4667
				<div class="col-sm-8">
4668
					<div class="col-sm-6">
4669
						<button type="submit" class="btn btn-default">
4670
							<i class="fa fa-upload"></i>
4671
							<?php echo I18N::translate('Upload') ?>
4672
						</button>
4673
					</div>
4674
				</div>
4675
			</div>
4676
		</form>
4677
4678
		<form class="form-horizontal" action="module.php" method="get">
4679
			<input type="hidden" name="mod" value="googlemap">
4680
			<input type="hidden" name="mod_action" value="admin_places">
4681
			<input type="hidden" name="action" value="ExportFile">
4682
			<div class="form-group">
4683
				<label class="form-control-static col-sm-4">
4684
					<?php echo I18N::translate('Download geographic data') ?>
4685
				</label>
4686
				<div class="col-sm-8">
4687
					<div class="col-sm-6">
4688
						<?php echo FunctionsEdit::selectEditControl('parent', $where_am_i, I18N::translate('All'), $WT_TREE->getTreeId(), 'class="form-control"') ?>
4689
					</div>
4690
					<button type="submit" class="btn btn-default">
4691
						<i class="fa fa-download"></i>
4692
						<?php echo I18N::translate('Download') ?>
4693
					</button>
4694
				</div>
4695
			</div>
4696
		</form>
4697
		<?php
4698
	}
4699
4700
	/**
4701
	 * Generate the streetview window.
4702
	 */
4703
	private function wtStreetView() {
4704
	header('Content-type: text/html; charset=UTF-8');
4705
4706
		?>
4707
		<html>
4708
			<head>
4709
				<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
4710
				<script src="https://maps.google.com/maps/api/js?v=3.2&amp;sensor=false"></script>
4711
				<script>
4712
4713
		// Following function creates an array of the google map parameters passed ---------------------
4714
		var qsParm = [];
4715
		function qs() {
4716
			var query = window.location.search.substring(1);
4717
			var parms = query.split('&');
4718
			for (var i=0; i<parms.length; i++) {
4719
				var pos = parms[i].indexOf('=');
4720
				if (pos > 0) {
4721
					var key = parms[i].substring(0,pos);
4722
					qsParm[key] = parms[i].substring(pos + 1);
4723
				}
4724
			}
4725
		}
4726
		qsParm['x'] = null;
4727
		qsParm['y'] = null;
4728
		qs();
4729
4730
		var geocoder = new google.maps.Geocoder();
4731
4732
		function geocodePosition(pos) {
4733
			geocoder.geocode({
4734
					latLng: pos
4735
			}, function(responses) {
4736
				if (responses && responses.length > 0) {
4737
					updateMarkerAddress(responses[0].formatted_address);
4738
				} else {
4739
					updateMarkerAddress('Cannot determine address at this location.');
4740
				}
4741
			});
4742
		}
4743
4744
		function updateMarkerStatus(str) {
4745
			document.getElementById('markerStatus').innerHTML = str;
4746
		}
4747
4748
		function updateMarkerPosition(latLng) {
4749
			document.getElementById('info').innerHTML = [
4750
				latLng.lat(),
4751
				latLng.lng()
4752
			].join(', ');
4753
		}
4754
4755
		function updateMarkerAddress(str) {
4756
			document.getElementById('address').innerHTML = str;
4757
		}
4758
4759
		function roundNumber(num, dec) {
4760
			return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
4761
		}
4762
4763
		function initialize() {
4764
			var x = qsParm['x'];
4765
			var y = qsParm['y'];
4766
			var b = parseFloat(qsParm['b']);
4767
			var p = parseFloat(qsParm['p']);
4768
			var m = parseFloat(qsParm['m']);
4769
4770
			var latLng = new google.maps.LatLng(y, x);
4771
4772
			// Create the map and mapOptions
4773
			var mapOptions = {
4774
				zoom: 16,
4775
				center: latLng,
4776
				mapTypeId: google.maps.MapTypeId.ROADMAP,  // ROADMAP, SATELLITE, HYBRID, TERRAIN
4777
				mapTypeControlOptions: {
4778
					style: google.maps.MapTypeControlStyle.DROPDOWN_MENU  // DEFAULT, DROPDOWN_MENU, HORIZONTAL_BAR
4779
				},
4780
				navigationControl: true,
4781
				navigationControlOptions: {
4782
					position: google.maps.ControlPosition.TOP_RIGHT,  // BOTTOM, BOTTOM_LEFT, LEFT, TOP, etc
4783
					style: google.maps.NavigationControlStyle.SMALL   // ANDROID, DEFAULT, SMALL, ZOOM_PAN
4784
				},
4785
				streetViewControl: false,  // Show Pegman or not
4786
				scrollwheel: true
4787
			};
4788
4789
			var map = new google.maps.Map(document.getElementById('mapCanvas'), mapOptions);
4790
4791
			var bearing = b;
4792
			if (bearing < 0) {
4793
				bearing=bearing+360;
4794
			}
4795
			var pitch = p;
4796
			var svzoom = m;
4797
4798
			var imageNum = Math.round(bearing/22.5) % 16;
4799
4800
			var image = new google.maps.MarkerImage('<?php echo WT_BASE_URL . WT_MODULES_DIR ?>googlemap/images/panda-icons/panda-' + imageNum + '.png',
4801
				// This marker is 50 pixels wide by 50 pixels tall.
4802
				new google.maps.Size(50, 50),
4803
				// The origin for this image is 0,0.
4804
				new google.maps.Point(0, 0),
4805
				// The anchor for this image is the base of the flagpole at 0,32.
4806
				new google.maps.Point(26, 36)
4807
			);
4808
4809
			var shape = {
4810
				coord: [1, 1, 1, 20, 18, 20, 18 , 1],
4811
				type: 'poly'
4812
			};
4813
4814
			var marker = new google.maps.Marker({
4815
				icon: image,
4816
				// shape: shape,
4817
				position: latLng,
4818
				title: 'Drag me to a Blue Street',
4819
				map: map,
4820
				draggable: true
4821
			});
4822
4823
			// ===Next, get the map’s default panorama and set up some defaults. ===========================
4824
4825
			// --- First check if Browser supports html5 ---
4826
			var browserName=navigator.appName;
4827
			if (browserName=='Microsoft Internet Explorer') {
4828
				var render_type = '';
4829
			} else {
4830
				var render_type = 'html5';
4831
			}
4832
4833
			// --- Create the panorama ---
4834
			var panoramaOptions = {
4835
				navigationControl: true,
4836
				navigationControlOptions: {
4837
					position: google.maps.ControlPosition.TOP_RIGHT,  // BOTTOM, BOTTOM_LEFT, LEFT, TOP, etc
4838
					style: google.maps.NavigationControlStyle.SMALL   // ANDROID, DEFAULT, SMALL, ZOOM_PAN
4839
				},
4840
				linksControl: true,
4841
				addressControl: true,
4842
				addressControlOptions: {
4843
					style: {
4844
						// display: 'none'
4845
						// backgroundColor: 'red'
4846
					}
4847
				},
4848
				position: latLng,
4849
				mode: render_type,
4850
				pov: {
4851
					heading: bearing,
4852
					pitch: pitch,
4853
					zoom: svzoom
4854
				}
4855
			};
4856
			panorama = new google.maps.StreetViewPanorama(document.getElementById('mapCanvas'), panoramaOptions);
4857
			panorama.setPosition(latLng);
4858
			setTimeout(function() { panorama.setVisible(true); }, 1000);
4859
			setTimeout(function() { panorama.setVisible(true); }, 2000);
4860
			setTimeout(function() { panorama.setVisible(true); }, 3000);
4861
4862
			// Enable navigator contol and address control to be toggled with right mouse button -------
4863
			var aLink = document.createElement('a');
4864
			aLink.href = 'javascript:void(0)'; onmousedown=function(e) {
4865
				if (parseInt(navigator.appVersion)>3) {
4866
					var clickType=1;
4867
					if (navigator.appName=='Netscape') {
4868
						clickType=e.which;
4869
					} else {
4870
						clickType=event.button;
4871
					}
4872
					if (clickType==1) {
4873
						self.status='Left button!';
4874
					}
4875
					if (clickType!=1) {
4876
						if (panorama.get('addressControl') == false) {
4877
							panorama.set('navigationControl', false);
4878
							panorama.set('addressControl', true);
4879
							panorama.set('linksControl', true);
4880
						} else {
4881
							panorama.set('navigationControl', false);
4882
							panorama.set('addressControl', false);
4883
							panorama.set('linksControl', false);
4884
						}
4885
					}
4886
				}
4887
				return true;
4888
			};
4889
			panorama.controls[google.maps.ControlPosition.TOP_RIGHT].push(aLink);
4890
4891
			// Update current position info.
4892
			updateMarkerPosition(latLng);
4893
			geocodePosition(latLng);
4894
4895
			// Add dragging event listeners.
4896
			google.maps.event.addListener(marker, 'dragstart', function() {
4897
				updateMarkerAddress('Dragging...');
4898
			});
4899
4900
			google.maps.event.addListener(marker, 'drag', function() {
4901
				updateMarkerStatus('Dragging...');
4902
				updateMarkerPosition(marker.getPosition());
4903
				panorama.setPosition(marker.getPosition());
4904
			});
4905
4906
			google.maps.event.addListener(marker, 'dragend', function() {
4907
				updateMarkerStatus('Drag ended');
4908
				geocodePosition(marker.getPosition());
4909
			});
4910
4911
			google.maps.event.addListener(panorama, 'pov_changed', function() {
4912
				var povLevel = panorama.getPov();
4913
				parent.document.getElementById('sv_bearText').value = roundNumber(povLevel.heading, 2)+"\u00B0";
4914
				parent.document.getElementById('sv_elevText').value = roundNumber(povLevel.pitch, 2)+"\u00B0";
4915
				parent.document.getElementById('sv_zoomText').value = roundNumber(povLevel.zoom, 2);
4916
			});
4917
4918
			google.maps.event.addListener(panorama, 'position_changed', function() {
4919
				var pos = panorama.getPosition();
4920
				marker.setPosition(pos);
4921
				parent.document.getElementById('sv_latiText').value = pos.lat()+"\u00B0";
4922
				parent.document.getElementById('sv_longText').value = pos.lng()+"\u00B0";
4923
			});
4924
4925
			//======================================================================================
4926
			// Now add the ImageMapType overlay to the map
4927
			//--------------------------------------------------------------------------------------
4928
			map.overlayMapTypes.push(null);
4929
4930
			//======================================================================================
4931
			// Now create the StreetView ImageMap
4932
			//--------------------------------------------------------------------------------------
4933
			var street = new google.maps.ImageMapType({
4934
				getTileUrl: function(coord, zoom) {
4935
					var X = coord.x % (1 << zoom);  // wrap
4936
					return 'https://cbk0.google.com/cbk?output=overlay&zoom=' + zoom + '&x=' + X + '&y=' + coord.y + '&cb_client=api';
4937
				},
4938
				tileSize: new google.maps.Size(256, 256),
4939
				isPng: true
4940
			});
4941
4942
			//======================================================================================
4943
			//  Add the Street view Image Map
4944
			//--------------------------------------------------------------------------------------
4945
			map.overlayMapTypes.setAt(1, street);
4946
			//==============================================================================================
4947
		}
4948
4949
		function toggleStreetView() {
4950
			var toggle = panorama.getVisible();
4951
			if (toggle == false) {
4952
				panorama.setVisible(true);
4953
				document.myForm.butt1.value = "<?php echo I18N::translate('Google Maps™') ?>";
4954
			} else {
4955
				panorama.setVisible(false);
4956
				document.myForm.butt1.value = "<?php echo I18N::translate('Google Street View™') ?>";
4957
			}
4958
		}
4959
4960
		// Onload handler to fire off the app.
4961
		google.maps.event.addDomListener(window, 'load', initialize);
4962
4963
		</script>
4964
		</head>
4965
			<body>
4966
				<style>
4967
					#mapCanvas {
4968
						width: 520px;
4969
						height: 350px;
4970
						margin: -10px auto 0;
4971
						border:1px solid black;
4972
					}
4973
					#infoPanel {
4974
						display: none;
4975
						margin: 5px auto 0;
4976
					}
4977
					#infoPanel div {
4978
						display: none;
4979
						margin-bottom: 5px;
4980
						background: #ffffff;
4981
					}
4982
					div {
4983
						text-align: center;
4984
					}
4985
				</style>
4986
4987
				<div id="toggle">
4988
					<form name="myForm" title="myForm">
4989
						<?php
4990
						echo '<input id="butt1" name ="butt1" type="button" value="', I18N::translate('Google Maps™'), '" onclick="toggleStreetView();"></input>';
4991
						echo '<input id="butt2" name ="butt2" type="button" value="', I18N::translate('reset'), '" onclick="initialize();"></input>';
4992
						?>
4993
					</form>
4994
				</div>
4995
4996
				<div id="mapCanvas">
4997
				</div>
4998
4999
				<div id="infoPanel">
5000
					<div id="markerStatus"><em>Click and drag the marker.</em></div>
5001
					<div id="info" ></div>
5002
					<div id="address"></div>
5003
				</div>
5004
			</body>
5005
		</html>
5006
		<?php
5007
	}
5008
}
5009