/** * Create Timeline Image * * @param $date * @param $draw if FALSE return coordinates only, for imagemap * @param $translate for translate * @param $img_type [normal | small] * @return image */ public function createTimelineImage($date, $draw = true, $translate = null, $img_type = 'normal') { $this->GetDataTimeline($date); if (empty($this->atime)) { // Nothing data to graph return; } $this->calculateImageData($img_type); if (!$draw) { $img_map = array(); } $ttf_font_error = 0; // созд-е пустого холста // Create a new true color image : // resource imagecreatetruecolor ( int width, int height ) $img = ImageCreateTrueColor($this->img_width, $this->img_height); if (!$img) { // Handle the error $this->view->result = null; $this->getResponse()->setHeader('Content-Type', 'text/html; charset=utf-8'); throw new Zend_Exception('Internal ERROR: ImageCreateTrueColor'); return; } // цвета $white = ImageColorAllocate($img, 255, 255, 255); $black = ImageColorAllocate($img, 0, 0, 0); $blue = ImageColorAllocate($img, 0x49, 0x74, 0xbc0); // массив цветов для полос $acolor = array(ImageColorAllocate($img, 0xea, 0xea, 0x33), ImageColorAllocate($img, 0xff, 0xba, 0xba), ImageColorAllocate($img, 0xd0, 0xae, 0xff), ImageColorAllocate($img, 0x9d, 0xed, 0x0), ImageColorAllocate($img, 0xdc, 0xdc, 0xdc)); $acolor_count = count($acolor); // кол-во цветов для рисования полос $bg_color = $white; $text_color = $black; // создание фона для рисования // Draw a filled rectangle : bool imagefilledrectangle ( resource image, int x1, int y1, int x2, int y2, int color ) if ($draw) { ImageFilledRectangle($img, 0, 0, $this->img_width, $this->img_height, $bg_color); } // контур фона // Draw a rectangle : bool imagerectangle ( resource image, int x1, int y1, int x2, int y2, int color ) if ($draw) { ImageRectangle($img, 0, 0, $this->img_width - 1, $this->img_height - 1, $blue); } // --------------------------------- вычерчивание координатной сетки --------------------------------------- // ось X // Draw a line : // bool imageline ( resource image, int x1, int y1, int x2, int y2, int color ) // $y0, $x0 - начало координат $y0 = $y2 = $this->img_height - $this->margin_bottom - $this->margin_top + $this->bar_space; $x0 = $this->margin_left; if ($draw) { ImageLine($img, $x0, $y0, $this->img_width - $this->margin_right, $y2, $blue); } // ось X // вертикальные линии - часы // пунктирная линия $style_dash = array_merge(array_fill(0, 1, $blue), array_fill(0, 3, IMG_COLOR_TRANSPARENT)); if ($draw) { ImageSetStyle($img, $style_dash); } $hour1 = ceil(($this->img_width - $x0 - $this->margin_right) / 24); // шаг засечек или 1 час в пикселах $y2 = 0; for ($i = 0; $i <= 23; $i++) { $x1 = $x0 + $i * $hour1; ImageLine($img, $x1, $y0, $x1, $y2, IMG_COLOR_STYLED); } if ($img_type == 'normal') { // подписи к оси X $y1 = $this->img_height - $this->margin_bottom - $this->margin_top + $this->bar_space + $this->font_size; for ($i = 0; $i <= 23; $i++) { // Draw a string horizontally : // bool imagestring ( resource image, int font, int x, int y, string sring, int color ) // Can be 1, 2, 3, 4, 5 for built-in fonts (where higher numbers corresponding to larger fonts) // для учета кол-ва символов в цифрах часов if ($i < 10) { $div2 = 10; } else { $div2 = 5; } $x1 = $x0 - $div2 + $i * $hour1; if ($draw) { ImageString($img, 4, $x1, $y1, sprintf("% 2d", $i), $blue); } } // X axis title / название оси X if (empty($this->font_name)) { // use system fixed font / ось подписываем встроенным шрифтом if ($draw) { ImageString($img, $this->fixfont, floor($this->img_width / 2), $this->img_height - floor(($this->img_height - $y0) / 2), "Hours", $blue); } // do not to translate (перевод не нужен) } else { if ($draw) { @($ares = ImageTtfText($img, $this->font_size, 0, floor($this->img_width / 2), $this->img_height - floor(($this->img_height - $y0) / 3), $blue, $this->font_name, $translate->_("Hours"))); if (empty($ares)) { $ttf_font_error = 1; // TTF font not loaded/found if ($draw) { ImageString($img, 4, 5, 5, "Font " . $this->font_name . " not loaded/found.", $black); } // do not to translate (перевод не нужен) } } } } //---------------- draw graph (рисуем график) -------------------------------------------- $yt = $this->margin_top; $c = 0; for ($i = 0; $i <= $this->bar_count - 1; $i++) { $str = '(' . $this->atime[$i]['jobid'] . ') ' . $this->atime[$i]['name']; // для заданий не уложившихся в сутки, рисуем знаки с определенной стороны switch ($this->atime[$i]['flag']) { case -1: $str = '<--' . $str; // задание началось ранее break; case 1: $str = $str . '-->'; // задание закончилось позднее break; case 2: $str = '<--' . $str . '-->'; // задание началось ранее и закончилось позднее (очень длинное задание) break; } // Draw a filled rectangle: // bool imagefilledrectangle ( resource image, int x1, int y1, int x2, int y2, int color ) // полосы $yr1 = $yt - ceil($this->font_size / 2) - ceil($this->bar_height / 2); $yr2 = $yr1 + $this->bar_height; $xr1 = $x0 + floor($hour1 * $this->atime[$i]['h1']); $xr2 = $x0 + floor($hour1 * $this->atime[$i]['h2']); // если слишком маленькая полоса if ($xr2 - $xr1 < 3) { $xr2 = $xr1 + 3; } // цвет if ($c > $acolor_count - 1) { $c = 0; } // draw restangle if ($draw) { ImageFilledRectangle($img, $xr1, $yr1, $xr2, $yr2, $acolor[$c++]); } // Write text to the image using TrueType fonts : // array imagettftext ( resource image, float size, float angle, int x, int y, int color, string fontfile, string text ) // x - The coordinates given by x and y will define the basepoint of the first character // (roughly the lower-left corner of the character). // This is different from the imagestring(), where x and y define the upper-left corner of the first character. // For example, "top left" is 0, 0. // size - The font size. Depending on your version of GD, this should be specified as the pixel size (GD1) or point size (GD2) // **************** text ***************** // array imagettfbbox ( float size, float angle, string fontfile, string text ) // где расположить текст // расчет координат текста // левая координата X = $abox[0], правая X = $abox[2] if (!$ttf_font_error && !empty($this->font_name)) { // TTF font loaded OK $abox = ImageTtfBbox($this->font_size, 0, $this->font_name, $str); $xt = $xr1 + $this->margin_text_left; if ($xt + $abox[2] > $this->img_width) { $xt = $xr2 - $abox[2] - $this->margin_text_left; if (!$draw) { $xt > $xr2 ? $x2 = $xt : ($x2 = $xr2); } // coordinates for imagemap } else { if (!$draw) { $xt + $abox[2] > $xr2 ? $x2 = $xt + $abox[2] : ($x2 = $xr2); } // coordinates for imagemap } // draw text if ($draw) { ImageTtfText($img, $this->font_size, 0, $xt, $yt, $text_color, $this->font_name, $str); } } else { // fix font $lenfix = strlen($str) * imagefontwidth($this->fixfont); if ($xr1 + $lenfix > $this->img_width) { $xt = $xr2 - $lenfix - $this->margin_text_left; if (!$draw) { $xt > $xr2 ? $x2 = $xt : ($x2 = $xr2); } // coordinates for imagemap } else { $xt = $xr1; if (!$draw) { $xt + $lenfix > $xr2 ? $x2 = $xt + $lenfix : ($x2 = $xr2); } // coordinates for imagemap } // draw text if ($draw) { ImageString($img, $this->fixfont, $xt, $yr1, $str, $text_color); } } // save coordinates if (!$draw) { $xt < $xr1 ? $x1 = $xt : ($x1 = $xr1); $img_map[$i]['jobid'] = $this->atime[$i]['jobid']; $img_map[$i]['name'] = $this->atime[$i]['name']; $img_map[$i]['short_desc'] = $this->atime[$i]['short_desc']; $img_map[$i]['x1'] = $x1; $img_map[$i]['y1'] = $yr1; $img_map[$i]['x2'] = $x2; $img_map[$i]['y2'] = $yr2; } $yt = $yt + $this->bar_height + $this->bar_space; } if ($draw) { return $img; } else { return $img_map; } }
/** * print ancestors on a fan chart * * @param array $treeid ancestry pid * @param int $fanw fan width in px (default=640) * @param int $fandeg fan size in deg (default=270) */ function print_fan_chart($treeid, $fanw = 640, $fandeg = 270) { global $PEDIGREE_GENERATIONS, $fan_width, $fan_style; global $name, $pgv_lang, $SHOW_ID_NUMBERS, $view, $TEXT_DIRECTION; global $stylesheet, $print_stylesheet; global $PGV_IMAGE_DIR, $PGV_IMAGES, $LINK_ICONS, $GEDCOM; // check for GD 2.x library if (!defined("IMG_ARC_PIE")) { print "<span class=\"error\">" . $pgv_lang["gd_library"] . "</span>"; print " <a href=\"" . $pgv_lang["gd_helplink"] . "\"><img src=\"" . $PGV_IMAGE_DIR . "/" . $PGV_IMAGES["help"]["small"] . "\" class=\"icon\" alt=\"\" /></a><br /><br />"; return false; } if (!function_exists("ImageTtfBbox")) { print "<span class=\"error\">" . $pgv_lang["gd_freetype"] . "</span>"; print " <a href=\"" . $pgv_lang["gd_helplink"] . "\"><img src=\"" . $PGV_IMAGE_DIR . "/" . $PGV_IMAGES["help"]["small"] . "\" class=\"icon\" alt=\"\" /></a><br /><br />"; return false; } // parse CSS file include "includes/cssparser.inc.php"; $css = new cssparser(false); if ($view == "preview") { $css->Parse($print_stylesheet); } else { $css->Parse($stylesheet); } // check for fontfile $fontfile = $css->Get(".fan_chart", "font-family"); $fontsize = $css->Get(".fan_chart", "font-size"); $fontfile = str_replace("url(", "", $fontfile); $fontfile = str_replace(")", "", $fontfile); if (!file_exists($fontfile)) { if (!empty($fontfile)) { print "<span class=\"error\">" . $pgv_lang["fontfile_error"] . " : {$fontfile}</span>"; } $fontfile = "./includes/fonts/DejaVuSans.ttf"; } if ($fontfile[0] != '/') { $fontfile = dirname(__FILE__) . "/" . $fontfile; } if (!file_exists($fontfile)) { print "<span class=\"error\">" . $pgv_lang["fontfile_error"] . " : {$fontfile}</span>"; return false; } if (intval($fontsize) < 2) { $fontsize = 7; } $treesize = count($treeid); if ($treesize < 1) { return; } // generations count $gen = log($treesize) / log(2) - 1; $sosa = $treesize - 1; // fan size if ($fandeg == 0) { $fandeg = 360; } $fandeg = min($fandeg, 360); $fandeg = max($fandeg, 90); $cx = $fanw / 2 - 1; // center x $cy = $cx; // center y $rx = $fanw - 1; $rw = $fanw / ($gen + 1); $fanh = $fanw; // fan height if ($fandeg == 180) { $fanh = round($fanh * ($gen + 1) / ($gen * 2)); } if ($fandeg == 270) { $fanh = round($fanh * 0.86); } $scale = $fanw / 640; // image init $image = ImageCreate($fanw, $fanh); $black = ImageColorAllocate($image, 0, 0, 0); $white = ImageColorAllocate($image, 0xff, 0xff, 0xff); ImageFilledRectangle($image, 0, 0, $fanw, $fanh, $white); ImageColorTransparent($image, $white); $rgb = $css->Get(".fan_chart", "color"); if (empty($rgb)) { $rgb = "#000000"; } $color = ImageColorAllocate($image, hexdec(substr($rgb, 1, 2)), hexdec(substr($rgb, 3, 2)), hexdec(substr($rgb, 5, 2))); $rgb = $css->Get(".fan_chart", "background-color"); if (empty($rgb)) { $rgb = "#EEEEEE"; } $bgcolor = ImageColorAllocate($image, hexdec(substr($rgb, 1, 2)), hexdec(substr($rgb, 3, 2)), hexdec(substr($rgb, 5, 2))); $rgb = $css->Get(".fan_chart_box", "background-color"); if (empty($rgb)) { $rgb = "#D0D0AC"; } $bgcolorM = ImageColorAllocate($image, hexdec(substr($rgb, 1, 2)), hexdec(substr($rgb, 3, 2)), hexdec(substr($rgb, 5, 2))); $rgb = $css->Get(".fan_chart_boxF", "background-color"); if (empty($rgb)) { $rgb = "#D0ACD0"; } $bgcolorF = ImageColorAllocate($image, hexdec(substr($rgb, 1, 2)), hexdec(substr($rgb, 3, 2)), hexdec(substr($rgb, 5, 2))); // imagemap $imagemap = "<map id=\"fanmap\" name=\"fanmap\">"; // loop to create fan cells while ($gen >= 0) { // clean current generation area $deg2 = 360 + ($fandeg - 180) / 2; $deg1 = $deg2 - $fandeg; ImageFilledArc($image, $cx, $cy, $rx, $rx, $deg1, $deg2, $bgcolor, IMG_ARC_PIE); $rx -= 3; // calculate new angle $p2 = pow(2, $gen); $angle = $fandeg / $p2; $deg2 = 360 + ($fandeg - 180) / 2; $deg1 = $deg2 - $angle; // special case for rootid cell if ($gen == 0) { $deg1 = 90; $deg2 = 360 + $deg1; } // draw each cell while ($sosa >= $p2) { $pid = $treeid[$sosa]; if (!empty($pid)) { $indirec = find_person_record($pid); if (!$indirec) { $indirec = find_updated_record($pid); } if ($sosa % 2) { $bg = $bgcolorF; } else { $bg = $bgcolorM; } if ($sosa == 1) { $bg = $bgcolor; // sex unknown if (preg_match("/1 SEX F/", $indirec) > 0) { $bg = $bgcolorF; } else { if (preg_match("/1 SEX M/", $indirec) > 0) { $bg = $bgcolorM; } } } ImageFilledArc($image, $cx, $cy, $rx, $rx, $deg1, $deg2, $bg, IMG_ARC_PIE); $person = Person::getInstance($pid); $name = $person->getFullName(); $addname = $person->getAddName(); //$name = str_replace(array('<span class="starredname">', '</span>'), '', $name); //$addname = str_replace(array('<span class="starredname">', '</span>'), '', $addname); //$name = str_replace(array('<span class="starredname">', '</span>'), array('<u>', '</u>'), $name); //@@ //$addname = str_replace(array('<span class="starredname">', '</span>'), array('<u>', '</u>'), $addname); //@@ // ToDo - print starred names underlined - 1985154 // Todo - print Arabic letters combined - 1360209 $text = reverseText($name) . "\n"; if (!empty($addname)) { $text .= reverseText($addname) . "\n"; } if (displayDetailsById($pid)) { $birthrec = get_sub_record(1, "1 BIRT", $indirec); $ct = preg_match("/2 DATE.*(\\d\\d\\d\\d)/", $birthrec, $match); if ($ct > 0) { $text .= trim($match[1]); } $deathrec = get_sub_record(1, "1 DEAT", $indirec); $ct = preg_match("/2 DATE.*(\\d\\d\\d\\d)/", $deathrec, $match); if ($ct > 0) { $text .= "-" . trim($match[1]); } } $text = unhtmlentitiesrtl($text); $text = strip_tags($text); //Do we still need? // split and center text by lines $wmax = floor($angle * 7 / $fontsize * $scale); $wmax = min($wmax, 35 * $scale); if ($gen == 0) { $wmax = min($wmax, 17 * $scale); } $text = split_align_text($text, $wmax); // text angle $tangle = 270 - ($deg1 + $angle / 2); if ($gen == 0) { $tangle = 0; } // calculate text position $bbox = ImageTtfBbox((double) $fontsize, 0, $fontfile, $text); $textwidth = $bbox[4]; $deg = $deg1 + 0.44; if ($deg2 - $deg1 > 40) { $deg = $deg1 + ($deg2 - $deg1) / 11; } if ($deg2 - $deg1 > 80) { $deg = $deg1 + ($deg2 - $deg1) / 7; } if ($deg2 - $deg1 > 140) { $deg = $deg1 + ($deg2 - $deg1) / 4; } if ($gen == 0) { $deg = 180; } $rad = deg2rad($deg); $mr = ($rx - $rw / 4) / 2; if ($gen > 0 and $deg2 - $deg1 > 80) { $mr = $rx / 2; } $tx = $cx + $mr * cos($rad); $ty = $cy - $mr * -sin($rad); if ($sosa == 1) { $ty -= $mr / 2; } // print text ImageTtfText($image, (double) $fontsize, $tangle, $tx, $ty, $color, $fontfile, $text); $imagemap .= "<area shape=\"poly\" coords=\""; // plot upper points $mr = $rx / 2; $deg = $deg1; while ($deg <= $deg2) { $rad = deg2rad($deg); $tx = round($cx + $mr * cos($rad)); $ty = round($cy - $mr * -sin($rad)); $imagemap .= "{$tx}, {$ty}, "; $deg += ($deg2 - $deg1) / 6; } // plot lower points $mr = ($rx - $rw) / 2; $deg = $deg2; while ($deg >= $deg1) { $rad = deg2rad($deg); $tx = round($cx + $mr * cos($rad)); $ty = round($cy - $mr * -sin($rad)); $imagemap .= "{$tx}, {$ty}, "; $deg -= ($deg2 - $deg1) / 6; } // join first point $mr = $rx / 2; $deg = $deg1; $rad = deg2rad($deg); $tx = round($cx + $mr * cos($rad)); $ty = round($cy - $mr * -sin($rad)); $imagemap .= "{$tx}, {$ty}"; // add action url $tempURL = "javascript://" . htmlspecialchars(strip_tags($name)); if ($SHOW_ID_NUMBERS) { $tempURL .= " (" . $pid . ")"; } $imagemap .= "\" href=\"{$tempURL}\" "; $tempURL = "fanchart.php?rootid={$pid}&PEDIGREE_GENERATIONS={$PEDIGREE_GENERATIONS}&fan_width={$fan_width}&fan_style={$fan_style}"; if (!empty($view)) { $tempURL .= "&view={$view}"; } $count = 0; $lbwidth = 200; print "<div id=\"I" . $pid . "." . $count . "links\" style=\"position:absolute; >"; print "left:" . $tx . "px; top:" . $ty . "px; width: " . $lbwidth . "px; visibility:hidden; z-index:'100';\">"; print "<table class=\"person_box\"><tr><td class=\"details1\">"; print "<a href=\"individual.php?pid={$pid}\" class=\"name1\">" . PrintReady($name); if (!empty($addname)) { print "<br />" . PrintReady($addname); } print "</a>"; print "<br /><a href=\"pedigree.php?rootid={$pid}\" >" . $pgv_lang["index_header"] . "</a>"; print "<br /><a href=\"descendancy.php?pid={$pid}\" >" . $pgv_lang["descend_chart"] . "</a>"; if (PGV_USER_GEDCOM_ID) { print "<br /><a href=\"" . encode_url("relationship.php?pid1=" . PGV_USER_GEDCOM_ID . "&pid2={$pid}&ged={$GEDCOM}") . "\" onmouseover=\"clear_family_box_timeout('" . $pid . "." . $count . "');\" onmouseout=\"family_box_timeout('" . $pid . "." . $count . "');\">" . $pgv_lang["relationship_to_me"] . "</a>"; } print "<br /><a href=\"ancestry.php?rootid={$pid}\" onmouseover=\"clear_family_box_timeout('" . $pid . "." . $count . "');\" onmouseout=\"family_box_timeout('" . $pid . "." . $count . "');\">" . $pgv_lang["ancestry_chart"] . "</a>"; print "<br /><a href=\"compact.php?rootid={$pid}\" onmouseover=\"clear_family_box_timeout('" . $pid . "." . $count . "');\" onmouseout=\"family_box_timeout('" . $pid . "." . $count . "');\">" . $pgv_lang["compact_chart"] . "</a>"; print "<br /><a href=\"" . encode_url($tempURL) . "\" onmouseover=\"clear_family_box_timeout('" . $pid . "." . $count . "');\" onmouseout=\"family_box_timeout('" . $pid . "." . $count . "');\">" . $pgv_lang["fan_chart"] . "</a>"; print "<br /><a href=\"hourglass.php?pid={$pid}\" onmouseover=\"clear_family_box_timeout('" . $pid . "." . $count . "');\" onmouseout=\"family_box_timeout('" . $pid . "." . $count . "');\">" . $pgv_lang["hourglass_chart"] . "</a>"; if ($sosa >= 1) { $famids = find_sfamily_ids($pid); //-- make sure there is more than 1 child in the family with parents $cfamids = find_family_ids($pid); $num = 0; for ($f = 0; $f < count($cfamids); $f++) { $famrec = find_family_record($cfamids[$f]); if ($famrec) { $num += preg_match_all("/1\\s*CHIL\\s*@(.*)@/", $famrec, $smatch, PREG_SET_ORDER); } } if ($famids || $num > 1) { //-- spouse(s) and children for ($f = 0; $f < count($famids); $f++) { $famrec = find_family_record(trim($famids[$f])); if ($famrec) { $parents = find_parents($famids[$f]); if ($parents) { if ($pid != $parents["HUSB"]) { $spid = $parents["HUSB"]; } else { $spid = $parents["WIFE"]; } $person = Person::getInstance($spid); if ($person) { echo '<br /><a href="', $person->getLinkUrl(), '" class="name1">', $person->getFullName(), '</a>'; } } $num = preg_match_all("/1\\s*CHIL\\s*@(.*)@/", $famrec, $smatch, PREG_SET_ORDER); for ($i = 0; $i < $num; $i++) { $person = Person::getInstance($smatch[$i][1]); if ($person) { echo '<br /> <a href="', $person->getLinkUrl(), '" class="name1">< ', $person->getFullName(), '</a>'; } } } } //-- siblings for ($f = 0; $f < count($cfamids); $f++) { $famrec = find_family_record($cfamids[$f]); if ($famrec) { $num = preg_match_all("/1\\s*CHIL\\s*@(.*)@/", $famrec, $smatch, PREG_SET_ORDER); if ($num > 2) { print "<br /><span class=\"name1\">" . $pgv_lang["siblings"] . "</span>"; } if ($num == 2) { print "<br /><span class=\"name1\">" . $pgv_lang["sibling"] . "</span>"; } for ($i = 0; $i < $num; $i++) { $cpid = $smatch[$i][1]; if ($cpid != $pid) { $person = Person::getInstance($cpid); if ($person) { echo '<br /> <a href="', $person->getLinkUrl(), '" class="name1"> ', $person->getFullName(), '</a>'; } } } } } } } print "</td></tr></table>"; print "</div>"; $imagemap .= " onclick=\"show_family_box('" . $pid . "." . $count . "', 'relatives'); return false;\""; $imagemap .= " onmouseout=\"family_box_timeout('" . $pid . "." . $count . "'); return false;\""; $imagemap .= " alt=\"" . PrintReady(strip_tags($name)) . "\" title=\"" . PrintReady(strip_tags($name)) . "\" />"; } $deg1 -= $angle; $deg2 -= $angle; $sosa--; } $rx -= $rw; $gen--; } $imagemap .= "</map>"; echo $imagemap; // PGV banner ;-) ImageStringUp($image, 1, $fanw - 10, $fanh / 3, PGV_PHPGEDVIEW_URL, $color); // here we cannot send image to browser ('header already sent') // and we dont want to use a tmp file // step 1. save image data in a session variable ob_start(); ImagePng($image); $image_data = ob_get_contents(); ob_end_clean(); $image_data = serialize($image_data); unset($_SESSION['image_data']); $_SESSION['image_data'] = $image_data; // step 2. call imageflush.php to read this session variable and display image // note: arg "image_name=" is to avoid image miscaching $image_name = "V" . time(); unset($_SESSION[$image_name]); // statisticsplot.php uses this to hold a file name to send to browser $image_title = preg_replace("~<.*>~", "", $name) . " " . $pgv_lang["fan_chart"]; echo "<p align=\"center\" >"; echo "<img src=\"imageflush.php?image_type=png&image_name={$image_name}&height={$fanh}&width={$fanw}\" width=\"{$fanw}\" height=\"{$fanh}\" border=\"0\" alt=\"{$image_title}\" title=\"{$image_title}\" usemap=\"#fanmap\" />"; echo "</p>"; ImageDestroy($image); }
/** * print ancestors on a fan chart * @param array $treeid ancestry pid * @param int $fanw fan width in px (default=840) * @param int $fandeg fan size in deg (default=270) */ function print_fan_chart($treeid, $fanw = 840, $fandeg = 270) { global $dbh, $tree_id, $db_functions, $fontsize, $date_display; global $fan_style, $family_id; global $printing, $language, $selected_language; global $pers_var, $tree_prefix_quoted; global $china_message; // check for GD 2.x library if (!defined("IMG_ARC_PIE")) { print "ERROR: NO GD LIBRARY"; return false; } if (!function_exists("ImageTtfBbox")) { print "ERROR: NO GD LIBRARY"; return false; } if (intval($fontsize) < 2) { $fontsize = 7; } $treesize = count($treeid); if ($treesize < 1) { return; } // generations count $gen = log($treesize) / log(2) - 1; $sosa = $treesize - 1; // fan size if ($fandeg == 0) { $fandeg = 360; } $fandeg = min($fandeg, 360); $fandeg = max($fandeg, 90); $cx = $fanw / 2 - 1; // center x $cy = $cx; // center y $rx = $fanw - 1; $rw = $fanw / ($gen + 1); $fanh = $fanw; // fan height if ($fandeg == 180) { $fanh = round($fanh * ($gen + 1) / ($gen * 2)); } if ($fandeg == 270) { $fanh = round($fanh * 0.86); } $scale = $fanw / 840; // image init $image = ImageCreate($fanw, $fanh); $black = ImageColorAllocate($image, 0, 0, 0); $white = ImageColorAllocate($image, 0xff, 0xff, 0xff); ImageFilledRectangle($image, 0, 0, $fanw, $fanh, $white); if ($printing == 1) { ImageColorTransparent($image, $white); } // *** Border colour *** $rgb = ""; if (empty($rgb)) { $rgb = "#6E6E6E"; } $grey = ImageColorAllocate($image, hexdec(substr($rgb, 1, 2)), hexdec(substr($rgb, 3, 2)), hexdec(substr($rgb, 5, 2))); // *** Text colour *** $rgb = ""; if (empty($rgb)) { $rgb = "#000000"; } $color = ImageColorAllocate($image, hexdec(substr($rgb, 1, 2)), hexdec(substr($rgb, 3, 2)), hexdec(substr($rgb, 5, 2))); // *** Background colour *** $rgb = ""; if (empty($rgb)) { $rgb = "#EEEEEE"; } $bgcolor = ImageColorAllocate($image, hexdec(substr($rgb, 1, 2)), hexdec(substr($rgb, 3, 2)), hexdec(substr($rgb, 5, 2))); // *** Man colour *** $rgb = ""; if (empty($rgb)) { $rgb = "#B2DFEE"; } $bgcolorM = ImageColorAllocate($image, hexdec(substr($rgb, 1, 2)), hexdec(substr($rgb, 3, 2)), hexdec(substr($rgb, 5, 2))); // *** wife colour *** $rgb = ""; if (empty($rgb)) { $rgb = "#FFE4C4"; } $bgcolorF = ImageColorAllocate($image, hexdec(substr($rgb, 1, 2)), hexdec(substr($rgb, 3, 2)), hexdec(substr($rgb, 5, 2))); // imagemap $imagemap = "<map id=\"fanmap\" name=\"fanmap\">"; // loop to create fan cells while ($gen >= 0) { // clean current generation area $deg2 = 360 + ($fandeg - 180) / 2; $deg1 = $deg2 - $fandeg; ImageFilledArc($image, $cx, $cy, $rx, $rx, $deg1, $deg2, $bgcolor, IMG_ARC_PIE); ImageFilledArc($image, $cx, $cy, $rx, $rx, $deg1, $deg2, $bgcolor, IMG_ARC_EDGED | IMG_ARC_NOFILL); $rx -= 3; // calculate new angle $p2 = pow(2, $gen); $angle = $fandeg / $p2; $deg2 = 360 + ($fandeg - 180) / 2; $deg1 = $deg2 - $angle; // special case for rootid cell if ($gen == 0) { $deg1 = 90; $deg2 = 360 + $deg1; } // draw each cell while ($sosa >= $p2) { $pid = $treeid[$sosa][0]; $birthyr = $treeid[$sosa][1]; $deathyr = $treeid[$sosa][4]; $fontpx = $fontsize; if ($sosa >= 16 and $fandeg == 180) { $fontpx = $fontsize - 1; } if ($sosa >= 32 and $fandeg != 180) { $fontpx = $fontsize - 1; } if (!empty($pid)) { if ($sosa % 2) { $bg = $bgcolorF; } else { $bg = $bgcolorM; } if ($sosa == 1) { if ($treeid[$sosa][5] == "F") { $bg = $bgcolorF; } else { if ($treeid[$sosa][5] == "M") { $bg = $bgcolorM; } else { $bg = $bgcolor; // sex unknown } } } ImageFilledArc($image, $cx, $cy, $rx, $rx, $deg1, $deg2, $bg, IMG_ARC_PIE); if ($gen != 0) { ImageFilledArc($image, $cx, $cy, $rx, $rx, $deg1, $deg2, $grey, IMG_ARC_EDGED | IMG_ARC_NOFILL); } else { ImageFilledArc($image, $cx, $cy, $rx, $rx, $deg1, $deg2, $grey, IMG_ARC_NOFILL); } $name = $pid; // check if string is RTL language- if it is, it has to be reversed later on by persian_log2vis() $rtlstr = 0; //if(preg_match('/(*UTF8)[א-ת]/',$name)!==0 OR preg_match('/(*UTF8)[أ-ى]/',$name)!==0) { if (preg_match('/(*UTF8)[א-ת]/', $name) === 1 or preg_match('/(*UTF8)[أ-ى]/', $name) === 1) { // this is either Hebrew, Arabic or Persian -> we have to reverse the text! $rtlstr = 1; } $fontfile = CMS_ROOTPATH . "include/fanchart/dejavusans.ttf"; // this default font serves: Latin,Hebrew,Arabic,Persian,Russian //if(preg_match('/(*UTF8)\p{Han}/',$name)!==0) { // String is Chinese so use a Chinese ttf font if present in the folder if (preg_match('/(*UTF8)\\p{Han}/', $name) === 1) { // String is Chinese so use a Chinese ttf font if present in the folder if (is_dir(CMS_ROOTPATH . "include/fanchart/chinese")) { $dh = opendir(CMS_ROOTPATH . "include/fanchart/chinese"); while (false !== ($filename = readdir($dh))) { //if (strtolower(substr($filename, -3)) == "ttf"){ if (strtolower(substr($filename, -3)) == "otf" or strtolower(substr($filename, -3)) == "ttf") { $fontfile = CMS_ROOTPATH . "include/fanchart/chinese/" . $filename; } } } if ($fontfile == CMS_ROOTPATH . "include/fanchart/dejavusans.ttf") { //no Chinese ttf file found $china_message = 1; } } $text = $name; // names $text2 = ""; // dates if ($date_display == 1) { // don't show dates } else { if ($date_display == 2) { //show years only // years only chosen but we also do this if no place in outer circles $text2 .= substr($birthyr, -4) . " - " . substr($deathyr, -4); } else { if ($date_display == 3) { //show full dates (but not in narrow outer circles!) if ($gen > 5) { $text2 .= substr($birthyr, -4) . " - " . substr($deathyr, -4); } else { if ($gen > 4 and $fan_style != 4) { $text2 .= substr($birthyr, -4) . " - " . substr($deathyr, -4); } else { // full dates if ($birthyr) { $text2 .= "b." . $birthyr . "\n"; } if ($deathyr) { $text2 .= "d." . $deathyr; } } } } } } // split and center text by lines $wmax = floor($angle * 7 / $fontpx * $scale); $wmax = min($wmax, 35 * $scale); //35 //$wmax = floor((90*$wmax)/100); if ($gen == 0) { $wmax = min($wmax, 17 * $scale); } //17 $text = split_align_text($text, $wmax, $rtlstr, 1, $gen); $text2 = split_align_text($text2, $wmax, $rtlstr, 0, $gen); if ($rtlstr == 1) { persian_log2vis($text); // converts persian, arab and hebrew text from logical to visual and reverses it } $text .= "\n" . $text2; // text angle $tangle = 270 - ($deg1 + $angle / 2); if ($gen == 0) { $tangle = 0; } // calculate text position $bbox = ImageTtfBbox((double) $fontpx, 0, $fontfile, $text); $textwidth = $bbox[4]; //4 $deg = $deg1 + 0.44; if ($deg2 - $deg1 > 40) { $deg = $deg1 + ($deg2 - $deg1) / 11; } // 11 if ($deg2 - $deg1 > 80) { $deg = $deg1 + ($deg2 - $deg1) / 7; } // 7 if ($deg2 - $deg1 > 140) { $deg = $deg1 + ($deg2 - $deg1) / 4; } // 4 if ($gen == 0) { $deg = 180; } $rad = deg2rad($deg); $mr = ($rx - $rw / 4) / 2; if ($gen > 0 and $deg2 - $deg1 > 80) { $mr = $rx / 2; } $tx = $cx + $mr * cos($rad); $ty = $cy - $mr * -sin($rad); if ($sosa == 1) { $ty -= $mr / 2; } // print text ImageTtfText($image, (double) $fontpx, $tangle, $tx, $ty, $color, $fontfile, $text); $imagemap .= "<area shape=\"poly\" coords=\""; // plot upper points $mr = $rx / 2; $deg = $deg1; while ($deg <= $deg2) { $rad = deg2rad($deg); $tx = round($cx + $mr * cos($rad)); $ty = round($cy - $mr * -sin($rad)); $imagemap .= "{$tx}, {$ty}, "; $deg += ($deg2 - $deg1) / 6; } // plot lower points $mr = ($rx - $rw) / 2; $deg = $deg2; while ($deg >= $deg1) { $rad = deg2rad($deg); $tx = round($cx + $mr * cos($rad)); $ty = round($cy - $mr * -sin($rad)); $imagemap .= "{$tx}, {$ty}, "; $deg -= ($deg2 - $deg1) / 6; } // join first point $mr = $rx / 2; $deg = $deg1; $rad = deg2rad($deg); $tx = round($cx + $mr * cos($rad)); $ty = round($cy - $mr * -sin($rad)); $imagemap .= "{$tx}, {$ty}"; if (CMS_SPECIFIC == "Joomla") { $imagemap .= "\" href=\"index.php?option=com_humo-gen&task=family&id=" . $treeid[$sosa][2] . "&main_person=" . $treeid[$sosa][3] . "\""; } else { $imagemap .= "\" href=\"family.php?id=" . $treeid[$sosa][2] . "&main_person=" . $treeid[$sosa][3] . "\""; } //NEW - add first spouse to base person's tooltip $spousename = ""; if ($gen == 0 and $treeid[1][2] != "") { // base person and has spouse if ($treeid[1][5] == "F") { $spouse = "fam_man"; } else { $spouse = "fam_woman"; } $spouse_result = $dbh->query("SELECT " . $spouse . " FROM humo_families\n\t\t\t\t\t\tWHERE fam_tree_id='" . $tree_id . "' AND fam_gedcomnumber='" . $treeid[1][2] . "'"); @($spouseDb = $spouse_result->fetch()); // fetch() with no parameter deaults to array which is what we want here @($spouse2Db = $db_functions->get_person($spouseDb[$spouse])); $spouse_cls = new person_cls(); $spouse_cls->construct($spouse2Db); $spname = $spouse_cls->person_name($spouse2Db); if ($treeid[1][5] == "F") { $spouse_lan = "SPOUSE_MALE"; } else { $spouse_lan = "SPOUSE_FEMALE"; } if ($spname != "") { $spousename = "\n(" . __($spouse_lan) . ": " . $spname["standard_name"] . ")"; } } $imagemap .= " alt=\"" . $pid . "\" title=\"" . $pid . $spousename . "\">"; } $deg1 -= $angle; $deg2 -= $angle; $sosa--; } $rx -= $rw; $gen--; } $imagemap .= "</map>"; echo $imagemap; $image_title = preg_replace("~<.*>~", "", $name) . " - " . __('RELOAD FANCHART WITH \'VIEW\' BUTTON ON THE LEFT'); echo "<p align=\"center\" >"; if (CMS_SPECIFIC == "Joomla") { ImagePng($image, CMS_ROOTPATH . "include/fanchart/tmpimg.png"); $ext = "?" . time(); // add random string to file to prevent loading from cache and then replacing which is not nice echo "<img src=\"index.php?option=com_humo-gen&task=fanimage&format=raw&nochache=" . $ext . "\" width=\"{$fanw}\" height=\"{$fanh}\" border=\"0\" alt=\"{$image_title}\" title=\"{$image_title}\" usemap=\"#fanmap\">"; } else { ob_start(); ImagePng($image); $image_data = ob_get_contents(); ob_end_clean(); $image_data = serialize($image_data); unset($_SESSION['image_data']); $_SESSION['image_data'] = $image_data; echo "<img src=\"include/fanchart/fanimage.php\" width=\"{$fanw}\" height=\"{$fanh}\" border=\"0\" alt=\"{$image_title}\" title=\"{$image_title}\" usemap=\"#fanmap\">"; } echo "</p>\n"; ImageDestroy($image); }