/** * {@inhericDoc} * @see \MyArtJaub\Webtrees\Mvc\View\AbstractView::renderContent() */ protected function renderContent() { $nb_found = $this->data->get('stats_gen_nb_found'); $nb_other = $this->data->get('stats_gen_nb_other'); $nb_unknown = $this->data->get('stats_gen_nb_unknown'); $perc_known = Functions::safeDivision($nb_found - $nb_other, $nb_found + $nb_unknown); $html = '<div id="geodispersion_summary"> <div class="maj-table center"> <div class="maj-row"> <div class="label">' . I18N::translate('Places found') . '</div> <div class="value">' . I18N::translate('%1$d (%2$s)', $nb_found - $nb_other, I18N::percentage($perc_known)) . '</div> </div>'; if ($nb_other > 0) { $perc_other = Functions::safeDivision($nb_other, $nb_found + $nb_unknown); $html .= '<div class="maj-row"> <div class="label">' . I18N::translate('Other places') . '</div> <div class="value">' . I18N::translate('%1$d (%2$s)', $nb_other, I18N::percentage($perc_other)) . '</div> </div>'; } $html .= '<div class="maj-row"> <div class="label">' . I18N::translate('Places not found') . '</div> <div class="value">' . I18N::translate('%1$d (%2$s)', $nb_unknown, I18N::percentage(1 - $perc_known)) . '</div> </div> </div> </div> <br/> <div id="geodispersion_data"> ' . $this->htmlAnalysisData() . ' </div>'; return $html; }
/** * {@inheritDoc} * @see \MyArtJaub\Webtrees\Module\GeoDispersion\Views\AbstractGeoAnalysisTabGeneralView::htmlAnalysisData() */ protected function htmlAnalysisData() { $results = $this->data->get('results'); $analysis_level = $this->data->get('analysis_level'); $nb_found = $this->data->get('stats_gen_nb_found'); $nb_other = $this->data->get('stats_gen_nb_other'); $i = 1; $previous_nb = 0; $html = '<div class="maj-table center">'; foreach ($results as $place => $nb) { $perc = Functions::safeDivision($nb, $nb_found - $nb_other); if ($nb != $previous_nb) { $j = I18N::number($i); } else { $j = ' '; } $levels = array_map('trim', explode(',', $place)); $placename = $levels[$analysis_level - 1]; if ($placename == '' && $analysis_level > 1) { $placename = I18N::translate('Unknown (%s)', $levels[$analysis_level - 2]); } $html .= '<div class="maj-row"> <div class="label"><strong>' . $j . '</strong></div> <div class="label">' . $placename . '</div> <div class="value">' . I18N::translate('%d', $nb) . '</div> <div class="value">' . I18N::percentage($perc, 1) . '</div> </div>'; $i++; $previous_nb = $nb; } $html .= '</div>'; return $html; }
/** * Render the Ajax response for the sortable table of Sosa individuals * @param AjaxController $controller */ protected function renderSosaListIndi(AjaxController $controller) { global $WT_TREE; $listSosa = $this->sosa_provider->getSosaListAtGeneration($this->generation); $this->view_bag->set('has_sosa', false); if (count($listSosa) > 0) { $this->view_bag->set('has_sosa', true); $table_id = 'table-sosa-indi-' . Uuid::uuid4(); $this->view_bag->set('table_id', $table_id); $controller->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)->addInlineJavascript(' jQuery.fn.dataTableExt.oSort["unicode-asc" ]=function(a,b) {return a.replace(/<[^<]*>/, "").localeCompare(b.replace(/<[^<]*>/, ""))}; jQuery.fn.dataTableExt.oSort["unicode-desc" ]=function(a,b) {return b.replace(/<[^<]*>/, "").localeCompare(a.replace(/<[^<]*>/, ""))}; jQuery.fn.dataTableExt.oSort["num-html-asc" ]=function(a,b) {a=parseFloat(a.replace(/<[^<]*>/, "")); b=parseFloat(b.replace(/<[^<]*>/, "")); return (a<b) ? -1 : (a>b ? 1 : 0);}; jQuery.fn.dataTableExt.oSort["num-html-desc"]=function(a,b) {a=parseFloat(a.replace(/<[^<]*>/, "")); b=parseFloat(b.replace(/<[^<]*>/, "")); return (a>b) ? -1 : (a<b ? 1 : 0);}; jQuery("#' . $table_id . '").dataTable( { dom: \'<"H"<"filtersH_' . $table_id . '">T<"dt-clear">pf<"dt-clear">irl>t<"F"pl<"dt-clear"><"filtersF_' . $table_id . '">>\', ' . I18N::datatablesI18N() . ', jQueryUI: true, autoWidth: false, processing: true, retrieve: true, columns: [ /* 0-Sosa */ { type: "num", class: "center" }, /* 1-ID */ { visible: false }, /* 2-givn */ { dataSort: 4, class: "left"}, /* 3-surn */ { datasort: 5}, /* 4-GIVN,SURN */ { type: "unicode", visible: false}, /* 5-SURN,GIVN */ { type: "unicode", visible: false}, /* 6-Birth */ { dataSort : 7 , class: "center"}, /* 7-SORT_BIRT */ { visible : false}, /* 8-BIRT_PLAC */ { type: "unicode", class: "center"}, /* PERSO Modify table to include IsSourced module */ /* 9-BIRT_SOUR */ { dataSort : 10, class: "center", visible: ' . (ModuleManager::getInstance()->isOperational(Constants::MODULE_MAJ_ISSOURCED_NAME) ? 'true' : 'false') . ' }, /* 10-SORT_BIRTSC */{ visible : false}, /* 11-Death */ { dataSort : 12 , class: "center"}, /* 12-SORT_DEAT */ { visible : false}, /* 13-Age */ { dataSort : 14 , class: "center"}, /* 14-AGE */ { type: "num", visible: false}, /* 15-DEAT_PLAC */ { type: "unicode", class: "center" }, /* 16-DEAT_SOUR */ { dataSort : 17, class: "center", visible: ' . (ModuleManager::getInstance()->isOperational(Constants::MODULE_MAJ_ISSOURCED_NAME) ? 'true' : 'false') . ' }, /* 17-SORT_DEATSC */{ visible : false}, /* 18-SEX */ { visible : false}, /* 19-BIRT */ { visible : false}, /* 20-DEAT */ { visible : false}, /* 21-TREE */ { visible : false} /* END PERSO */ ], sorting: [[0,"asc"]], displayLength: 16, pagingType: "full_numbers" }); jQuery("#' . $table_id . '") /* Hide/show parents */ .on("click", ".btn-toggle-parents", function() { jQuery(this).toggleClass("ui-state-active"); jQuery(".parents", jQuery(this).closest("table").DataTable().rows().nodes()).slideToggle(); }) /* Hide/show statistics */ .on("click", ".btn-toggle-statistics", function() { jQuery(this).toggleClass("ui-state-active"); jQuery("#indi_list_table-charts_' . $table_id . '").slideToggle(); }) /* Filter buttons in table header */ .on("click", "button[data-filter-column]", function() { var btn = jQuery(this); // De-activate the other buttons in this button group btn.siblings().removeClass("ui-state-active"); // Apply (or clear) this filter var col = jQuery("#' . $table_id . '").DataTable().column(btn.data("filter-column")); if (btn.hasClass("ui-state-active")) { btn.removeClass("ui-state-active"); col.search("").draw(); } else { btn.addClass("ui-state-active"); col.search(btn.data("filter-value")).draw(); } }); jQuery("#sosa-indi-list").css("visibility", "visible"); jQuery("#btn-toggle-statistics-' . $table_id . '").click(); '); $stats = new Stats($WT_TREE); // Bad data can cause "longest life" to be huge, blowing memory limits $max_age = min($WT_TREE->getPreference('MAX_ALIVE_AGE'), $stats->LongestLifeAge()) + 1; // Inititialise chart data $deat_by_age = array(); for ($age = 0; $age <= $max_age; $age++) { $deat_by_age[$age] = ''; } $birt_by_decade = array(); $deat_by_decade = array(); for ($year = 1550; $year < 2030; $year += 10) { $birt_by_decade[$year] = ''; $deat_by_decade[$year] = ''; } $unique_indis = array(); // Don't double-count indis with multiple names. $nb_displayed = 0; Individual::load($WT_TREE, $listSosa); foreach ($listSosa as $sosa => $pid) { $person = Individual::getInstance($pid, $WT_TREE); if (!$person || !$person->canShowName()) { unset($listSosa[$sosa]); continue; } $nb_displayed++; if ($birth_dates = $person->getAllBirthDates()) { if (FunctionsPrint::isDateWithinChartsRange($birth_dates[0]) && !isset($unique_indis[$person->getXref()])) { $birt_by_decade[(int) ($birth_dates[0]->gregorianYear() / 10) * 10] .= $person->getSex(); } } else { $birth_dates[0] = new Date(''); } if ($death_dates = $person->getAllDeathDates()) { if (FunctionsPrint::isDateWithinChartsRange($death_dates[0]) && !isset($unique_indis[$person->getXref()])) { $deat_by_decade[(int) ($death_dates[0]->gregorianYear() / 10) * 10] .= $person->getSex(); } } else { $death_dates[0] = new Date(''); } $age = Date::getAge($birth_dates[0], $death_dates[0], 0); if (!isset($unique_indis[$person->getXref()]) && $age >= 0 && $age <= $max_age) { $deat_by_age[$age] .= $person->getSex(); } $listSosa[$sosa] = $person; $unique_indis[$person->getXref()] = true; } $this->view_bag->set('sosa_list', $listSosa); $this->view_bag->set('sosa_count', count($listSosa)); $this->view_bag->set('sosa_theo', pow(2, $this->generation - 1)); $this->view_bag->set('sosa_ratio', Functions::safeDivision($this->view_bag->get('sosa_count'), $this->view_bag->get('sosa_theo'))); $this->view_bag->set('sosa_hidden', $this->view_bag->get('sosa_count') - $nb_displayed); $this->view_bag->set('chart_births', FunctionsPrintLists::chartByDecade($birt_by_decade, I18N::translate('Decade of birth'))); $this->view_bag->set('chart_deaths', FunctionsPrintLists::chartByDecade($deat_by_decade, I18N::translate('Decade of death'))); $this->view_bag->set('chart_ages', FunctionsPrintLists::chartByAge($deat_by_age, I18N::translate('Age related to death year'))); } ViewFactory::make('SosaListIndi', $this, $controller, $this->view_bag)->render(); }
/** * Returns HTML code for a graph showing the dispersion of ancestors across grand-parents * @return string HTML code */ private function htmlAncestorDispersionG3() { $ancestorsDispGen2 = $this->sosa_provider->getAncestorDispersionForGen(3); $size = '700x300'; $color_motmot = 'ffd1dc'; $color_motfat = 'b998a0'; $color_fatfat = '577292'; $color_fatmot = '84beff'; $color_shared = '777777'; $total_fatfat = array_key_exists(1, $ancestorsDispGen2) ? $ancestorsDispGen2[1] : 0; $total_fatmot = array_key_exists(2, $ancestorsDispGen2) ? $ancestorsDispGen2[2] : 0; $total_motfat = array_key_exists(4, $ancestorsDispGen2) ? $ancestorsDispGen2[4] : 0; $total_motmot = array_key_exists(8, $ancestorsDispGen2) ? $ancestorsDispGen2[8] : 0; $total_sha = array_key_exists(-1, $ancestorsDispGen2) ? $ancestorsDispGen2[-1] : 0; $total = $total_fatfat + $total_fatmot + $total_motfat + $total_motmot + $total_sha; $chd = $this->arrayToExtendedEncoding(array(4095 * Functions::safeDivision($total_fatfat, $total), 4095 * Functions::safeDivision($total_fatmot, $total), 4095 * Functions::safeDivision($total_sha, $total), 4095 * Functions::safeDivision($total_motfat, $total), 4095 * Functions::safeDivision($total_motmot, $total))); $chart_title = I18N::translate('Known Sosa ancestors\' dispersion - G3'); $chl = \Fisharebest\Webtrees\Functions\Functions::getRelationshipNameFromPath('fatfat') . ' - ' . I18N::percentage(Functions::safeDivision($total_fatfat, $total), 1) . '|' . \Fisharebest\Webtrees\Functions\Functions::getRelationshipNameFromPath('fatmot') . ' - ' . I18N::percentage(Functions::safeDivision($total_fatmot, $total), 1) . '|' . I18N::translate('Shared') . ' - ' . I18N::percentage(Functions::safeDivision($total_sha, $total), 1) . '|' . \Fisharebest\Webtrees\Functions\Functions::getRelationshipNameFromPath('motfat') . ' - ' . I18N::percentage(Functions::safeDivision($total_motfat, $total), 1) . '|' . \Fisharebest\Webtrees\Functions\Functions::getRelationshipNameFromPath('motmot') . ' - ' . I18N::percentage(Functions::safeDivision($total_motmot, $total), 1); return "<img src=\"https://chart.googleapis.com/chart?cht=p&chp=1.5708&chd=e:{$chd}&chs={$size}&chco={$color_fatfat},{$color_fatmot},{$color_shared},{$color_motfat},{$color_motmot}&chf=bg,s,ffffff00&chl={$chl}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />"; }
/** * Returns HTML code for the GeoAnalysis general tab (can be either a map or a table). * * @param GeoAnalysis $ga Reference GeoAnalysis * @param array $placesGeneralResults Analysis results at a general level * @param (null|array) $flags Array of flags * @return string HTML code for the general tab */ protected function htmlPlacesAnalysisGeneralTab(GeoAnalysis $ga, $placesGeneralResults, $flags = null) { global $WT_TREE; if (!empty($placesGeneralResults)) { $data = new ViewBag(); $nb_found = $placesGeneralResults['knownsum']; $nb_other = 0; if (isset($placesGeneralResults['other'])) { $nb_other = $placesGeneralResults['other']; } $nb_unknown = $placesGeneralResults['unknown']; $data->set('stats_gen_nb_found', $nb_found); $data->set('stats_gen_nb_other', $nb_other); $data->set('stats_gen_nb_unknown', $nb_unknown); $data->set('use_flags', $ga->getOptions() && $ga->getOptions()->isUsingFlags()); if ($ga->hasMap()) { $max = $placesGeneralResults['max']; $map = $ga->getOptions()->getMap(); $results_by_subdivs = $map->getSubdivisions(); $places_mappings = $map->getPlacesMappings(); foreach ($placesGeneralResults['places'] as $location => $count) { $levelvalues = array_reverse(array_map('trim', explode(',', $location))); $level_map = $ga->getAnalysisLevel() - $ga->getOptions()->getMapLevel(); if ($level_map >= 0 && $level_map < count($levelvalues)) { $levelref = $levelvalues[0] . '@' . $levelvalues[$level_map]; if (!isset($results_by_subdivs[$levelref])) { $levelref = $levelvalues[0]; } } else { $levelref = $levelvalues[0]; } if (isset($places_mappings[$levelref])) { $levelref = $places_mappings[$levelref]; } if (isset($results_by_subdivs[$levelref])) { $count_subd = isset($results_by_subdivs[$levelref]['count']) ? $results_by_subdivs[$levelref]['count'] : 0; $count_subd += $count; $results_by_subdivs[$levelref]['count'] = $count_subd; $results_by_subdivs[$levelref]['transparency'] = Functions::safeDivision($count_subd, $max); if ($ga->getOptions()->isUsingFlags() && $flags) { $results_by_subdivs[$levelref]['place'] = new Place($location, $WT_TREE); $results_by_subdivs[$levelref]['flag'] = $flags[$location]; } } } $data->set('map', $ga->getOptions()->getMap()); $data->set('results_by_subdivisions', $results_by_subdivs); $html = ViewFactory::make('GeoAnalysisTabGeneralMap', $this, new BaseController(), $data)->getHtmlPartial(); } else { $results = $placesGeneralResults['places']; arsort($results); $data->set('results', $results); $data->set('analysis_level', $ga->getAnalysisLevel()); $html = ViewFactory::make('GeoAnalysisTabGeneralTable', $this, new BaseController(), $data)->getHtmlPartial(); } } else { $html = '<p class="warning">' . I18N::translate('No data is available for the general analysis.') . '</p>'; } return $html; }
/** * {@inheritDoc} * @see \MyArtJaub\Webtrees\Module\GeoDispersion\Views\AbstractGeoAnalysisTabGeneralView::htmlAnalysisData() */ protected function htmlAnalysisData() { /** @var OutlineMap $map */ $map = $this->data->get('map'); $canvas = $map->getCanvas(); $subdvisions_results = $this->data->get('results_by_subdivisions'); $nb_found = $this->data->get('stats_gen_nb_found'); $nb_other = $this->data->get('stats_gen_nb_other'); $html = '<script> var tip = null; var tipText = ""; var over = false; var isin = false; function addTip(node, txt){ jQuery(node).bind({ mouseover : function(){ oldisin = isin; isin = true; if(oldisin != isin){ tipText = txt; tip.stop(true, true).fadeIn(); over = true; } }, mouseout : function(){ oldisin = isin; isin = false; if(oldisin != isin){ tip.stop(true, true).fadeOut("fast"); over = false; } } }); } jQuery(document).ready(function() { tip = $("#geodispersion_tip").hide(); var positionTab = jQuery("#geodispersion-tabs").offset(); jQuery("#geodispersion_map").mousemove(function(e){ if (over){ tip.css("left", e.pageX + 20 - positionTab.left).css("top", e.pageY + 20 - positionTab.top); tip.html(tipText); } }); var paper = new Raphael(document.getElementById("geodispersion_map"), ' . $canvas->width . ', ' . $canvas->height . '); var background = paper.rect(0, 0, ' . $canvas->width . ', ' . $canvas->height . '); background.attr({"fill" : "' . $canvas->background_color . '", "stroke" : "' . $canvas->background_stroke . '", "stroke-width": 1, "stroke-linejoin": "round" }); var attr = { fill: "' . $canvas->default_color . '", stroke: "' . $canvas->default_stroke . '", "stroke-width": 1, "stroke-linejoin": "round" }; var map = {}; '; foreach ($subdvisions_results as $name => $location) { $html .= 'map.area' . $location['id'] . ' = paper.path("' . $location['coord'] . '").attr(attr);'; if (isset($location['transparency'])) { $textToolTip = '<strong>' . $location['displayname'] . '</strong><br/>'; if ($this->data->get('use_flags') && $location['flag'] != '') { $textToolTip .= '<span class="geodispersion_flag">' . FunctionsPrint::htmlPlaceIcon($location['place'], $location['flag']) . '</span><br/>'; } $textToolTip .= I18N::translate('%d individuals', $location['count']) . '<br/>' . I18N::percentage(Functions::safeDivision($location['count'], $nb_found - $nb_other), 1); $html .= 'addTip(map.area' . $location['id'] . '.node, "' . Filter::escapeJs($textToolTip) . '");'; $html .= 'map.area' . $location['id'] . '.attr({"fill" : "' . $canvas->max_color . '", "fill-opacity" : ' . $location['transparency'] . ' });'; $html .= 'map.area' . $location['id'] . '.mouseover(function () {' . 'map.area' . $location['id'] . '.stop().animate({"fill" : "' . $canvas->hover_color . '", "fill-opacity" : 1}, 100, "linear");' . '});' . 'map.area' . $location['id'] . '.mouseout(function () {' . 'map.area' . $location['id'] . '.stop().animate({"fill" : "' . $canvas->max_color . '", "fill-opacity" : ' . $location['transparency'] . '}, 100, "linear");' . '});'; } } $html .= '}); </script> <div id="geodispersion_map"></div> <div id="geodispersion_tip"></div>'; return $html; }
/** * Returns the HTML code fo display a row of the Top Places found for a generation. * * @param array $data Data array * @param int $analysis_level Level of subdivision of analysis * @return string HTML code for Top Places row */ protected function htmlGenerationTopPlacesRow($data, $analysis_level) { $tmp_places = array(); $sum_gen = $data['sum']; $other = $data['other']; foreach ($data['places'] as $placename => $count) { if ($placename != 'other') { $levels = array_map('trim', explode(',', $placename)); $placename = '<span title="' . implode(I18N::$list_separator, array_reverse($levels)) . '">' . $levels[$analysis_level - 1] . '</span>'; } else { $placename = I18N::translate('Other places'); } $tmp_places[] = I18N::translate('<strong>%s</strong> [%d - %s]', $placename, $count, I18N::percentage(Functions::safeDivision($count, $sum_gen + $other), 1)); } return implode(I18N::$list_separator, $tmp_places); }