summaryrefslogtreecommitdiff
path: root/app/Controller/LifespanController.php
diff options
context:
space:
mode:
Diffstat (limited to 'app/Controller/LifespanController.php')
-rw-r--r--app/Controller/LifespanController.php785
1 files changed, 333 insertions, 452 deletions
diff --git a/app/Controller/LifespanController.php b/app/Controller/LifespanController.php
index bcd062a384..94a587e2a3 100644
--- a/app/Controller/LifespanController.php
+++ b/app/Controller/LifespanController.php
@@ -16,54 +16,64 @@ namespace Fisharebest\Webtrees\Controller;
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-use Fisharebest\ExtCalendar\GregorianCalendar;
+use Fisharebest\Webtrees\ColorGenerator;
use Fisharebest\Webtrees\Database;
use Fisharebest\Webtrees\Date;
+use Fisharebest\Webtrees\Fact;
use Fisharebest\Webtrees\Filter;
use Fisharebest\Webtrees\Functions\Functions;
use Fisharebest\Webtrees\GedcomTag;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Individual;
+use Fisharebest\Webtrees\Family;
use Fisharebest\Webtrees\Place;
use Fisharebest\Webtrees\Session;
+use Fisharebest\Webtrees\Date\FrenchDate;
+use Fisharebest\Webtrees\Date\GregorianDate;
+use Fisharebest\Webtrees\Date\HijriDate;
+use Fisharebest\Webtrees\Date\JalaliDate;
+use Fisharebest\Webtrees\Date\JewishDate;
+use Fisharebest\Webtrees\Date\JulianDate;
/**
* Class LifespanController - Controller for the timeline chart
*/
class LifespanController extends PageController {
- private $pids = array();
- public $people = array();
- public $place = '';
- public $beginYear = 0;
- public $endYear = 0;
- public $YrowLoc = 125;
- // The following colours are deliberately omitted from the $colors list:
- // Blue, Red, Black, White, Green
- private $colors = array('Aliceblue', 'Antiquewhite', 'Aqua', 'Aquamarine', 'Azure', 'Beige', 'Bisque', 'Blanchedalmond', 'Blueviolet', 'Brown', 'Burlywood', 'Cadetblue', 'Chartreuse', 'Chocolate', 'Coral', 'Cornflowerblue', 'Cornsilk', 'Crimson', 'Cyan', 'Darkcyan', 'Darkgoldenrod', 'Darkgray', 'Darkgreen', 'Darkkhaki', 'Darkmagenta', 'Darkolivegreen', 'Darkorange', 'Darkorchid', 'Darkred', 'Darksalmon', 'Darkseagreen', 'Darkslateblue', 'Darkturquoise', 'Darkviolet', 'Deeppink', 'Deepskyblue', 'Dimgray', 'Dodgerblue', 'Firebrick', 'Floralwhite', 'Forestgreen', 'Fuchsia', 'Gainsboro', 'Ghostwhite', 'Gold', 'Goldenrod', 'Gray', 'Greenyellow', 'Honeydew', 'Hotpink', 'Indianred', 'Ivory', 'Khaki', 'Lavender', 'Lavenderblush', 'Lawngreen', 'Lemonchiffon', 'Lightblue', 'Lightcoral', 'Lightcyan', 'Lightgoldenrodyellow', 'Lightgreen', 'Lightgrey', 'Lightpink', 'Lightsalmon', 'Lightseagreen', 'Lightskyblue', 'Lightslategray', 'Lightsteelblue', 'Lightyellow', 'Lime', 'Limegreen', 'Linen', 'Magenta', 'Maroon', 'Mediumaqamarine', ' Mediumblue', 'Mediumorchid', 'Mediumpurple', 'Mediumseagreen', 'Mediumslateblue', 'Mediumspringgreen', 'Mediumturquoise', 'Mediumvioletred', 'Mintcream', 'Mistyrose', 'Moccasin', 'Navajowhite', 'Oldlace', 'Olive', 'Olivedrab', 'Orange', 'Orangered', 'Orchid', 'Palegoldenrod', 'Palegreen', 'Paleturquoise', 'Palevioletred', 'Papayawhip', 'Peachpuff', 'Peru', 'Pink', 'Plum', 'Powderblue', 'Purple', 'Rosybrown', 'Royalblue', 'Saddlebrown', 'Salmon', 'Sandybrown', 'Seagreen', 'Seashell', 'Sienna', 'Silver', 'Skyblue', 'Slateblue', 'Slategray', 'Snow', 'Springgreen', 'Steelblue', 'Tan', 'Teal', 'Thistle', 'Tomato', 'Turquoise', 'Violet', 'Wheat', 'Whitesmoke', 'Yellow', 'YellowGreen');
- private $malecolorR = array(' 100', ' 110', ' 120', ' 130', ' 140', ' 150', ' 160', ' 170', ' 180', ' 190', ' 200', ' 210', ' 220', ' 230', ' 240', ' 250');
- private $malecolorG = array(' 100', ' 110', ' 120', ' 130', ' 140', ' 150', ' 160', ' 170', ' 180', ' 190', ' 200', ' 210', ' 220', ' 230', ' 240', ' 250');
- private $malecolorB = 255;
- private $femalecolorR = 255;
- private $femalecolorG = array(' 100', ' 110', ' 120', ' 130', ' 140', ' 150', ' 160', ' 170', ' 180', ' 190', ' 200', ' 210', ' 220', ' 230', ' 240', ' 250');
- private $femalecolorB = array('250', ' 240', ' 230', ' 220', ' 210', ' 200', ' 190', ' 180', ' 170', ' 160', ' 150', ' 140', ' 130', ' 120', ' 110', ' 100');
- private $color;
- private $colorindex;
- private $Fcolorindex;
- private $Mcolorindex;
- private $zoomfactor;
- public $timelineMinYear;
- public $timelineMaxYear;
- private $birthMod;
- private $deathMod;
- private $endMod = 0;
- private $modTest;
- private $currentYear;
- private $endDate;
- private $currentsex;
+ // Base color parameters
+ const range = 120; // degrees
+ const saturation = 100; // percent
+ const lightness = 30; // percent
+ const alpha = 0.25;
+
+ const chartTop = 10; // px
+ const barSpacing = 22; // px
+ const yearSpan = 10; // No. years per scale section
+ const pixelsPerYear = 7; // No. pixels to shift per year
+ const sessionData = 'lifespan_data';
- private $nonfacts = array(
- 'FAMS', 'FAMC', 'MAY', 'BLOB', 'OBJE', 'SEX', 'NAME', 'SOUR', 'NOTE', 'BAPL', 'ENDL', 'SLGC', 'SLGS', '_TODO', '_WT_OBJE_SORT', 'CHAN', 'HUSB', 'WIFE', 'CHIL', 'BIRT', 'DEAT', 'BURI',
+ // Used in lifespan.php
+ public $place = null;
+ public $beginYear = null;
+ public $endYear = null;
+ public $subtitle = '&nbsp;';
+ public $showDetails;
+
+ private $people = array();
+ private $defaultCalendar;
+ private $calendar;
+ private $calendarEscape;
+ private $timelineMinYear;
+ private $timelineMaxYear;
+ private $currentYear;
+ private $colors = array();
+ private $place_obj = null;
+ private $startDate = null;
+ private $endDate = null;
+ private $strictDate;
+ private $facts;
+ private $nonfacts = array(
+ 'FAMS', 'FAMC', 'MAY', 'BLOB', 'OBJE', 'SEX', 'NAME', 'SOUR', 'NOTE', 'BAPL', 'ENDL', 'SLGC', 'SLGS', '_TODO', '_WT_OBJE_SORT', 'CHAN', 'HUSB', 'WIFE', 'CHIL', 'OCCU', 'ASSO'
);
/**
@@ -74,541 +84,412 @@ class LifespanController extends PageController {
parent::__construct();
$this->setPageTitle(I18N::translate('Lifespans'));
+ $this->showDetails = $WT_TREE->getPreference('PEDIGREE_FULL_DETAILS', true) ? 'checked' : '';
- $this->colorindex = 0;
- $this->Fcolorindex = 0;
- $this->Mcolorindex = 0;
- $this->zoomfactor = 10;
- $this->color = '#0000FF';
- $this->currentYear = (int) date('Y');
- $this->deathMod = 0;
- $this->endDate = $this->currentYear;
+ $this->facts = explode('|', WT_EVENTS_BIRT . '|' . WT_EVENTS_DEAT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV);
+ $tmp = explode('\\', get_class(I18N::defaultCalendar()));
+ $cal = strtolower(array_pop($tmp));
+ $this->defaultCalendar = str_replace('calendar', '', $cal);
+ $filterPids = false;
// Request parameters
- $newpid = Filter::get('newpid', WT_REGEX_XREF);
- $remove = Filter::get('remove', WT_REGEX_XREF);
- $pids = Filter::getArray('pids', WT_REGEX_XREF);
- $clear = Filter::getBool('clear');
- $addfam = Filter::getBool('addFamily');
- $place = Filter::get('place');
- $beginYear = Filter::getInteger('beginYear', 0, $this->currentYear + 100, 0);
- $endYear = Filter::getInteger('endYear', 0, $this->currentYear + 100, 0);
+ $clear = Filter::postBool('clear');
+ $newpid = Filter::post('newpid', WT_REGEX_XREF);
+ $addfam = Filter::postBool('addFamily');
+ $this->place = Filter::post('place');
+ $this->beginYear = Filter::postInteger('beginYear', 0, PHP_INT_MAX, null);
+ $this->endYear = Filter::postInteger('endYear', 0, PHP_INT_MAX, null);
+ $this->calendar = Filter::post('calendar', null, $this->defaultCalendar);
+ $this->strictDate = Filter::postBool('strictDate');
- $new_person = Individual::getInstance($newpid, $WT_TREE);
+ // Set up base color parameters
+ $this->colors['M'] = new ColorGenerator(240, self::saturation, self::lightness, self::alpha, self::range * -1);
+ $this->colors['F'] = new ColorGenerator(000, self::saturation, self::lightness, self::alpha, self::range);
+ // Build a list of people based on the input parameters
if ($clear) {
- // Empty list
- $this->pids = array();
- } elseif ($pids) {
- // List of specified records
- $this->pids = $pids;
- } elseif ($place) {
- // All records found in a place
- $wt_place = new Place($place, $WT_TREE);
- $this->pids = Database::prepare(
- "SELECT DISTINCT pl_gid FROM `##placelinks` WHERE pl_p_id = ? AND pl_file = ?"
- )->execute(array($wt_place->getPlaceId(), $WT_TREE->getTreeId()))->fetchOneColumn();
- $this->place = $place;
+ // Empty list & reset form
+ $xrefs = array();
+ $this->place = null;
+ $this->beginYear = null;
+ $this->endYear = null;
+ $this->calendar = $this->defaultCalendar;
+ } elseif ($this->place) {
+ // Get all individual & family records found for a place
+ $this->place_obj = new Place($this->place, $WT_TREE);
+ $xrefs = Database::prepare(
+ "SELECT DISTINCT `i_id` FROM `##placelinks`" .
+ " JOIN `##individuals` ON `pl_gid`=`i_id` AND `pl_file`=`i_file`" .
+ " WHERE `i_file`=:tree_id" .
+ " AND `pl_p_id`=:place_id" .
+ " UNION" .
+ " SELECT DISTINCT `f_id` FROM `##placelinks`" .
+ " JOIN `##families` ON `pl_gid`=`f_id` AND `pl_file`=`f_file`" .
+ " WHERE `f_file`=:tree_id" .
+ " AND `pl_p_id`=:place_id"
+ )->execute(array(
+ 'tree_id' => $WT_TREE->getTreeId(),
+ 'place_id' => $this->place_obj->getPlaceId()
+ ))->fetchOneColumn();
} else {
// Modify an existing list of records
- if (is_array(Session::get('timeline_pids'))) {
- $this->pids = Session::get('timeline_pids');
- } else {
- $this->pids = array();
- }
- if ($remove) {
- foreach ($this->pids as $key => $value) {
- if ($value == $remove) {
- unset($this->pids[$key]);
- }
- }
- } elseif ($new_person) {
- $this->addFamily($new_person, $addfam);
- } elseif (!$this->pids) {
- $this->addFamily($this->getSignificantIndividual(), false);
+ $xrefs = Session::get(self::sessionData, array());
+ if ($newpid) {
+ $xrefs = array_merge($xrefs, $this->addFamily(Individual::getInstance($newpid, $WT_TREE), $addfam));
+ $xrefs = array_unique($xrefs);
+ } elseif (!$xrefs) {
+ $xrefs = $this->addFamily($this->getSignificantIndividual(), false);
}
}
- Session::put('timeline_pids', $this->pids);
- $this->beginYear = $beginYear;
- $this->endYear = $endYear;
- if ($beginYear == 0 || $endYear == 0) {
- //-- cleanup user input
- $this->pids = array_unique($this->pids); //removes duplicates
- foreach ($this->pids as $key => $value) {
- if ($value != $remove) {
- $this->pids[$key] = $value;
- $person = Individual::getInstance($value, $WT_TREE);
- // list of linked records includes families as well as individuals.
- if ($person) {
- $bdate = $person->getEstimatedBirthDate();
- if ($bdate->isOK() && $person->canShow()) {
- $this->people[] = $person;
- }
- }
+ $tmp = $this->getCalendarDate(unixtojd());
+ $this->currentYear = $tmp->today()->y;
+
+ $tmp = strtoupper(strtr($this->calendar,
+ array('jewish' => 'hebrew',
+ 'french' => 'french r'
+ )));
+ $this->calendarEscape = sprintf("@#D%s@", $tmp);
+
+ if ($xrefs) {
+ // ensure date ranges are valid in preparation for filtering list
+ if ($this->beginYear || $this->endYear) {
+ $filterPids = true;
+ if (!$this->beginYear) {
+ $tmp = new Date($this->calendarEscape . ' 1');
+ $this->beginYear = $tmp->minimumDate()->y;
+ }
+ if (!$this->endYear) {
+ $this->endYear = $this->currentYear;
}
+ $this->startDate = new Date($this->calendarEscape . $this->beginYear);
+ $this->endDate = new Date($this->calendarEscape . $this->endYear);
}
- } else {
- //--Finds if the begin year and end year textboxes are not empty
- //-- reset the people array when doing a year range search
- $this->people = array();
- //Takes the begining year and end year passed by the postback and modifies them and uses them to populate
- //the time line
-
- //Variables to restrict the person boxes to the year searched.
- //--Searches for individuals who had an even between the year begin and end years
- $indis = self::searchIndividualsInYearRange($beginYear, $endYear);
- //--Populates an array of people that had an event within those years
- foreach ($indis as $person) {
- if (empty($searchplace) || in_array($person->getXref(), $this->pids)) {
- $bdate = $person->getEstimatedBirthDate();
- if ($bdate->isOK() && $person->canShow()) {
- $this->people[] = $person;
+ // Test each xref to see if the search criteria are met
+ foreach ($xrefs as $key => $xref) {
+ $valid = false;
+ $person = Individual::getInstance($xref, $WT_TREE);
+ if ($person) {
+ if ($person->canShow()) {
+ foreach ($person->getFacts() as $fact) {
+ if ($this->checkFact($fact)) {
+ $this->people[] = $person;
+ $valid = true;
+ break;
+ }
+ }
}
+ } else {
+ $family = Family::getInstance($xref, $WT_TREE);
+ if ($family && $family->canShow() && $this->checkFact($family->getMarriage())) {
+ $valid = true;
+ $this->people[] = $family->getHusband();
+ $this->people[] = $family->getWife();
+ }
+ }
+ if (!$valid) {
+ unset($xrefs[$key]); // no point in storing a xref if we can't use it
}
}
- Session::forget('timeline_pids');
+ Session::put(self::sessionData, $xrefs);
+ } else {
+ Session::forget(self::sessionData);
}
- // Sort the array in order of birth year
- uasort($this->people, function (Individual $a, Individual $b) {
- return Date::compare($a->getEstimatedBirthDate(), $b->getEstimatedBirthDate());
- });
- //If there is people in the array posted back this if occurs
- if (isset($this->people[0])) {
- //Find the maximum Death year and mimimum Birth year for each individual returned in the array.
- $bdate = $this->people[0]->getEstimatedBirthDate();
- $ddate = $this->people[0]->getEstimatedDeathDate();
- $this->timelineMinYear = $bdate->gregorianYear();
- $this->timelineMaxYear = $ddate->gregorianYear() ? $ddate->gregorianYear() : date('Y');
- foreach ($this->people as $value) {
- $bdate = $value->getEstimatedBirthDate();
- $ddate = $value->getEstimatedDeathDate();
- $this->timelineMinYear = min($this->timelineMinYear, $bdate->gregorianYear());
- $this->timelineMaxYear = max($this->timelineMaxYear, $ddate->gregorianYear() ? $ddate->gregorianYear() : date('Y'));
+ $this->people = array_filter(array_unique($this->people));
+ $count = count($this->people);
+ if ($count) {
+ // Build the subtitle
+ if ($this->place && $filterPids) {
+ $this->subtitle = I18N::plural(
+ '%s person with events in %s between %s and %s',
+ '%s people with events in %s between %s and %s',
+ $count, I18N::number($count),
+ $this->place, $this->startDate->display(false, '%Y'), $this->endDate->display(false, '%Y')
+ );
+ } elseif ($this->place) {
+ $this->subtitle = I18N::plural(
+ '%s person with events in %s',
+ '%s people with events in %s',
+ $count, I18N::number($count),
+ $this->place
+ );
+ } elseif ($filterPids) {
+ $this->subtitle = I18N::plural(
+ '%s person with events between %s and %s',
+ '%s people with events between %s and %s',
+ $count, I18N::number($count),
+ $this->startDate->display(false, '%Y'), $this->endDate->display(false, '%Y')
+ );
+ } else {
+ $this->subtitle = I18N::plural(
+ '%s person',
+ '%s people',
+ $count, I18N::number($count));
}
- if ($this->timelineMaxYear > $this->currentYear) {
- $this->timelineMaxYear = $this->currentYear;
- }
+ // Sort the array in order of birth year
+ usort($this->people, function (Individual $a, Individual $b) {
+ return Date::compare($a->getEstimatedBirthDate(), $b->getEstimatedBirthDate());
+ });
+
+ //Find the mimimum birth year and maximum death year from the individuals in the array.
+ $bdate = $this->getCalendarDate($this->people[0]->getEstimatedBirthDate()->minimumJulianDay());
+ $minyear = $bdate->y;
+
+ $maxyear = array_reduce($this->people, function ($carry, $item) {
+ $date = $this->getCalendarDate($item->getEstimatedDeathDate()->maximumJulianDay());
+ return max($carry, $date->y);
+ }, 0);
+ } elseif ($filterPids) {
+ $minyear = $this->endYear;
+ $maxyear = $this->endYear;
} else {
- // Sets the default timeline length
- $this->timelineMinYear = date("Y") - 101;
- $this->timelineMaxYear = date("Y");
+ $minyear = $this->currentYear;
+ $maxyear = $this->currentYear;
}
+
+ $maxyear = min($maxyear, $this->currentYear); // Limit maximum year to current year as we can't forecast the future
+ $minyear = min($minyear, $maxyear - $WT_TREE->getPreference('MAX_ALIVE_AGE')); // Set default minimum chart length
+
+ $this->timelineMinYear = (int)floor($minyear / 10) * 10; // round down to start of the decade
+ $this->timelineMaxYear = (int) ceil($maxyear / 10) * 10; // round up to start of next decade
}
/**
* Add a person (and optionally their immediate family members) to the pids array
*
* @param Individual $person
- * @param bool $add_family
+ * @param boolean $add_family
+ * @return array
*/
private function addFamily(Individual $person, $add_family) {
- $this->pids[] = $person->getXref();
+ $xrefs = array();
+ $xrefs[] = $person->getXref();
if ($add_family) {
foreach ($person->getSpouseFamilies() as $family) {
$spouse = $family->getSpouse($person);
if ($spouse) {
- $this->pids[] = $spouse->getXref();
+ $xrefs[] = $spouse->getXref();
foreach ($family->getChildren() as $child) {
- $this->pids[] = $child->getXref();
+ $xrefs[] = $child->getXref();
}
}
}
foreach ($person->getChildFamilies() as $family) {
foreach ($family->getSpouses() as $parent) {
- $this->pids[] = $parent->getXref();
+ $xrefs[] = $parent->getXref();
}
foreach ($family->getChildren() as $sibling) {
if ($person !== $sibling) {
- $this->pids[] = $sibling->getXref();
+ $xrefs[] = $sibling->getXref();
}
}
}
}
+ return $xrefs;
}
/**
- * Sets the start year and end year to a factor of 5
- *
- * @param int $year
- * @param int $key
+ * Prints the time line scale
*
- * @return int
*/
- private function modifyYear($year, $key) {
- $temp = $year;
- switch ($key) {
- case 1 : // rounds beginning year
- $this->birthMod = $year % 5;
- $year = $year - $this->birthMod;
- if ($temp == $year) {
- $this->modTest = 0;
- } else {
- $this->modTest = 1;
- }
- break;
- case 2 : // rounds end year
- $this->deathMod = $year % 5;
- // Only executed if the year needs to be modified
- if ($this->deathMod > 0) {
- $this->endMod = 5 - $this->deathMod;
- } else {
- $this->endMod = 0;
- }
- $year = $year + $this->endMod;
- break;
+ public function printTimeline() {
+ $startYear = $this->timelineMinYear;
+ while ($startYear < $this->timelineMaxYear) {
+ $date = new Date($this->calendarEscape . $startYear);
+ echo $date->display(false, '%Y', false);
+ $startYear += self::yearSpan;
}
-
- return $year;
}
/**
- * Prints the time line
+ * Populate the timeline
*
- * @param int $startYear
- * @param int $endYear
+ * @return integer
*/
- public function printTimeline($startYear, $endYear) {
- $leftPosition = 14; //start point
- $tickDistance = 50; //length of one timeline section
- $top = 65; //top starting position
- $yearSpan = 5; //default zoom level
- $newStartYear = $this->modifyYear($startYear, 1); //starting date for timeline
- $this->timelineMinYear = $newStartYear;
- $newEndYear = $this->modifyYear($endYear, 2); //ending date for timeline
- $totalYears = $newEndYear - $newStartYear; //length of timeline
- $timelineTick = $totalYears / $yearSpan; //calculates the length of the timeline
+ public function fillTimeline() {
- for ($i = 0; $i < $timelineTick; $i++) {
- echo "<div class=\"sublinks_cell\" style=\"text-align: left; position: absolute; top: ", $top, "px; left: ", $leftPosition, "px; width: ", $tickDistance, "px;\">$newStartYear<i class=\"icon-lifespan-chunk\"></i></div>"; //onclick="zoomToggle('100px', '100px', '200px', '200px', this);"
- $leftPosition += $tickDistance;
- $newStartYear += $yearSpan;
- }
- echo "<div class=\"sublinks_cell\" style=\"text-align: left; position: absolute; top: ", $top, "px; left: ", $leftPosition, "px; width: ", $tickDistance, "px;\">$newStartYear</div>";
- }
-
- /**
- * Method used to place the person boxes onto the timeline
- *
- * @param Individual[] $ar
- * @param int $top
- *
- * @return int
- */
- public function fillTimeline($ar, $top) {
- global $maxX, $zindex;
-
- $zindex = count($ar);
-
- $rows = array();
- $modFix = 0;
- if ($this->modTest == 1) {
- $modFix = (9 * $this->birthMod);
- }
+ $rows = array();
+ $maxY = self::chartTop;
//base case
- if (count($ar) == 0) {
- return $top;
+ if (!$this->people) {
+ return $maxY;
}
- $maxY = $top;
- foreach ($ar as $value) {
- //Creates appropriate color scheme to show relationships
- $this->currentsex = $value->getSex();
- if ($this->currentsex == "M") {
- $this->Mcolorindex++;
- if (!isset($this->malecolorR[$this->Mcolorindex])) {
- $this->Mcolorindex = 0;
- }
- $this->malecolorR[$this->Mcolorindex];
- $this->Mcolorindex++;
- if (!isset($this->malecolorG[$this->Mcolorindex])) {
- $this->Mcolorindex = 0;
- }
- $this->malecolorG[$this->Mcolorindex];
- $red = dechex($this->malecolorR[$this->Mcolorindex]);
- $green = dechex($this->malecolorR[$this->Mcolorindex]);
- if (strlen($red) < 2) {
- $red = "0" . $red;
- }
- if (strlen($green) < 2) {
- $green = "0" . $green;
- }
+ foreach ($this->people as $person) {
- $this->color = "#" . $red . $green . dechex($this->malecolorB);
- } elseif ($this->currentsex == "F") {
- $this->Fcolorindex++;
- if (!isset($this->femalecolorG[$this->Fcolorindex])) {
- $this->Fcolorindex = 0;
- }
- $this->femalecolorG[$this->Fcolorindex];
- $this->Fcolorindex++;
- if (!isset($this->femalecolorB[$this->Fcolorindex])) {
- $this->Fcolorindex = 0;
- }
- $this->femalecolorB[$this->Fcolorindex];
- $this->color = "#" . dechex($this->femalecolorR) . dechex($this->femalecolorG[$this->Fcolorindex]) . dechex($this->femalecolorB[$this->Fcolorindex]);
- } else {
- $this->color = $this->colors[$this->colorindex];
- }
-
- //set start position and size of person-box according to zoomfactor
- $bdate = $value->getEstimatedBirthDate();
- $ddate = $value->getEstimatedDeathDate();
- $birthYear = $bdate->gregorianYear();
- $deathYear = $ddate->gregorianYear() ? $ddate->gregorianYear() : date('Y');
-
- $width = ($deathYear - $birthYear) * $this->zoomfactor;
- $height = 2 * $this->zoomfactor;
-
- $startPos = (($birthYear - $this->timelineMinYear) * $this->zoomfactor) + 14 + $modFix;
- $minlength = mb_strlen(strip_tags($value->getFullName())) * $this->zoomfactor;
-
- if ($startPos > 15) {
- $startPos = (($birthYear - $this->timelineMinYear) * $this->zoomfactor) + 15;
- $width = (($deathYear - $birthYear) * $this->zoomfactor) - 2;
- }
-
- //set minimum width for single year lifespans
- if ($width < 10) {
- $width = 10;
- }
-
- $lifespan = "<span dir=\"ltr\">$birthYear-</span>";
- $deathReal = $value->getDeathDate()->isOK();
- $birthReal = $value->getBirthDate()->isOK();
- if ($value->isDead() && $deathReal) {
- $lifespan .= "<span dir=\"ltr\">$deathYear</span>";
- }
- $lifespannumeral = $deathYear - $birthYear;
+ $bdate = $this->getCalendarDate($person->getEstimatedBirthDate()->minimumJulianDay());
+ $ddate = $this->getCalendarDate($person->getEstimatedDeathDate()->maximumJulianDay());
+ $birthYear = $bdate->y;
+ $age = min($ddate->y, $this->currentYear) - $birthYear; // truncate the bar at the current year
+ $width = max(9, $age * self::pixelsPerYear); // min width is width of sex icon
+ $startPos = ($birthYear - $this->timelineMinYear) * self::pixelsPerYear;
//-- calculate a good Y top value
- $Y = $top;
- $Z = $zindex;
+ $Y = self::chartTop;
$ready = false;
while (!$ready) {
if (!isset($rows[$Y])) {
$ready = true;
$rows[$Y]["x1"] = $startPos;
$rows[$Y]["x2"] = $startPos + $width;
- $rows[$Y]["z"] = $zindex;
} else {
if ($rows[$Y]["x1"] > $startPos + $width) {
$ready = true;
$rows[$Y]["x1"] = $startPos;
- $Z = $rows[$Y]["z"];
} elseif ($rows[$Y]["x2"] < $startPos) {
$ready = true;
$rows[$Y]["x2"] = $startPos + $width;
- $Z = $rows[$Y]["z"];
} else {
- //move down 25 pixels
- if ($this->zoomfactor > 10) {
- $Y += 25 + $this->zoomfactor;
- } else {
- $Y += 25;
- }
+ //move down a line
+ $Y += self::barSpacing;
}
}
}
- //Need to calculate each event and the spacing between them
- // event1 distance will be event - birthyear that will be the distance. then each distance will chain off that
-
- //$event[][] = {"Cell 1 will hold events"}{"cell2 will hold time between that and the next value"};
- $facts = $value->getFacts();
- foreach ($value->getSpouseFamilies() as $family) {
+ $facts = $person->getFacts();
+ foreach ($person->getSpouseFamilies() as $family) {
foreach ($family->getFacts() as $fact) {
$facts[] = $fact;
}
}
- $unparsedEvents = array();
+ Functions::sortFacts($facts);
- foreach ($facts as $fact) {
- if (!in_array($fact->getTag(), $this->nonfacts)) {
- $unparsedEvents[] = $fact;
- }
- }
- Functions::sortFacts($unparsedEvents);
+ $acceptedFacts = array_filter($facts, function ($fact) {
+ return (in_array($fact->getTag(), $this->facts) && $fact->getDate()->isOK()) ||
+ (($this->place_obj || $this->startDate) && $this->checkFact($fact));
+ });
- $eventinformation = array();
- foreach ($unparsedEvents as $val) {
- $date = $val->getDate();
- if (!empty($date)) {
- $fact = $val->getTag();
- $yearsin = $date->minimumDate()->y - $birthYear;
- if ($lifespannumeral == 0) {
- $lifespannumeral = 1;
- }
- $eventwidth = ($yearsin / $lifespannumeral) * 100; // percent of the lifespan before the event occured used for determining div spacing
- // figure out some schema
- $evntwdth = $eventwidth . "%";
- //-- if the fact is a generic EVENt then get the qualifying TYPE
- if ($fact == "EVEN") {
- $fact = $val->getAttribute('TYPE');
- }
- $trans = GedcomTag::getLabel($fact);
- if (isset($eventinformation[$evntwdth])) {
- $eventinformation[$evntwdth] .= '<br>' . $trans . '<br>' . strip_tags($date->display()) . ' ' . $val->getPlace()->getFullName();
- } else {
- $eventinformation[$evntwdth] = $fact . '-fact, ' . $trans . '<br>' . strip_tags($date->display()) . ' ' . $val->getPlace()->getFullName();
- }
+ $eventList = array();
+ foreach ($acceptedFacts as $fact) {
+ $tag = $fact->getTag();
+ //-- if the fact is a generic EVENt then get the qualifying TYPE
+ if ($tag == "EVEN") {
+ $tag = $fact->getAttribute('TYPE');
}
+ $eventList[] = array('label' => GedcomTag::getLabel($tag),
+ 'date' => $fact->getDate()->display(),
+ 'place' => $fact->getPlace()->getFullName()
+ );
}
+ $direction = I18N::direction() === 'ltr' ? 'left' : 'right';
+ $lifespan = ' ' . $person->getLifeSpan(); // put the space here so its included in the length calcs
+ $sex = $person->getSex();
+ $popupClass = strtr($sex, array('M' => '', 'U' => 'NN'));
+ $color = $sex === 'U' ? '' : sprintf("background-color: %s", $this->colors[$sex]->getNextColor());
- $bdate = $value->getEstimatedBirthDate();
- $ddate = $value->getEstimatedDeathDate();
- if ($width > ($minlength + 110)) {
- echo "<div id=\"bar_", $value->getXref(), "\" style=\"position: absolute; top:", $Y, "px; left:", $startPos, "px; width:", $width, "px; height:", $height, "px; background-color:", $this->color, "; border: solid blue 1px; z-index:$Z;\">";
- foreach ($eventinformation as $evtwidth => $val) {
- echo "<div style=\"position:absolute; left:", $evtwidth, ";\"><a class=\"showit\" href=\"#\" style=\"top:-2px; font-size:10px;\"><b>";
- $text = explode("-fact, ", $val);
- $fact = $text[0];
- echo '</b><span>', self::getAbbreviation($fact), '</span></a></div>';
- }
- $indiName = $value->getFullName();
- echo '<table><tr><td width="15"><a class="showit" href="#"><b>';
- echo self::getAbbreviation('BIRT');
- echo '</b><span>', $value->getSexImage(), $indiName, '<br>', GedcomTag::getLabel('BIRT'), ' ', strip_tags($bdate->display()), ' ', $value->getBirthPlace(), '</span></a>',
- '<td align="left" width="100%"><a href="', $value->getHtmlUrl(), '">', $value->getSexImage(), $indiName, ' ', $lifespan, ' </a></td>',
- '<td width="15">';
- if ($value->isDead()) {
- if ($deathReal || $value->isDead()) {
- echo '<a class="showit" href="#"><b>';
- echo self::getAbbreviation('DEAT');
- if (!$deathReal) {
- echo '*';
- }
- echo '</b><span>' . $value->getSexImage() . $indiName . '<br>' . GedcomTag::getLabel('DEAT') . ' ' . strip_tags($ddate->display()) . ' ' . $value->getDeathPlace() . '</span></a>';
- }
- }
- echo '</td></tr></table>';
- echo '</div>';
+ // following lines are a nasty method of approximating
+ // the width of a string in pixels from the character count
+ $name_length = mb_strlen(strip_tags($person->getFullName())) * 6.5;
+ $short_name_length = mb_strlen(strip_tags($person->getShortName())) * 6.5;
+ $lifespan_length = mb_strlen(strip_tags($lifespan)) * 6.5;
+
+ if ($width > $name_length + $lifespan_length) {
+ $printName = $person->getFullName();
+ $abbrLifespan = $lifespan;
+ } elseif ($width > $name_length) {
+ $printName = $person->getFullName();
+ $abbrLifespan = '&hellip;';
+ } elseif ($width > $short_name_length) {
+ $printName = $person->getShortName();
+ $abbrLifespan = '';
} else {
- if ($width > $minlength + 5) {
- echo '<div style="text-align: left; position: absolute; top:', $Y, 'px; left:', $startPos, 'px; width:', $width, 'px; height:', $height, 'px; background-color:', $this->color, '; border: solid blue 1px; z-index:', $Z, '">';
- foreach ($eventinformation as $evtwidth => $val) {
- echo '<div style="position:absolute; left:', $evtwidth, ' "><a class="showit" href="#" style="top:-2px; font-size:10px;"><b>';
- $text = explode("-fact,", $val);
- $fact = $text[0];
- echo '</b><span>' . self::getAbbreviation($fact) . '</span></a></div>';
- }
- $indiName = $value->getFullName();
- echo '<table dir="ltr"><tr><td width="15"><a class="showit" href="#"><b>';
- echo self::getAbbreviation('BIRT');
- if (!$birthReal) {
- echo '*';
- }
- echo '</b><span>' . $value->getSexImage() . $indiName . '<br>' . GedcomTag::getLabel('BIRT') . ' ' . strip_tags($bdate->display(false)) . ' ' . $value->getBirthPlace() . '</span></a></td>' . '<td align="left" width="100%"><a href="' . $value->getHtmlUrl() . '">' . $value->getSexImage() . $indiName . '</a></td>' .
- '<td width="15">';
- if ($value->isDead()) {
- if ($deathReal || $value->isDead()) {
- echo '<a class="showit" href="#"><b>';
- echo self::getAbbreviation('DEAT');
- if (!$deathReal) {
- echo "*";
- }
- echo '</b><span>' . $value->getSexImage() . $indiName . '<br>' . GedcomTag::getLabel('DEAT') . ' ' . strip_tags($ddate->display()) . ' ' . $value->getDeathPlace() . '</span></a>';
- }
- }
- echo '</td></tr></table>';
- echo '</div>';
- } else {
- echo '<div style="text-align: left; position: absolute;top:', $Y, 'px; left:', $startPos, 'px;width:', $width, 'px; height:', $height, 'px; background-color:', $this->color, '; border: solid blue 1px; z-index:', $Z, '">';
- $indiName = $value->getFullName();
- echo '<a class="showit" href="' . $value->getHtmlUrl() . '"><b>';
- echo self::getAbbreviation('BIRT');
- echo '</b><span>' . $value->getSexImage() . $indiName . '<br>' . GedcomTag::getLabel('BIRT') . ' ' . strip_tags($bdate->display()) . ' ' . $value->getBirthPlace() . '<br>';
- foreach ($eventinformation as $val) {
- $text = explode('-fact,', $val);
- $val = $text[1];
- echo $val . "<br>";
- }
- if ($value->isDead() && $deathReal) {
- echo GedcomTag::getLabel('DEAT') . " " . strip_tags($ddate->display()) . " " . $value->getDeathPlace();
- }
- echo '</span></a>';
- echo '</div>';
- }
+ $printName = '';
+ $abbrLifespan = '';
}
- $zindex--;
- if ($maxX < $startPos + $width) {
- $maxX = $startPos + $width;
- }
- if ($maxY < $Y) {
- $maxY = $Y;
+ // Bar framework
+ printf("
+ <div class='person_box%s' style='top:%spx; %s:%spx; width:%spx; %s'>
+ <div class='itr'>%s %s %s
+ <div class='popup person_box%s'>
+ <div>
+ <a href='%s'>%s%s</a>
+ </div>",
+ $popupClass, $Y, $direction, $startPos, $width, $color,
+ $person->getSexImage(), $printName, $abbrLifespan,
+ $popupClass,
+ $person->getHtmlUrl(), $person->getFullName(), $lifespan
+ );
+
+ // Add events to popup
+ foreach ($eventList as $event) {
+ printf("<div>%s: %s %s</div>", $event['label'], $event['date'], $event['place']);
}
- }
+ echo
+ "</div>" . // class='popup'
+ "</div>" . // class='itr'
+ "</div>"; // class=$popupclass
+ $maxY = max($maxY, $Y);
+ }
return $maxY;
}
/**
- * The significant individual on this page is the first one.
+ * Function checkFact
*
- * @return Individual
+ * Does this fact meet the search criteria?
+ * @param Fact $fact
+ * @return bool
*/
- public function getSignificantIndividual() {
- if ($this->people) {
- return $this->people[0];
- } else {
- return parent::getSignificantIndividual();
+ private function checkFact(Fact $fact) {
+ $valid = !in_array($fact->getTag(), $this->nonfacts);
+ if ($valid && $this->place_obj) {
+ $valid = stripos($fact->getPlace()->getGedcomName(), $this->place_obj->getGedcomName()) !== false;
}
+ if ($valid && $this->startDate) {
+ if ($this->strictDate && $this->calendar !== $this->defaultCalendar) {
+ $valid = stripos($fact->getAttribute('DATE'), $this->calendar) !== false;
+ }
+ if ($valid) {
+ $date = $fact->getDate();
+ $valid = $date->isOK() && Date::compare($date, $this->startDate) >= 0 && Date::compare($date, $this->endDate) <= 0;
+ }
+ }
+ return $valid;
}
/**
- * Search for people who had events in a given year range
- *
- * @param int $startyear
- * @param int $endyear
- *
- * @return Individual[]
+ * Function getCalendarDate
+ * @param int $date
+ * @return object
*/
- private static function searchIndividualsInYearRange($startyear, $endyear) {
- global $WT_TREE;
-
- // At present, the lifespan chart is driven by Gregorian years.
- // We ought to allow it to work with other calendars...
- $gregorian_calendar = new GregorianCalendar;
-
- $startjd = $gregorian_calendar->ymdToJd($startyear, 1, 1);
- $endjd = $gregorian_calendar->ymdToJd($endyear, 12, 31);
-
- $rows = Database::prepare(
-"SELECT DISTINCT i_id AS xref, i_file AS gedcom_id, i_gedcom AS gedcom" .
- " FROM `##individuals`" .
- " JOIN `##dates` ON i_id=d_gid AND i_file=d_file" .
- " WHERE i_file=? AND d_julianday1 BETWEEN ? AND ?"
- )->execute(array($WT_TREE->getTreeId(), $startjd, $endjd))->fetchAll();
-
- $list = array();
- foreach ($rows as $row) {
- $list[] = Individual::getInstance($row->xref, $WT_TREE, $row->gedcom);
+ private function getCalendarDate($date) {
+ switch ($this->calendar) {
+ case 'julian':
+ $caldate = new JulianDate($date);
+ break;
+ case 'french':
+ $caldate = new FrenchDate($date);
+ break;
+ case 'jewish':
+ $caldate = new JewishDate($date);
+ break;
+ case 'hijri':
+ $caldate = new HijriDate($date);
+ break;
+ case 'jalali':
+ $caldate = new JalaliDate($date);
+ break;
+ default:
+ $caldate = new GregorianDate($date);
}
-
- return $list;
+ return $caldate;
}
/**
- * Generate a very short abbreviation for birth/marriage/death
- *
- * @param string $tag
- *
+ * Function getCalendarOptionList
* @return string
*/
- private static function getAbbreviation($tag) {
- switch ($tag) {
- case 'BIRT':
- return I18N::translateContext('Abbreviation for birth', 'b.');
- case 'MARR':
- return I18N::translateContext('Abbreviation for marriage', 'm.');
- case 'DEAT':
- return I18N::translateContext('Abbreviation for death', 'd.');
- default:
- return mb_substr(GedcomTag::getLabel($tag), 0, 1); // Just use the first letter of the full fact
+ public function getCalendarOptionList() {
+ $html = '';
+ foreach (Date::calendarNames() as $calendar => $name) {
+ $selected = $this->calendar === $calendar ? 'selected' : '';
+ $html .= sprintf("<option dir='auto' value='%s' %s>%s</option>", $calendar, $selected, $name);
}
+ return $html;
}
}