NicolasLeGland icon

INSEE, 2010-2020, nombre de décès

NicolasLeGland | PRO | 02/11/21 01:30:15 PM UTC (Edited) | 0 ⭐ | 805 👁️ | Never ⏰ | []
PHP |

32.38 KB

|

None

|

0 👍

/

0 👎

<?php
assert_options(ASSERT_BAIL, 1);
date_default_timezone_set('UTC');
 
// https://pastebin.com/s4VZwcYY
 
function sublime_normalize_workspace($file)
{
    assert(true === is_string($file));
    assert(true === is_file($file));
 
    $content = file_get_contents($file);
    assert(true === is_string($content));
 
    $data = json_decode($content);
    unset($data->auto_complete);
    unset($data->expanded_folders);
    unset($data->file_history);
    unset($data->find_state->find_history);
    foreach ($data->groups as &$group)
    {
        foreach ($group->sheets as &$sheet) foreach (array
        (
            'selection',
            'translation.x',
            'translation.y',
        )
        as $property)
        {
            unset($sheet->settings->$property);
        }
    }
 
    file_put_contents($file, str_replace("\n", PHP_EOL, str_replace(array("\r\n", "\r"), "\n", preg_replace(array_keys($map = array
    (
        '/^(\t*)("[^"]+":) ([{[])/m' => '\1\2'."\n".'\1\3',
        '/^(\t*)\[]/m' => '\1['."\n".'\1]',
        '/^(\t*)\{}/m' => '\1{'."\n".'\1}',
    )
    ), array_values($map), preg_replace_callback('/^ +/m', function ($match)
    {
        return str_replace('    ', "\t", $match[0]);
    }
    , json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION))))));
}
$file = substr(__FILE__, 0, -3).'sublime-workspace';
if (true === is_file($file))
{
    sublime_normalize_workspace($file);
}
 
// https://www.insee.fr/fr/information/4190491
foreach (array
(
    array('7ed71ef5f78579e05d77e50bed090cb169852bda', '2010-01-01', '2018-12-31', 'https://www.insee.fr/fr/statistiques/fichier/4769950/deces-2010-2018-csv.zip'),
    array('24a1e0fef31c5ad5c7eed1a00c954259909158d4', '2018-01-01', '2018-12-31', 'https://www.insee.fr/fr/statistiques/fichier/4190491/Deces_2018.zip'),
    array('d32de660b525422907ed938d31a1cd525e69c705', '2019-01-01', '2019-12-31', 'https://www.insee.fr/fr/statistiques/fichier/4190491/Deces_2019.zip'),
    array('b9625e7ea21b7224084d18328a2ed7a22ee3cb17', '2020-01-01', '2020-12-31', 'https://www.insee.fr/fr/statistiques/fichier/4190491/Deces_2020.zip'),
    array('1f599e2852b3def54e80e082b8f4827677172322', '2021-01-01', '2021-01-31', 'https://www.insee.fr/fr/statistiques/fichier/4190491/Deces_2021_M01.zip'),
)
as $file)
{
    list($hash, $begin, $end, $source) = $file;
    $target = substr(__FILE__, 0, -4).'-2-'.$begin.'-'.$end.'.zip';
    if (true !== is_file($target))
    {
        echo 'download archive from ', $begin, ' to ', $end, PHP_EOL;
        $content = file_get_contents($source);
        assert(true === is_string($content));
        assert(sha1($content) === $hash);
        $result = file_put_contents($target, $content);
        assert(strlen($content) === $result);
        assert(true === is_file($target));
        assert(strlen($content) === filesize($target));
    }
}
 
foreach (array
(
    array('e1534a56eab1573b0105d68976f1ad12ccf0fbf3', '2010-01-01', '2010-12-31', '2010-01-01-2018-12-31', 'deces-2010'),
    array('cc2f5ac5032995b174bd4ac4d0c7bdffb1308d2c', '2011-01-01', '2011-12-31', '2010-01-01-2018-12-31', 'deces-2011'),
    array('ecf924ab4be6f27b78f10fa04522f3393ade059f', '2012-01-01', '2012-12-31', '2010-01-01-2018-12-31', 'deces-2012'),
    array('b7cb22efe9783b7d777b8d5fd02545218641c3c9', '2013-01-01', '2013-12-31', '2010-01-01-2018-12-31', 'deces-2013'),
    array('d4c543359d6ff25fb500a09314f7a10c73accff6', '2014-01-01', '2014-12-31', '2010-01-01-2018-12-31', 'deces-2014'),
    array('a3ffdd5510042aa1b3173706a491a66b69679e45', '2015-01-01', '2015-12-31', '2010-01-01-2018-12-31', 'deces-2015'),
    array('9ed276678a94856801685e0cbab20a2d4d15f170', '2016-01-01', '2016-12-31', '2010-01-01-2018-12-31', 'deces-2016'),
    array('717ff6411ad28618fc07f418d7cc166930fefc30', '2017-01-01', '2017-12-31', '2010-01-01-2018-12-31', 'deces-2017'),
    array('e072fbb64b928d01fd355dee5507153c2e0d6700', '2018-01-01', '2018-12-31', '2018-01-01-2018-12-31', 'deces-2018'),
    array('3ba9419aff67752acd7e91fa61a6afb6ad4a6aab', '2019-01-01', '2019-12-31', '2019-01-01-2019-12-31', 'deces-2019'),
    array('0e7c6ff2d4b6a2efef399a9d031fcb23810fa41e', '2020-01-01', '2020-12-31', '2020-01-01-2020-12-31', 'deces_2020'),
    array('3231cf18ec319afb8a13c962ddb0f520ba7a6293', '2021-01-01', '2021-01-31', '2021-01-01-2021-01-31', 'Deces_2021_M01'),
)
as $file)
{
    list($hash, $begin, $end, $archive, $name) = $file;
    $target = substr(__FILE__, 0, -4).'-3-'.$begin.'-'.$end.'.csv';
    if (true !== is_file($target))
    {
        echo 'extract data from ', $begin, ' to ', $end, PHP_EOL;
        $source = substr(__FILE__, 0, -4).'-2-'.$archive.'.zip';
        assert(true === is_file($source));
        $zip = new ZipArchive();
        assert(true === is_object($zip));
        assert('ZipArchive' === get_class($zip));
        assert('fbe34874de4c3b1729f048eb50447b773cddb2d5' === sha1(serialize($zip)));
        $result = $zip->open($source);
        assert(true === $result);
        assert($zip->filename === $source);
        $name .= '.csv';
        $entry = $zip->statName($name);
        assert(true === is_array($entry));
        assert($entry['name'] === $name);
        $content = $zip->getFromName($name);
        assert(strlen($content) === $entry['size']);
        assert(sha1($content) === $hash);
        $result = file_put_contents($target, $content);
        assert(strlen($content) === $result);
        assert(true === is_file($target));
        $result = $zip->close();
        assert(true === $result);
        assert('fbe34874de4c3b1729f048eb50447b773cddb2d5' === sha1(serialize($zip)));
    }
}
 
$target = substr(__FILE__, 0, -4).'-4.txt';
if (true !== is_file($target))
{
    $year_map = array();
    foreach (array
    (
        array('2010-01-01', '2010-12-31'),
        array('2011-01-01', '2011-12-31'),
        array('2012-01-01', '2012-12-31'),
        array('2013-01-01', '2013-12-31'),
        array('2014-01-01', '2014-12-31'),
        array('2015-01-01', '2015-12-31'),
        array('2016-01-01', '2016-12-31'),
        array('2017-01-01', '2017-12-31'),
        array('2018-01-01', '2018-12-31'),
        array('2019-01-01', '2019-12-31'),
        array('2020-01-01', '2020-12-31'),
        array('2021-01-01', '2021-01-31'),
    )
    as $file)
    {
        list($begin, $end) = $file;
        echo 'reading data from ', $begin, ' to ', $end, PHP_EOL;
        $line_list = explode("\r\n", file_get_contents(substr(__FILE__, 0, -4).'-3-'.$begin.'-'.$end.'.csv'));
        $line = array_shift($line_list);
        assert('"nomprenom";"sexe";"datenaiss";"lieunaiss";"commnaiss";"paysnaiss";"datedeces";"lieudeces";"actedeces"' === $line);
        $line = array_pop($line_list);
        assert('' === $line);
        foreach ($line_list as $line)
        {
            assert('"' === $line[0]);
            assert('"' === substr($line, -1));
            $line = explode('";"', substr($line, 1, -1));
            assert(9 === count($line));
            assert(1 === preg_match('/^[0-9]{4}[012][0-9][0123][0-9]$/', $line[2]));
            assert(1 === preg_match('/^[0-9]{4}[012][0-9]/', $line[6]));
            $code = strtolower(substr($line[7], 0, 2));
            assert(1 === preg_match('/^(?:|2[ab]|[0-9]{2})$/', $code));
            if (!(('2a' === $code) || ('2b' === $code) || ('' === $code)))
            {
                $code = (int)$code;
                assert((0 < $code) && ($code < 100));
            }
            ++$year_map[(int)(substr($line[6], 0, 4))][(int)(substr($line[6], 4, 2))][(int)(substr($line[6], 6, 2))][$line[7]];
        }
    }
    ksort($year_map);
    foreach ($year_map as $year => $month_map)
    {
        ksort($year_map[$year]);
        foreach ($month_map as $month => $day_map)
        {
            ksort($year_map[$year][$month]);
            foreach (array_keys($day_map) as $day)
            {
                ksort($year_map[$year][$month][$day]);
            }
        }
    }
    $content = serialize($year_map);
    assert('4a85dc1e2b48b55d1005167a263ba4c5949771f7' === sha1($content));
    $result = file_put_contents($target, $content);
    assert(strlen($content) === $result);
    assert(true === is_file($target));
    assert(strlen($content) === filesize($target));
}
 
function render_table($target, $year_map, $filter = '', $template_map = array())
{
    assert(true === is_string($target));
    assert(true === is_array($year_map));
    assert(true === is_string($filter));
    assert(true === is_array($template_map));
    echo 'rendering 2017-01-01 to 2020-12-31 ', str_replace('-', ' ', substr($target, strlen(__FILE__) - 1, -4)), PHP_EOL;
 
    if (array() !== $template_map)
    {
        $template_total = array();
        $template_subtotal = array();
        foreach ($template_map as $template_month => $template_list)
        {
            if (true === in_array($template_month, array('total', 'subtotal'), true))
            {
                continue;
            }
            foreach ($template_list as $template_year => $template_count)
            {
                $template_total[$template_year] += $template_count;
                if ($template_month <= 9)
                {
                    $template_subtotal[$template_year] += $template_count;                  
                }
            }
        }
        for ($template_year = 2017; $template_year <= 2020; ++$template_year)
        {
            $difference = $template_map['subtotal'][$template_year] - $template_subtotal[$template_year];
            assert((0 <= $difference) && ($difference <= 10));
            if (true === array_key_exists($template_year, $template_map['total']))
            {
                $difference = $template_map['total'][$template_year] - $template_total[$template_year];
                assert((0 <= $difference) && ($difference <= 15));
            }
        }
    }
 
    $target_image = imagecreatefromjpeg(substr(__FILE__, 0, -4).'-1-template.jpg');
    assert(true === is_resource($target_image));
    assert('gd' === get_resource_type($target_image));
    assert(true === imageistruecolor($target_image));
    $render_image = imagecreatetruecolor(imagesx($target_image) << 2, imagesy($target_image) << 2);
    assert(true === is_resource($render_image));
    assert('gd' === get_resource_type($render_image));
    assert(true === imageistruecolor($render_image));
    $result = imagealphablending($render_image, false);
    assert(true === $result);
    $result = imagefilledrectangle($render_image, 0, 0, imagesx($render_image) - 1, imagesy($render_image) - 1, 0x7FFF00FF);
    assert(true === $result);
    $result = imagealphablending($render_image, true);
    assert(true === $result);
    $render_font = str_replace('/', DIRECTORY_SEPARATOR, 'C:/Windows/Fonts/calibrib.ttf');
    $result = imageantialias($render_image, true);
    assert(true === $result);
    $render_size = 48;
    $target_x = 122;
    foreach (array
    (
        2017 => array(87, 0x8899BB),
        2018 => array(88, 0x444477),
        2019 => array(87, 0x222233),
        2020 => array(87, 0x000000),
    )
    as $target_year => $target_list)
    {
        list($target_width, $render_color) = $target_list;
        $render_x = $target_x << 2;
        $render_width = $target_width << 2;
        $target_y = 276;
        foreach (array
        (
            1 => 18,
            2 => 18,
            3 => 19,
            4 => 18,
            5 => 18,
            6 => 19,
            7 => 18,
            8 => 18,
            9 => 18,
            'subtotal' => 28,
            10 => 33,
            11 => 17,
            12 => 17,
            'total' => 17,
        )
        as $target_month => $target_height)
        {
            if ('subtotal' === $target_month)
            {
                $render_begin = $target_year.'-01-01';
                $render_end   = $target_year.'-09-30';
            }
            else if ('total' === $target_month)
            {
                $render_begin = $target_year.'-01-01';
                $render_end   = $target_year.'-12-31';
            }
            else
            {
                $render_begin = $target_year.'-'.substr('0'.$target_month, -2).'-01';
                $render_end   = $target_year.'-'.substr('0'.$target_month, -2).'-31';
            }
            $render_count = 0;
            foreach ($year_map as $source_year => $month_map)
            {
                foreach ($month_map as $source_month => $day_map)
                {
                    foreach ($day_map as $source_day => $area_map)
                    {
                        $source_date = $source_year.'-'.substr('0'.$source_month, -2).'-'.substr('0'.$source_day, -2);
                        if (($render_begin <= $source_date) && ($source_date <= $render_end))
                        {
                            if ('' === $filter)
                            {
                                $render_count += array_sum($area_map);
                            }
                            else
                            {
                                foreach ($area_map as $area_code => $area_count)
                                {
                                    if (0 === preg_match($filter, $area_code))
                                    {
                                        $render_count += $area_count;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            if (0 !== $render_count)
            {
                $text_color = $render_color;
                if (array() !== $template_map)
                {
                    $template_count = 1;
                    if ((true === array_key_exists($target_month, $template_map)) && (true === array_key_exists($target_year, $template_map[$target_month])))
                    {
                        $template_count = max(1, $template_map[$target_month][$target_year]);
                    }
                    $error_ratio = $render_count / $template_count;
                    if (($error_ratio < 0.95) || (1.05 < $error_ratio))
                    {
                        $text_color = 0xFF0000;
                    }
                }
                $render_text = number_format($render_count, 0, '.', ' ');
                $render_y = $target_y << 2;
                $render_height = $target_height << 2;
                $result = imagefilledrectangle($render_image, $render_x, $render_y, $render_x + $render_width - 1, $render_y + $render_height - 1, 0xFFFFFF);
                assert(true === $result);
                $text_box = imagettfbbox($render_size, 0, $render_font, $render_text);
                assert(true === is_array($text_box));
                assert(array(0, 1, 2, 3, 4, 5, 6, 7) === array_keys($text_box));
                assert(array(true) === array_unique(array_map('is_integer', $text_box)));
                $text_width = max($text_box[0], $text_box[2], $text_box[4], $text_box[6]) - min($text_box[0], $text_box[2], $text_box[4], $text_box[6]);
                assert(0 < $text_width);
                $text_height = max($text_box[1], $text_box[3], $text_box[5], $text_box[7]) - min($text_box[1], $text_box[3], $text_box[5], $text_box[7]);
                assert(0 < $text_height);
                $text_x = $render_x + $render_width - $text_width - 24;
                $text_y = $render_y + $render_height - 8;
                if ('subtotal' === $month)
                {
                    $text_y -= 12;
                }
                $result = imagettftext($render_image, $render_size, 0, $text_x, $text_y, $text_color, $render_font, $render_text);
                assert(true === is_array($result));
                assert(array(0, 1, 2, 3, 4, 5, 6, 7) === array_keys($result));
                assert(array(true) === array_unique(array_map('is_integer', $result)));
            }
            $target_y += $target_height + 3;
        }
        $target_x += $target_width + 2;
    }
    $render_x = 32 << 2;
    $render_y = 597 << 2;
    $render_width = 446 << 2;
    $render_height = 24 << 2;
    $result = imagefilledrectangle($render_image, $render_x, $render_y, $render_x + $render_width - 1, $render_y + $render_height - 1, 0xFFFFFF);
    assert(true === $result);
    $result = imagettftext($render_image, 48, 0, $render_x + 8, $render_y + $render_height - 16, 0xBBAA99, $render_font, 'https://twitter.com/nicolaslegland/status/1320752230824366081');
    assert(true === is_array($result));
    assert(array(0, 1, 2, 3, 4, 5, 6, 7) === array_keys($result));
    assert(array(true) === array_unique(array_map('is_integer', $result)));
    $result = imagecopyresampled($target_image, $render_image, 0, 0, 0, 0, imagesx($target_image), imagesy($target_image), imagesx($render_image), imagesy($render_image));
    assert(true === $result);
    assert(true === $result);
    $result = imagedestroy($render_image);
    assert(true === $result);
    $result = imagepng($target_image, $target, 9, PNG_ALL_FILTERS);
    assert(true === $result);
    assert(true === is_file($target));
    $result = imagedestroy($target_image);
    assert(true === $result);
}
 
$year_map = unserialize(file_get_contents($target));
 
$target = substr(__FILE__, 0, -4).'-5-table-metropole.png';
if (true !== is_file($target))
{
    render_table($target, $year_map, '/^9[7-9]/');
    assert('7165871b50ca1650152f6c7f1dab5197835450c4' === sha1_file($target));
}
 
$target = substr(__FILE__, 0, -4).'-5-table-world.png';
if (true !== is_file($target))
{
    render_table($target, $year_map);
    assert('8f77f1820db6e4f2208c1c0243b21fe5a67b105b' === sha1_file($target));
}
 
$target = substr(__FILE__, 0, -4).'-5-table-world-etchebest.png';
if (true !== is_file($target))
{
    render_table($target, $year_map, '', array
    (
             1 => array(2017 => 84917,  2018 => 79753,  2019 => 80155,  2020 => 73404),
             2 => array(2017 => 53169,  2018 => 52819,  2019 => 56462,  2020 => 54805),
             3 => array(2017 => 50817,  2018 => 61072,  2019 => 54331,  2020 => 60501),
             4 => array(2017 => 47497,  2018 => 51008,  2019 => 49719,  2020 => 67213),
             5 => array(2017 => 48910,  2018 => 48427,  2019 => 49672,  2020 => 49121),
             6 => array(2017 => 44898,  2018 => 45568,  2019 => 46992,  2020 => 40069),
             7 => array(2017 => 46818,  2018 => 48862,  2019 => 48670,  2020 => 47407),
             8 => array(2017 => 47027,  2018 => 47761,  2019 => 47566,  2020 => 49171),
             9 => array(2017 => 46504,  2018 => 46192,  2019 => 46796,  2020 => 40056),
    'subtotal' => array(2017 => 470557, 2018 => 481462, 2019 => 480363, 2020 => 481747),
            10 => array(2017 => 49625,  2018 => 50101,  2019 => 50503),
            11 => array(2017 => 49659,  2018 => 49415,  2019 => 51812),
            12 => array(2017 => 43186,  2018 => 39146,  2019 => 42694),
       'total' => array(2017 => 613027, 2018 => 620124, 2019 => 625372),
    ));
    assert('979f97265197ce791e58806ea164d97a0b4dee2d' === sha1_file($target));
}
 
$target = substr(__FILE__, 0, -4).'-5-table-world-december.png';
if (true !== is_file($target))
{
    render_table($target, $year_map, '', array
    (
             1 => array(2017 => 68969,  2018 => 60625,  2019 => 61210,  2020 => 58020),
             2 => array(2017 => 53294,  2018 => 52911,  2019 => 56511,  2020 => 51975),
             3 => array(2017 => 50965,  2018 => 61207,  2019 => 54393,  2020 => 63721),
             4 => array(2017 => 47683,  2018 => 51121,  2019 => 49797,  2020 => 67538),
             5 => array(2017 => 49113,  2018 => 48560,  2019 => 49757,  2020 => 49652),
             6 => array(2017 => 45176,  2018 => 45702,  2019 => 47083,  2020 => 46770),
             7 => array(2017 => 47146,  2018 => 49056,  2019 => 48834,  2020 => 47703),
             8 => array(2017 => 47434,  2018 => 48039,  2019 => 47734,  2020 => 49781),
             9 => array(2017 => 46925,  2018 => 46495,  2019 => 46828,  2020 => 49789),
    'subtotal' => array(2017 => 456715, 2018 => 463726, 2019 => 462153, 2020 => 484955),
            10 => array(2017 => 50255,  2018 => 50673,  2019 => 51061,  2021 => 58460),
            11 => array(2017 => 50813,  2018 => 50438,  2019 => 52605,  2021 => 66169),
            12 => array(2017 => 57793,  2018 => 53745,  2019 => 55660,  2021 => 53974),
       'total' => array(2017 => 615578, 2018 => 618587, 2019 => 621482, 2021 => 663560),
    ));
    assert('bc2738adb15cc7396b48ca8f57e2682d472bab54' === sha1_file($target));
}
 
function render_curve($year_map, $target, $step, $maximum, $smooth = false, $filter = '/^(?:$|[^9]|9[^7-9])/', $description = '')
{
    assert(true === is_array($year_map));
    assert(true === is_string($target));
    assert(true === is_integer($step));
    assert(true === is_integer($maximum));
    assert(true === is_string($filter));
    assert(true === is_string($description));
    echo 'rendering 2010-01-01 to 2020-12-31 curve';
    if ('' !== $description)
    {
        echo ' for ', preg_replace('/&([A-Za-z])(?:acute|circ|grave);/', '\1', $description);
    }
    if (true === $smooth)
    {
        echo ' smooth';
    }
    echo PHP_EOL;
    $line_map = array();
    foreach ($year_map as $year => $month_map)
    {
        if (($year < 2010) || (2020 < $year))
        {
            continue;
        }
        $line_map[$year] = array();
        foreach ($month_map as $month => $day_map)
        {
            if ($month < 1)
            {
                continue;
            }
            foreach ($day_map as $day => $area_map)
            {
                if ($day < 1)
                {
                    continue;
                }
                $count = 0;
                foreach ($area_map as $area_code => $area_count)
                {
                    if (1 === preg_match($filter, $area_code))
                    {
                        $count += $area_count;
                    }
                }
                $line_map[$year][] = $count;
            }
        }
        if (true === in_array($year, array(2012, 2016, 2020), true))
        {
            assert(count($line_map[$year]) === 366);
        }
        else
        {
            assert(count($line_map[$year]) === 365);
        }
    }
    $target_width = 1024;
    $target_height = 512;
    $border_size = 0;
    $margin_size = 8;
    $padding_size = 0;
    $render_width = ($target_width - (2 * ($margin_size + $border_size + $padding_size))) * 16;
    $render_height = ($target_height - (2 * ($margin_size + $border_size + $padding_size))) * 16;
    $render_image = imagecreate($render_width, $render_height);
    assert(true === is_resource($render_image));
    assert('gd' === get_resource_type($render_image));
    assert(false === imageistruecolor($render_image));
    for ($level = 0; 256 !== $level; ++$level)
    {
        $color = imagecolorallocate($render_image, $level, $level, $level);
        assert($level === $color);
    }
    $result = imagefilledrectangle($render_image, 0, 0, $render_width - 1, $render_height - 1, 255);
    assert(true === $result);
    for ($month = 2; $month <= 12; ++$month)
    {
        $x = (int)(round(((strtotime('1970-'.substr('0'.$month, -2).'-01') / 86400) * ($render_width - 1)) / 364));
        $x = ($x >> 4) << 4;
        $result = imagefilledrectangle($render_image, $x - 16, 0, $x + 15, $render_height - 1, 0xEE);
        assert(true === $result);
    }
    $font = str_replace('/', DIRECTORY_SEPARATOR, 'C:/Windows/Fonts/calibrib.ttf');
    $size = 160;
    for ($count = $step; $count <= $maximum; $count += $step)
    {
        $y = (int)(round((($maximum - $count) * ($render_height - 1)) / $maximum));
        if ($count < $maximum)
        {
            $result = imagefilledrectangle($render_image, 0, $y - 16, $render_width - 1, $y + 15, 0xEE);
            assert(true === $result);
        }
        $result = imagettftext($render_image, $size, 0, 0, $y + $size + 32, 0x7F, $font, $count);
        assert(true === is_array($result));
        assert(array(0, 1, 2, 3, 4, 5, 6, 7) === array_keys($result));
        assert(array(true) === array_unique(array_map('is_integer', $result))); 
    }
    $y = imagesy($render_image) - 128;
    foreach (array_map('html_entity_decode', array
    (
        'Janvier', 'F&eacute;vrier', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Ao&ucirc;t', 'Septembre', 'Octobre', 'Novembre', 'D&eacute;cembre',
    )
    ) as $month => $text)
    {
        $x = (int)(round(((strtotime('1970-'.substr('0'.($month + 1), -2).'-01') / 86400) * ($render_width - 1)) / 364)) + 32;
        $result = imagettftext($render_image, $size, 0, $x, $y, 0x7F, $font, $text);
        assert(true === is_array($result));
        assert(array(0, 1, 2, 3, 4, 5, 6, 7) === array_keys($result));
        assert(array(true) === array_unique(array_map('is_integer', $result)));     
    }
    foreach (array
    (
        'https://twitter.com/nicolaslegland/status/1320752230824366081',
        'https://www.insee.fr/fr/information/4190491',
        'https://pastebin.com/s4VZwcYY',
    )
    as $index => $text)
    {
        $text_box = imagettfbbox($size, 0, $font, $text);
        assert(true === is_array($text_box));
        assert(array(0, 1, 2, 3, 4, 5, 6, 7) === array_keys($text_box));
        assert(array(true) === array_unique(array_map('is_integer', $text_box)));
        $text_width = max($text_box[0], $text_box[2], $text_box[4], $text_box[6]) - min($text_box[0], $text_box[2], $text_box[4], $text_box[6]);
        assert(0 < $text_width);
        $result = imagettftext($render_image, $size, 0, $render_width - $text_width - 32, 640 + (192 * $index), 0xBF, $font, $text);
        assert(true === is_array($result));
        assert(array(0, 1, 2, 3, 4, 5, 6, 7) === array_keys($result));
        assert(array(true) === array_unique(array_map('is_integer', $result)));
    }
    $caption_x = 896;
    $caption_y = 192;
    $text = html_entity_decode('D&eacute;c&egrave;s quotidiens');
    $text_box = imagettfbbox($size, 0, $font, $text);
    assert(true === is_array($text_box));
    assert(array(0, 1, 2, 3, 4, 5, 6, 7) === array_keys($text_box));
    assert(array(true) === array_unique(array_map('is_integer', $text_box)));
    $text_width = max($text_box[0], $text_box[2], $text_box[4], $text_box[6]) - min($text_box[0], $text_box[2], $text_box[4], $text_box[6]);
    assert(0 < $text_width);
    $result = imagettftext($render_image, $size, 0, $caption_x, $caption_y, 0x7F, $font, $text);
    assert(true === is_array($result));
    assert(array(0, 1, 2, 3, 4, 5, 6, 7) === array_keys($result));
    assert(array(true) === array_unique(array_map('is_integer', $result)));
    $text = '2010-2019';
    $text_box = imagettfbbox($size, 0, $font, $text);
    assert(true === is_array($text_box));
    assert(array(0, 1, 2, 3, 4, 5, 6, 7) === array_keys($text_box));
    assert(array(true) === array_unique(array_map('is_integer', $text_box)));
    $text_width = max($text_box[0], $text_box[2], $text_box[4], $text_box[6]) - min($text_box[0], $text_box[2], $text_box[4], $text_box[6]);
    assert(0 < $text_width);
    $result = imagettftext($render_image, $size, 0, $caption_x, $caption_y + 192, 0xBF, $font, $text);
    assert(true === is_array($result));
    assert(array(0, 1, 2, 3, 4, 5, 6, 7) === array_keys($result));
    assert(array(true) === array_unique(array_map('is_integer', $result)));
    $caption_width = $text_width;
    for ($year = 2010; 2020 !== $year; ++$year)
    {
        $box_x = $caption_x + $caption_width + 128 + (($year - 2010) * 192);
        $box_color = 240 + (8 * (2010 - $year));
        $result = imagefilledrectangle($render_image, $box_x, $caption_y + 64, $box_x + 127, $caption_y + 191, $box_color);
        assert(true === $result);
    }
    $text = '2020';
    $text_box = imagettfbbox($size, 0, $font, $text);
    assert(true === is_array($text_box));
    assert(array(0, 1, 2, 3, 4, 5, 6, 7) === array_keys($text_box));
    assert(array(true) === array_unique(array_map('is_integer', $text_box)));
    $text_width = max($text_box[0], $text_box[2], $text_box[4], $text_box[6]) - min($text_box[0], $text_box[2], $text_box[4], $text_box[6]);
    assert(0 < $text_width);
    $result = imagettftext($render_image, $size, 0, $caption_x + $caption_width - $text_width, $caption_y + 384, 0xBF, $font, $text);
    assert(true === is_array($result));
    assert(array(0, 1, 2, 3, 4, 5, 6, 7) === array_keys($result));
    assert(array(true) === array_unique(array_map('is_integer', $result)));
    $box_x = $caption_x + $caption_width + 128;
    $result = imagefilledrectangle($render_image, $box_x, $caption_y + 256, $box_x + 127, $caption_y + 383, 0x00);
    assert(true === $result);
    $size = 320;
    if ('' === $description)
    {
        $text = 'France';
    }
    else
    {
        $text = html_entity_decode($description);
    }
    $text_box = imagettfbbox($size, 0, $font, $text);
    assert(true === is_array($text_box));
    assert(array(0, 1, 2, 3, 4, 5, 6, 7) === array_keys($text_box));
    assert(array(true) === array_unique(array_map('is_integer', $text_box)));
    $text_width = max($text_box[0], $text_box[2], $text_box[4], $text_box[6]) - min($text_box[0], $text_box[2], $text_box[4], $text_box[6]);
    assert(0 < $text_width);
    $result = imagettftext($render_image, $size, 0, $render_width - $text_width - 64, 384, 0x7F, $font, $text);
    assert(true === is_array($result));
    assert(array(0, 1, 2, 3, 4, 5, 6, 7) === array_keys($result));
    assert(array(true) === array_unique(array_map('is_integer', $result)));
    $result = imagesetthickness($render_image, 24);
    assert(true === $result);
    foreach ($line_map as $year => $point_map)
    {
        if (2020 === $year)
        {
            $color = 0;
        }
        else
        {
            $color = 240 + (8 * (2010 - $year));
        }
        $length = count($point_map);
        $previous_x = null;
        $previous_y = null;
        foreach ($point_map as $index => $count)
        {
            if (true === $smooth)
            {
                $smooth_begin = $index - 3;
                $smooth_end = $index + 4;
                $smooth_total = 0;
                for ($smooth_index = $smooth_begin; $smooth_end !== $smooth_index; ++$smooth_index)
                {
                    if ($smooth_index < 0)
                    {
                        $smooth_year = $year - 1;
                        $smooth_day = count($line_map[$smooth_year]) + $smooth_index;
                    }
                    else if ($length <= $smooth_index)
                    {
                        $smooth_year = $year + 1;
                        $smooth_day = $smooth_index - $length;
                    }
                    else
                    {
                        $smooth_year = $year;
                        $smooth_day = $smooth_index;
                    }
                    if ((true === array_key_exists($smooth_year, $line_map)) && (true === array_key_exists($smooth_day, $line_map[$smooth_year])))
                    {
                        $smooth_total += $line_map[$smooth_year][$smooth_day];
                    }
                    else
                    {
                        assert(true === in_array($smooth_year, array(2009, 2021), true));
                        $smooth_total += $count;
                    }
                }
                $count = $smooth_total / 7;
            }
 
            $x = (int)(round(($index * ($render_width - 1)) / ($length - 1)));
            $y = (int)(round((($maximum - $count) * ($render_height - 1)) / $maximum));
            assert(0 <= $x);
            assert($x < $render_width);
            assert(0 <= $y);
            assert($y < $render_height);
            if (null === $previous_x)
            {
                assert(null === $previous_y);
                $previous_x = $x;
                $previous_y = $y;
            }
            else
            {
                assert(true === is_integer($previous_x));
                assert(true === is_integer($previous_y));
                imageline($render_image, $previous_x, $previous_y, $x, $y, $color);
                $previous_x = $x;
                $previous_y = $y;
            }
        }
    }
    $target_image = imagecreatetruecolor($target_width, $target_height);
    assert(true === is_resource($target_image));
    assert('gd' === get_resource_type($target_image));
    assert(true === imageistruecolor($target_image));
    $result = imagefilledrectangle($target_image, 0, 0, $target_width - 1, $target_height - 1, 0xFFFFFF);
    assert(true === $result);
    $result = imagefilledrectangle($target_image, $margin_size, $margin_size, $target_width - $margin_size - 1, $target_height - $margin_size - 1, 0xEEEEEE);
    assert(true === $result);
    $result = imagefilledrectangle($target_image, $margin_size + $border_size, $margin_size + $border_size, $target_width - $margin_size - $border_size - 1, $target_height - $margin_size - $border_size - 1, 0xFFFFFF);
    assert(true === $result);
    $result = imagecopyresampled
    (
        $target_image, $render_image,
        $margin_size + $border_size + $padding_size, $margin_size + $border_size + $padding_size,
        0, 0,
        $target_width - (2 * ($margin_size + $border_size + $padding_size)), $target_height - (2 * ($margin_size + $border_size + $padding_size)),
        $render_width, $render_height
    );
    assert(true === $result);
    $result = imagedestroy($render_image);
    assert(true === $result);
    $result = imagepng($target_image, $target, 9, PNG_ALL_FILTERS);
    assert(true === $result);
    assert(true === is_file($target));
    $result = imagedestroy($target_image);
    assert(true === $result);
}
 
$target = substr(__FILE__, 0, -4).'-6-curve.png';
if (true !== is_file($target))
{
    render_curve($year_map, $target, 1000, 4000);
    assert('ecfffb289bc3566293253794e1ddd50a1b479728' === sha1_file($target));
}
 
$target = substr(__FILE__, 0, -4).'-6-curve-smooth.png';
if (true !== is_file($target))
{
    render_curve($year_map, $target, 1000, 4000, true);
    assert('880231127a76b14831d2a923f5d8defd4bf40dee' === sha1_file($target));
}
 
foreach (array
(
    array(7, 'cbc2436af126ec845d3a73c500480e7372eee7cb', '1566de136bbdf7c6b9d5738bf3a1f9f30639a88d', 50, 250, '/^(?:22|29|35|56)/', 'Bretagne'),
    array(7, 'aacd1fd074c2abbfeaa5abc4510d7a03e353048b', '3fc310ae037db308a0dfe8bcdd206467303032c2', 10, 40, '/^(?:2[AB])/', 'Corse'),
    array(7, '4f64f3f505165873a957ba56f15788628c3942d8', '9e17b840c0dfb07075c1ffa845894ab165bc0e68', 250, 1000, '/^(?:75|77|78|91|92|93|94|95)/', '&Icirc;le-de-France'),
    array(7, '100c84c8fc1f738899bc089b52982926d9b36ad9', '12146da9295b5ca72401d4c79954fbbd5ba623de', 50, 200, '/^(?:14|27|50|61|76)/', 'Normandie'),
    array(7, '7b02cdb74cd67dfaf9e061aad2844659ae6a8d3f', 'ca8ec1efaccb4ace8ba276f9082d088e3dd6e4cc', 100, 400, '/^(?:04|05|06|13|83|84)/', 'Provence-Alpes-C&ocirc;te d\'Azur'),
 
    array(8, '0fa8c7a90ec44dfb8c3408a76a4a1978ae495f6f', 'a208fb3eb39ddb1a0c042a08ad60ca022d858799', 50, 200, '/^13/', 'Bouches-du-Rh&ocirc;ne'),
    array(8, '4acf0cb6dde4a30e63ac9efbcb4c74ef0b309ac6', '1ccc54797ab6539788743e52533c08e2a55f1581', 25, 100, '/^35/', 'Ille-et-Vilaine'),
    array(8, '6d46d2e8da152dd7f934ffb9f1f359a44e5f4957', '43d8ec96f53446191c67c0b218c212404b9a3b62', 25, 100, '/^57/', 'Moselle'),
    array(8, '4e685ef8eeb0d97f3da4c158bdc76fa0d785f0e7', '98b6dfe0c855208b20dcc0126af313201335eabd', 25, 100, '/^68/', 'Haut-Rhin'),
 
    array(9, 'a0a5147ad958f1d76ab9820d50ae42dc5a683d17', '78b67c3d0774875cba36d0c7907d054c1809bf09', 10,  50, '/^69(?:00[1-9]|123|38[1-9])$/', 'Lyon'),
    array(9, '380e78d3e9b0f2a4b67360537d3e0c5a79ad53ba', '2e22d83bf4ee078ab94021ddf526f198cc77aaee', 10,  40, '/^130(?:0[1-9]|1[0-6])$/', 'Marseille'),
    array(9, '75d405c7ffde8725129bb1abda1de4bdba740771', 'f651bc1f2328e6877f85b0305a2f4773e24547ad', 50, 200, '/^75/', 'Paris'),
    array(9, 'f1d7eb39494aaa421cfc507c26720a065ca7bb6d', '8d8a06262fa2d47c3503a053b99bbfb7144e5042', 10,  30, '/^35(?:[027]00|238)$/', 'Rennes'),
)
as $list)
{
    list($group, $hash_sharp, $hash_smooth, $step, $maximum, $filter, $description) = $list;
    $name = strtolower(preg_replace('/&([A-Za-z])(?:acute|circ);/', '\1', $description));
    $target = substr(__FILE__, 0, -4).'-'.$group.'-curve-'.$name.'.png';
    if (true !== is_file($target))
    {
        render_curve($year_map, $target, $step, $maximum, false, $filter, $description);
        assert($hash_sharp === sha1_file($target));
    }
    $target = substr(__FILE__, 0, -4).'-'.$group.'-curve-'.$name.'-smooth.png';
    if (true !== is_file($target))
    {
        render_curve($year_map, $target, $step, $maximum, true, $filter, $description);
        assert($hash_smooth === sha1_file($target));
    }
}
 
 

Comments