Example #1
0
 /**
  * Startup activity
  */
 public function __construct()
 {
     parent::__construct();
     // Extract the request parameters
     $this->show_thumbs = Filter::getBool('show_thumbs');
     if ($this->root && $this->root->canShowName()) {
         $this->setPageTitle(I18N::translate('Compact tree of %s', $this->root->getFullName()));
     } else {
         $this->setPageTitle(I18N::translate('Compact tree'));
     }
     $this->treeid = $this->sosaAncestors(5);
 }
 /**
  * Create the descendancy controller
  */
 public function __construct()
 {
     global $WT_TREE;
     parent::__construct();
     // Extract parameters from form
     $this->chart_style = Filter::getInteger('chart_style', 0, 3, 0);
     $this->generations = Filter::getInteger('generations', 2, $WT_TREE->getPreference('MAX_DESCENDANCY_GENERATIONS'), $WT_TREE->getPreference('DEFAULT_PEDIGREE_GENERATIONS'));
     if ($this->root && $this->root->canShowName()) {
         $this->setPageTitle(I18N::translate('Descendants of %s', $this->root->getFullName()));
     } else {
         $this->setPageTitle(I18N::translate('Descendants'));
     }
 }
 /**
  * Startup activity
  */
 public function __construct()
 {
     global $WT_TREE;
     parent::__construct();
     // Extract form parameters
     $this->show_cousins = Filter::getInteger('show_cousins', 0, 1);
     $this->chart_style = Filter::getInteger('chart_style', 0, 3);
     $this->generations = Filter::getInteger('PEDIGREE_GENERATIONS', 2, $WT_TREE->getPreference('MAX_PEDIGREE_GENERATIONS'), $WT_TREE->getPreference('DEFAULT_PEDIGREE_GENERATIONS'));
     if ($this->root && $this->root->canShowName()) {
         $this->setPageTitle(I18N::translate('Ancestors of %s', $this->root->getFullName()));
     } else {
         $this->setPageTitle(I18N::translate('Ancestors'));
     }
 }
 /**
  * Create the controller
  */
 public function __construct()
 {
     global $WT_TREE;
     parent::__construct();
     $default_generations = $WT_TREE->getPreference('DEFAULT_PEDIGREE_GENERATIONS');
     // Extract the request parameters
     $this->fan_style = Filter::getInteger('fan_style', 2, 4, 3);
     $this->fan_width = Filter::getInteger('fan_width', 50, 500, 100);
     $this->generations = Filter::getInteger('generations', 2, 9, $default_generations);
     if ($this->root && $this->root->canShowName()) {
         $this->setPageTitle(I18N::translate('Fan chart of %s', $this->root->getFullName()));
     } else {
         $this->setPageTitle(I18N::translate('Fan chart'));
     }
 }
 /**
  * Create a family-book controller
  */
 public function __construct()
 {
     global $WT_TREE;
     parent::__construct();
     // Extract the request parameters
     $this->show_spouse = Filter::getInteger('show_spouse', 0, 1);
     $this->descent = Filter::getInteger('descent', 0, 9, 5);
     $this->generations = Filter::getInteger('generations', 2, $WT_TREE->getPreference('MAX_DESCENDANCY_GENERATIONS'), 2);
     $this->bhalfheight = $this->getBoxDimensions()->height / 2;
     if ($this->root && $this->root->canShowName()) {
         $this->setPageTitle(I18N::translate('Family book of %s', $this->root->getFullName()));
     } else {
         $this->setPageTitle(I18N::translate('Family book'));
     }
     //Checks how many generations of descendency is for the person for formatting purposes
     $this->dgenerations = $this->maxDescendencyGenerations($this->root->getXref(), 0);
     if ($this->dgenerations < 1) {
         $this->dgenerations = 1;
     }
 }
 /**
  * Create the hourglass controller.
  *
  * @param string $rootid
  * @param int    $show_full
  * @param bool   $loadJS
  */
 public function __construct($rootid = '', $show_full = 1, $loadJS = true)
 {
     global $WT_TREE;
     parent::__construct($show_full);
     // Extract parameters from
     $this->show_spouse = Filter::getInteger('show_spouse', 0, 1, 0);
     $this->generations = Filter::getInteger('generations', 2, $WT_TREE->getPreference('MAX_DESCENDANCY_GENERATIONS'), 3);
     $this->canLoadJS = $loadJS;
     //-- flip the arrows for RTL languages
     if (I18N::direction() === 'ltr') {
         $this->left_arrow = 'icon-larrow';
         $this->right_arrow = 'icon-rarrow';
     } else {
         $this->left_arrow = 'icon-rarrow';
         $this->right_arrow = 'icon-larrow';
     }
     $this->bhalfheight = (int) ($this->getBoxDimensions()->height / 2);
     //Checks how many generations of descendency is for the person for formatting purposes
     $this->dgenerations = $this->maxDescendencyGenerations($this->root, 0);
     if ($this->dgenerations < 1) {
         $this->dgenerations = 1;
     }
     $this->setPageTitle(I18N::translate('Hourglass chart of %s', $this->root->getFullName()));
 }
    /**
     * This is a general purpose hook, allowing modules to respond to routes
     * of the form module.php?mod=FOO&mod_action=BAR
     *
     * @param string $mod_action
     */
    public function modAction($mod_action)
    {
        global $controller, $WT_TREE;
        switch ($mod_action) {
            case 'treeview':
                $controller = new ChartController();
                $tv = new TreeView('tv');
                ob_start();
                $person = $controller->getSignificantIndividual();
                list($html, $js) = $tv->drawViewport($person, 4);
                $controller->setPageTitle(I18N::translate('Interactive tree of %s', $person->getFullName()))->pageHeader()->addExternalJavascript($this->js())->addExternalJavascript(WT_JQUERYUI_TOUCH_PUNCH_URL)->addInlineJavascript($js)->addInlineJavascript('
					if (document.createStyleSheet) {
						document.createStyleSheet("' . $this->css() . '"); // For Internet Explorer
					} else {
						jQuery("head").append(\'<link rel="stylesheet" type="text/css" href="' . $this->css() . '">\');
					}
				');
                echo $html;
                break;
            case 'getDetails':
                header('Content-Type: text/html; charset=UTF-8');
                $pid = Filter::get('pid', WT_REGEX_XREF);
                $i = Filter::get('instance');
                $tv = new TreeView($i);
                $individual = Individual::getInstance($pid, $WT_TREE);
                if ($individual) {
                    echo $tv->getDetails($individual);
                }
                break;
            case 'getPersons':
                header('Content-Type: text/html; charset=UTF-8');
                $q = Filter::get('q');
                $i = Filter::get('instance');
                $tv = new TreeView($i);
                echo $tv->getPersons($q);
                break;
            default:
                http_response_code(404);
                break;
        }
    }
Example #8
0
    /**
     * Display a map showing the originas of ones ancestors.
     */
    private function pedigreeMap()
    {
        global $controller, $WT_TREE;
        $MAX_PEDIGREE_GENERATIONS = $WT_TREE->getPreference('MAX_PEDIGREE_GENERATIONS');
        $controller = new ChartController();
        $this->generations = Filter::getInteger('PEDIGREE_GENERATIONS', 2, $WT_TREE->getPreference('MAX_PEDIGREE_GENERATIONS'), $WT_TREE->getPreference('DEFAULT_PEDIGREE_GENERATIONS'));
        $this->treesize = pow(2, $this->generations) - 1;
        $this->ancestors = array_values($controller->sosaAncestors($this->generations));
        // Start of internal configuration variables
        // Limit this to match available number of icons.
        // 8 generations equals 255 individuals
        $MAX_PEDIGREE_GENERATIONS = min($MAX_PEDIGREE_GENERATIONS, 8);
        // End of internal configuration variables
        $controller->setPageTitle(I18N::translate('Pedigree map of %s', $controller->root->getFullName()))->pageHeader()->addExternalJavascript(WT_AUTOCOMPLETE_JS_URL)->addInlineJavascript('autocomplete();');
        echo '<link type="text/css" href="', WT_STATIC_URL, WT_MODULES_DIR, 'googlemap/css/wt_v3_googlemap.css" rel="stylesheet">';
        echo '<div id="pedigreemap-page">
				<h2>', $controller->getPageTitle(), '</h2>';
        // -- print the form to change the number of displayed generations
        ?>
		<form name="people" method="get" action="?">
			<input type="hidden" name="ged" value="<?php 
        echo $WT_TREE->getNameHtml();
        ?>
">
			<input type="hidden" name="mod" value="googlemap">
			<input type="hidden" name="mod_action" value="pedigree_map">
			<table class="list_table" width="555">
				<tr>
					<td class="descriptionbox wrap">
						<?php 
        echo I18N::translate('Individual');
        ?>
					</td>
					<td class="optionbox">
						<input class="pedigree_form" data-autocomplete-type="INDI" type="text" id="rootid" name="rootid" size="3" value="<?php 
        echo $controller->root->getXref();
        ?>
">
						<?php 
        echo FunctionsPrint::printFindIndividualLink('rootid');
        ?>
					</td>
					<td class="topbottombar" rowspan="2">
						<input type="submit" value="<?php 
        echo I18N::translate('View');
        ?>
">
					</td>
				</tr>
				<tr>
					<td class="descriptionbox wrap">
						<?php 
        echo I18N::translate('Generations');
        ?>
					</td>
					<td class="optionbox">
						<select name="PEDIGREE_GENERATIONS">
						<?php 
        for ($p = 3; $p <= $MAX_PEDIGREE_GENERATIONS; $p++) {
            echo '<option value="', $p, '" ';
            if ($p == $this->generations) {
                echo 'selected';
            }
            echo '>', $p, '</option>';
        }
        ?>
						</select>
					</td>
				</tr>
			</table>
		</form>
		<!-- end of form -->

		<!-- count records by type -->
		<?php 
        $curgen = 1;
        $priv = 0;
        $count = 0;
        $miscount = 0;
        $missing = '';
        $latlongval = array();
        $lat = array();
        $lon = array();
        for ($i = 0; $i < $this->treesize; $i++) {
            // -- check to see if we have moved to the next generation
            if ($i + 1 >= pow(2, $curgen)) {
                $curgen++;
            }
            $person = $this->ancestors[$i];
            if (!empty($person)) {
                $name = $person->getFullName();
                if ($name == I18N::translate('Private')) {
                    $priv++;
                }
                $place = $person->getBirthPlace();
                if (empty($place)) {
                    $latlongval[$i] = null;
                } else {
                    $latlongval[$i] = $this->getLatitudeAndLongitudeFromPlaceLocation($person->getBirthPlace());
                }
                if ($latlongval[$i]) {
                    $lat[$i] = str_replace(array('N', 'S', ','), array('', '-', '.'), $latlongval[$i]->pl_lati);
                    $lon[$i] = str_replace(array('E', 'W', ','), array('', '-', '.'), $latlongval[$i]->pl_long);
                    if ($lat[$i] && $lon[$i]) {
                        $count++;
                    } else {
                        // The place is in the table but has empty values
                        if ($name) {
                            if ($missing) {
                                $missing .= ', ';
                            }
                            $missing .= '<a href="' . $person->getHtmlUrl() . '">' . $name . '</a>';
                            $miscount++;
                        }
                    }
                } else {
                    // There was no place, or not listed in the map table
                    if ($name) {
                        if ($missing) {
                            $missing .= ', ';
                        }
                        $missing .= '<a href="' . $person->getHtmlUrl() . '">' . $name . '</a>';
                        $miscount++;
                    }
                }
            }
        }
        //<!-- end of count records by type -->
        //<!-- start of map display -->
        echo '<div id="pedigreemap_chart">';
        echo '<table class="tabs_table" cellspacing="0" cellpadding="0" border="0" width="100%">';
        echo '<tr>';
        echo '<td valign="top">';
        echo '<div id="pm_map" style="border: 1px solid gray; height: ', $this->getSetting('GM_YSIZE'), 'px; font-size: 0.9em;';
        echo '"><i class="icon-loading-large"></i></div>';
        if (Auth::isAdmin()) {
            echo '<table width="100%">';
            echo '<tr><td align="left">';
            echo '<a href="module.php?mod=googlemap&amp;mod_action=admin_config">', I18N::translate('Google Maps™ preferences'), '</a>';
            echo '</td>';
            echo '<td align="center">';
            echo '<a href="module.php?mod=googlemap&amp;mod_action=admin_places">', I18N::translate('Geographic data'), '</a>';
            echo '</td>';
            echo '<td align="right">';
            echo '<a href="module.php?mod=googlemap&amp;mod_action=admin_placecheck">', I18N::translate('Place check'), '</a>';
            echo '</td></tr>';
            echo '</table>';
        }
        echo '</td><td width="15px"></td>';
        echo '<td width="310px" valign="top">';
        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>';
        echo '</tr>';
        echo '</table>';
        // display info under map
        echo '<hr>';
        echo '<table cellspacing="0" cellpadding="0" border="0" width="100%">';
        echo '<tr>';
        echo '<td valign="top">';
        // print summary statistics
        if (isset($curgen)) {
            $total = pow(2, $curgen) - 1;
            echo I18N::plural('%1$s individual displayed, out of the normal total of %2$s, from %3$s generations.', '%1$s individuals displayed, out of the normal total of %2$s, from %3$s generations.', $count, I18N::number($count), I18N::number($total), I18N::number($curgen)), '<br>';
            echo '</td>';
            echo '</tr>';
            echo '<tr>';
            echo '<td valign="top">';
            if ($priv) {
                echo I18N::plural('%s individual is private.', '%s individuals are private.', $priv, $priv), '<br>';
            }
            if ($count + $priv != $total) {
                if ($miscount == 0) {
                    echo I18N::translate('No ancestors in the database.'), "<br>";
                } else {
                    echo I18N::plural('%1$s individual is missing birthplace map coordinates: %2$s.', '%1$s individuals are missing birthplace map coordinates: %2$s.', $miscount, I18N::number($miscount), $missing), '<br>';
                }
            }
        }
        echo '</td>';
        echo '</tr>';
        echo '</table>';
        echo '</div>';
        // close #pedigreemap_chart
        echo '</div>';
        // close #pedigreemap-page
        ?>
		<!-- end of map display -->
		<!-- Start of map scripts -->
		<?php 
        echo '<script src="', $this->googleMapsScript(), '"></script>';
        $controller->addInlineJavascript($this->pedigreeMapJavascript());
    }
Example #9
0
    /**
     * Display a map showing the origins of ones ancestors.
     */
    private function pedigreeMap()
    {
        global $controller, $WT_TREE;
        $MAX_PEDIGREE_GENERATIONS = $WT_TREE->getPreference('MAX_PEDIGREE_GENERATIONS');
        // Limit this to match available number of icons.
        // 8 generations equals 255 individuals
        $MAX_PEDIGREE_GENERATIONS = min($MAX_PEDIGREE_GENERATIONS, 8);
        $controller = new ChartController();
        $generations = Filter::getInteger('PEDIGREE_GENERATIONS', 2, $MAX_PEDIGREE_GENERATIONS, $WT_TREE->getPreference('DEFAULT_PEDIGREE_GENERATIONS'));
        $this->treesize = pow(2, $generations) - 1;
        $this->ancestors = array_values($controller->sosaAncestors($generations));
        $controller->setPageTitle(I18N::translate('Pedigree map of %s', $controller->root->getFullName()))->pageHeader()->addExternalJavascript(WT_AUTOCOMPLETE_JS_URL)->addInlineJavascript("\n\t\t\t\tjQuery('head').prepend('<link type=\"text/css\" href =\"" . WT_STATIC_URL . WT_MODULES_DIR . "googlemap/css/wt_v3_googlemap.css\" rel=\"stylesheet\">');\n\t\t\t\tautocomplete();" . $this->pedigreeMapJavascript());
        echo '<div id="pedigreemap-page"><h2>', $controller->getPageTitle(), '</h2>';
        // -- print the form to change the number of displayed generations
        ?>
		<form name="people" method="get" action="?">
			<input type="hidden" name="ged" value="<?php 
        echo $WT_TREE->getNameHtml();
        ?>
">
			<input type="hidden" name="mod" value="googlemap">
			<input type="hidden" name="mod_action" value="pedigree_map">
			<table class="list_table">
				<tr>
					<td class="descriptionbox wrap">
						<label for="rootid">
							<?php 
        echo I18N::translate('Individual');
        ?>
						</label>
					</td>
					<td class="optionbox">
						<input class="pedigree_form" data-autocomplete-type="INDI" type="text" id="rootid" name="rootid" size="3" value="<?php 
        echo $controller->root->getXref();
        ?>
">
						<?php 
        echo FunctionsPrint::printFindIndividualLink('rootid');
        ?>
					</td>
					<td class="topbottombar" rowspan="2">
						<input type="submit" value="<?php 
        echo I18N::translate('view');
        ?>
">
					</td>
				</tr>
				<tr>
					<td class="descriptionbox wrap">
						<label for="PEDIGREE_GENERATIONS">
							<?php 
        echo I18N::translate('Generations');
        ?>
						</label>
					</td>
					<td class="optionbox">
						<select name="PEDIGREE_GENERATIONS" id="PEDIGREE_GENERATIONS">
						<?php 
        for ($p = 3; $p <= $MAX_PEDIGREE_GENERATIONS; $p++) {
            echo '<option value="', $p, '" ';
            if ($p == $generations) {
                echo 'selected';
            }
            echo '>', $p, '</option>';
        }
        ?>
						</select>
					</td>
				</tr>
			</table>
		</form>
		<!-- end of form -->

		<!-- count records by type -->
		<?php 
        $curgen = 1;
        $priv = 0;
        $count = 0;
        $miscount = 0;
        $missing = array();
        $latlongval = array();
        $lat = array();
        $lon = array();
        for ($i = 0; $i < $this->treesize; $i++) {
            // -- check to see if we have moved to the next generation
            if ($i + 1 >= pow(2, $curgen)) {
                $curgen++;
            }
            $person = $this->ancestors[$i];
            if (!empty($person)) {
                $name = $person->getFullName();
                if ($name == I18N::translate('Private')) {
                    $priv++;
                }
                $place = $person->getBirthPlace();
                if (empty($place)) {
                    $latlongval[$i] = null;
                } else {
                    $latlongval[$i] = $this->getLatitudeAndLongitudeFromPlaceLocation($person->getBirthPlace());
                }
                if ($latlongval[$i]) {
                    $lat[$i] = strtr($latlongval[$i]->pl_lati, array('N' => '', 'S' => '-', ',' => '.'));
                    $lon[$i] = strtr($latlongval[$i]->pl_long, array('N' => '', 'S' => '-', ',' => '.'));
                    if ($lat[$i] && $lon[$i]) {
                        $count++;
                    } else {
                        // The place is in the table but has empty values
                        if ($name) {
                            $missing[] = '<a href="' . $person->getHtmlUrl() . '">' . $name . '</a>';
                            $miscount++;
                        }
                    }
                } else {
                    // There was no place, or not listed in the map table
                    if ($name) {
                        $missing[] = '<a href="' . $person->getHtmlUrl() . '">' . $name . '</a>';
                        $miscount++;
                    }
                }
            }
        }
        //<!-- end of count records by type -->
        //<!-- start of map display -->
        echo '<div class="gm-pedigree-map">';
        echo '<div class="gm-wrapper">';
        echo '<div class="gm-map"><i class="icon-loading-large"></i></div>';
        echo '<div class="gm-ancestors"></div>';
        echo '</div>';
        if (Auth::isAdmin()) {
            echo '<div class="gm-options noprint">';
            echo '<a href="module.php?mod=' . $this->getName() . '&amp;mod_action=admin_config">' . I18N::translate('Google Maps™ preferences') . '</a>';
            echo ' | <a href="module.php?mod=' . $this->getName() . '&amp;mod_action=admin_places">' . I18N::translate('Geographic data') . '</a>';
            echo ' | <a href="module.php?mod=' . $this->getName() . '&amp;mod_action=admin_placecheck">' . I18N::translate('Place check') . '</a>';
            echo '</div>';
        }
        // display info under map
        echo '<hr>';
        // print summary statistics
        if (isset($curgen)) {
            $total = pow(2, $curgen) - 1;
            echo '<div>';
            echo I18N::plural('%1$s individual displayed, out of the normal total of %2$s, from %3$s generations.', '%1$s individuals displayed, out of the normal total of %2$s, from %3$s generations.', $count, I18N::number($count), I18N::number($total), I18N::number($curgen));
            echo '</div>';
            if ($priv) {
                echo '<div>' . I18N::plural('%s individual is private.', '%s individuals are private.', $priv, $priv), '</div>';
            }
            if ($count + $priv != $total) {
                if ($miscount == 0) {
                    echo '<div>' . I18N::translate('No ancestors in the database.'), '</div>';
                } else {
                    echo '<div>' . I18N::plural('%1$s individual is missing birthplace map coordinates: %2$s.', '%1$s individuals are missing birthplace map coordinates: %2$s.', $miscount, I18N::number($miscount), implode(I18N::$list_separator, $missing)), '</div>';
                }
            }
        }
        echo '</div>';
        echo '</div>';
        echo '<script src="', $this->googleMapsScript(), '"></script>';
    }
 /**
  * Create a pedigree controller
  */
 public function __construct()
 {
     global $WT_TREE;
     parent::__construct();
     $this->orientation = Filter::getInteger('orientation', 0, 3, $WT_TREE->getPreference('PEDIGREE_LAYOUT'));
     $this->generations = Filter::getInteger('PEDIGREE_GENERATIONS', 2, $WT_TREE->getPreference('MAX_PEDIGREE_GENERATIONS'), $WT_TREE->getPreference('DEFAULT_PEDIGREE_GENERATIONS'));
     $bxspacing = Theme::theme()->parameter('chart-spacing-x');
     $byspacing = Theme::theme()->parameter('chart-spacing-y');
     $curgen = 1;
     // -- track which generation the algorithm is currently working on
     $addoffset = array();
     // With more than 8 generations, we run out of pixels on the <canvas>
     if ($this->generations > 8) {
         $this->generations = 8;
     }
     if ($this->root && $this->root->canShowName()) {
         $this->setPageTitle(I18N::translate('Pedigree tree of %s', $this->root->getFullName()));
     } else {
         $this->setPageTitle(I18N::translate('Pedigree'));
     }
     $this->treesize = pow(2, $this->generations) - 1;
     // sosaAncestors() starts array at index 1 we need to start at 0
     $this->nodes = array_map(function ($item) {
         return array('indi' => $item, 'x' => 0, 'y' => 0);
     }, array_values($this->sosaAncestors($this->generations)));
     //check earliest generation for any ancestors
     for ($i = (int) ceil($this->treesize / 2); $i < $this->treesize; $i++) {
         $this->chartHasAncestors = $this->chartHasAncestors || $this->nodes[$i]['indi'] && $this->nodes[$i]['indi']->getChildFamilies();
     }
     $this->arrows = new \stdClass();
     switch ($this->orientation) {
         case self::PORTRAIT:
             //drop through
         //drop through
         case self::LANDSCAPE:
             $this->arrows->prevGen = I18N::direction() === 'rtl' ? 'icon-larrow' : 'icon-rarrow';
             $this->arrows->menu = I18N::direction() === 'rtl' ? 'icon-rarrow' : 'icon-larrow';
             $addoffset['x'] = $this->chartHasAncestors ? self::ARROW_SIZE : 0;
             $addoffset['y'] = 0;
             break;
         case self::OLDEST_AT_TOP:
             $this->arrows->prevGen = 'icon-uarrow';
             $this->arrows->menu = 'icon-darrow';
             $addoffset['x'] = 0;
             $addoffset['y'] = $this->root->getSpouseFamilies() ? self::ARROW_SIZE : 0;
             break;
         case self::OLDEST_AT_BOTTOM:
             $this->arrows->prevGen = 'icon-darrow';
             $this->arrows->menu = 'icon-uarrow';
             $addoffset['x'] = 0;
             $addoffset['y'] = $this->chartHasAncestors ? self::ARROW_SIZE : 0;
             break;
     }
     // -- this next section will create and position the DIV layers for the pedigree tree
     // -- loop through all of IDs in the array from last to first
     // -- calculating the box positions
     for ($i = $this->treesize - 1; $i >= 0; $i--) {
         // -- check to see if we have moved to the next generation
         if ($i < (int) ($this->treesize / pow(2, $curgen))) {
             $curgen++;
         }
         // -- box position in current generation
         $boxpos = $i - pow(2, $this->generations - $curgen);
         // -- offset multiple for current generation
         if ($this->orientation < self::OLDEST_AT_TOP) {
             $genoffset = pow(2, $curgen - $this->orientation);
             $boxspacing = $this->getBoxDimensions()->height + $byspacing;
         } else {
             $genoffset = pow(2, $curgen - 1);
             $boxspacing = $this->getBoxDimensions()->width + $byspacing;
         }
         // -- calculate the yoffset position in the generation put child between parents
         $yoffset = $boxpos * ($boxspacing * $genoffset) + $boxspacing / 2 * $genoffset + $boxspacing * $genoffset;
         // -- calculate the xoffset
         switch ($this->orientation) {
             case self::PORTRAIT:
                 $xoffset = ($this->generations - $curgen) * (($this->getBoxDimensions()->width + $bxspacing) / 1.8);
                 if (!$i && $this->root->getSpouseFamilies()) {
                     $xoffset -= self::ARROW_SIZE;
                 }
                 // -- compact the tree
                 if ($curgen < $this->generations) {
                     if ($i % 2 == 0) {
                         $yoffset = $yoffset - $boxspacing / 2 * ($curgen - 1);
                     } else {
                         $yoffset = $yoffset + $boxspacing / 2 * ($curgen - 1);
                     }
                     $parent = (int) (($i - 1) / 2);
                     $pgen = $curgen;
                     while ($parent > 0) {
                         if ($parent % 2 == 0) {
                             $yoffset = $yoffset - $boxspacing / 2 * $pgen;
                         } else {
                             $yoffset = $yoffset + $boxspacing / 2 * $pgen;
                         }
                         $pgen++;
                         if ($pgen > 3) {
                             $temp = 0;
                             for ($j = 1; $j < $pgen - 2; $j++) {
                                 $temp += pow(2, $j) - 1;
                             }
                             if ($parent % 2 == 0) {
                                 $yoffset = $yoffset - $boxspacing / 2 * $temp;
                             } else {
                                 $yoffset = $yoffset + $boxspacing / 2 * $temp;
                             }
                         }
                         $parent = (int) (($parent - 1) / 2);
                     }
                     if ($curgen > 3) {
                         $temp = 0;
                         for ($j = 1; $j < $curgen - 2; $j++) {
                             $temp += pow(2, $j) - 1;
                         }
                         if ($i % 2 == 0) {
                             $yoffset = $yoffset - $boxspacing / 2 * $temp;
                         } else {
                             $yoffset = $yoffset + $boxspacing / 2 * $temp;
                         }
                     }
                 }
                 $yoffset -= $boxspacing / 2 * pow(2, $this->generations - 2) - $boxspacing / 2;
                 break;
             case self::LANDSCAPE:
                 $xoffset = ($this->generations - $curgen) * ($this->getBoxDimensions()->width + $bxspacing);
                 if ($curgen == 1) {
                     $xoffset += 10;
                 }
                 break;
             case self::OLDEST_AT_TOP:
                 //swap x & y offsets as chart is rotated
                 $xoffset = $yoffset;
                 $yoffset = $curgen * ($this->getBoxDimensions()->height + $byspacing * 4);
                 break;
             case self::OLDEST_AT_BOTTOM:
                 //swap x & y offsets as chart is rotated
                 $xoffset = $yoffset;
                 $yoffset = ($this->generations - $curgen) * ($this->getBoxDimensions()->height + $byspacing * 2);
                 if ($i && $this->root->getSpouseFamilies()) {
                     $yoffset += self::ARROW_SIZE;
                 }
                 break;
         }
         $this->nodes[$i]["x"] = (int) $xoffset;
         $this->nodes[$i]["y"] = (int) $yoffset;
     }
     // find the minimum x & y offsets and deduct that number from
     // each value in the array so that offsets start from zero
     $min_xoffset = min(array_map(function ($item) {
         return $item['x'];
     }, $this->nodes));
     $min_yoffset = min(array_map(function ($item) {
         return $item['y'];
     }, $this->nodes));
     array_walk($this->nodes, function (&$item) use($min_xoffset, $min_yoffset) {
         $item['x'] -= $min_xoffset;
         $item['y'] -= $min_yoffset;
     });
     // calculate chart & canvas dimensions
     $max_xoffset = max(array_map(function ($item) {
         return $item['x'];
     }, $this->nodes));
     $max_yoffset = max(array_map(function ($item) {
         return $item['y'];
     }, $this->nodes));
     $this->chartsize['x'] = $max_xoffset + $bxspacing + $this->getBoxDimensions()->width + $addoffset['x'];
     $this->chartsize['y'] = $max_yoffset + $byspacing + $this->getBoxDimensions()->height + $addoffset['y'];
 }