Добавил возможность обновления локального файла .osm.pbf

master
Vitaliy Filippov 2018-12-08 18:57:09 +03:00
parent 2ecaeee50a
commit 5729dc92eb
4 changed files with 105 additions and 51 deletions

View File

@ -1,10 +1,14 @@
FROM debian:sid AS build FROM debian:sid AS build
MAINTAINER Vitaliy Filippov <vitalif@mail.ru> MAINTAINER Vitaliy Filippov <vitalif@mail.ru>
ADD etc /etc # Используем одинаковое начало для ускорения сборки
ADD [ "etc/apt/apt.conf", "/etc/apt/" ]
ADD etc/locale.gen /etc/locale.gen
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y -q \
less wget sudo curl unzip git mc ca-certificates gnupg2 && \
apt-get clean && rm -rf /var/lib/apt/lists/*
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y -q \ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y -q \
less wget sudo curl unzip git mc ca-certificates \
build-essential golang-go libleveldb-dev libgeos-dev && \ build-essential golang-go libleveldb-dev libgeos-dev && \
apt-get clean && rm -rf /var/lib/apt/lists/* apt-get clean && rm -rf /var/lib/apt/lists/*
@ -20,13 +24,17 @@ RUN dpkg -l | grep libgeos | awk '{print $2}' >> /root/pkg.lst
FROM debian:sid AS run FROM debian:sid AS run
MAINTAINER Vitaliy Filippov <vitalif@mail.ru> MAINTAINER Vitaliy Filippov <vitalif@mail.ru>
ADD etc /etc # Используем одинаковое начало для ускорения сборки
ADD [ "etc/apt/apt.conf", "/etc/apt/" ]
ADD etc/locale.gen /etc/locale.gen
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y -q \
less wget sudo curl unzip git mc ca-certificates gnupg2 && \
apt-get clean && rm -rf /var/lib/apt/lists/*
COPY --from=build /root/pkg.lst /root/ COPY --from=build /root/pkg.lst /root/
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y -q \ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y -q \
less wget sudo curl unzip git mc \ osmctools `cat /root/pkg.lst` libleveldb1v5 libdbd-pg-perl && \
`cat /root/pkg.lst` libleveldb1v5 libdbd-pg-perl && \
apt-get clean && rm -rf /var/lib/apt/lists/* apt-get clean && rm -rf /var/lib/apt/lists/*
COPY --from=build /root/go/src/github.com/omniscale/imposm3/imposm /usr/bin/imposm3 COPY --from=build /root/go/src/github.com/omniscale/imposm3/imposm /usr/bin/imposm3

View File

@ -7,7 +7,8 @@ docker build -t imposm3 .
## Running ## Running
``` ```
docker run -it -d --restart always \ docker run -t --rm=true \
-v /home/test/russia-latest.osm.pbf:/home/valhalla/data.osm.pbf \ -e OSM_CACHE_DIR=/home/data \
--name valhalla -p 8002:8002 valhalla -v /home/data/osm:/home/data \
--name imposm3 imposm3
``` ```

2
etc/locale.gen Normal file
View File

@ -0,0 +1,2 @@
en_US.UTF-8 UTF-8
ru_RU.UTF-8 UTF-8

View File

@ -1,6 +1,19 @@
#!/usr/bin/perl #!/usr/bin/perl
# Configuration in env: OSM_CACHE_DIR, OSM_CARTO_VERSION, URL_LATEST, URL_UPDATES, # Конфигурация в переменных окружения:
# plus PG_ENV_OSM_{DB,HOST,PORT,USER,PASSWORD} like in README for the render server itself #
# OSM_CACHE_DIR - путь к директории с временными и загружаемыми файлами
# OSM_INIT - если 1, то при пустой базе инициализировать базу
# OSM_UPDATE_FILE - если 1, то обновлять локальный файл osm.pbf до актуального состояния путём наката дельт
# OSM_EXPIRE - если 1, то вызывать утилиту render_expired
# OSM_METHOD - режим загрузки: osm2pgsql-carto или imposm3-all
# OSM_CARTO_VERSION - для режима osm2pgsql-carto - версия osm-carto. Файлы должны быть в /usr/share/mapnik/openstreetmap-carto-ВЕРСИЯ
# URL_LATEST - URL последней версии .osm.pbf экспортного файла
# URL_UPDATES - URL директории с обновлениями (должна содержать state.txt и дельты)
# PG_ENV_OSM_DB - БД PostGIS
# PG_ENV_OSM_HOST - хост/сокет БД
# PG_ENV_OSM_PORT - порт БД (по умолчанию 5432)
# PG_ENV_OSM_USER - пользователь БД
# PG_ENV_OSM_PASSWORD - пароль пользователя БД
use strict; use strict;
use DBI; use DBI;
@ -14,22 +27,19 @@ my $url_updates = $ENV{URL_UPDATES} || 'http://download.geofabrik.de/russia-upda
if (!$ENV{PG_ENV_OSM_DB}) if (!$ENV{PG_ENV_OSM_DB})
{ {
fatal("OSM database not specified"); fatal("Не задана БД OSM");
} }
info("Начато обновлением OSM базы данных: $ENV{PG_ENV_OSM_DB}");
info("Started OSM update for database: $ENV{PG_ENV_OSM_DB}");
if ($method ne 'osm2pgsql-carto' && $method ne 'imposm3-all') if ($method ne 'osm2pgsql-carto' && $method ne 'imposm3-all')
{ {
fatal("Incorrect OSM update mode: $method"); fatal("Некорректный режим обновления: $method (поддерживаются только osm2pgsql-carto и imposm3-all)");
} }
-e $dir || mkdir($dir); -e $dir || mkdir($dir);
chdir $dir or fatal("Failed to chdir $dir"); chdir $dir or fatal("Директория $dir не существует");
eval { run_update() }; eval { run_update() };
if ($@) if ($@)
{ {
fatal("$@"); fatal("Ошибка кода: $@");
} }
exit(0); exit(0);
@ -47,7 +57,7 @@ sub run_update
{ {
if (!$ENV{OSM_INIT}) if (!$ENV{OSM_INIT})
{ {
fatal("Current OSM version missing, run with OSM_INIT=1 environment variable to initialize"); fatal("БД OSM не инициализирована, запустите скрипт с переменной окружения OSM_INIT=1 для инициализации");
} }
$dbh->rollback; # иначе postgresql пишет "statements until the end ignored" $dbh->rollback; # иначе postgresql пишет "statements until the end ignored"
# создаём таблицу и оставляем её заблокированной # создаём таблицу и оставляем её заблокированной
@ -56,13 +66,13 @@ sub run_update
$dbh->do('CREATE EXTENSION IF NOT EXISTS hstore'); $dbh->do('CREATE EXTENSION IF NOT EXISTS hstore');
# качаем дамп # качаем дамп
my ($fn) = $url_latest =~ /([^\/]+)$/so; my ($fn) = $url_latest =~ /([^\/]+)$/so;
$fn =~ s/^([^\.]+)/$1-$state->{timestamp}/; info("Скачивается файл $url_latest");
info("Downloading $url_latest");
system("curl -s -C - -f '$url_latest' -o $dir/$fn"); system("curl -s -C - -f '$url_latest' -o $dir/$fn");
if ($? || !-e "$dir/$fn") if ($? || !-e "$dir/$fn")
{ {
fatal("Failed to download $url_latest"); fatal("Не удалось скачать файл $url_latest");
} }
system("cp $dir/state.txt $dir/$fn.state.txt");
if ($method eq 'osm2pgsql-carto') if ($method eq 'osm2pgsql-carto')
{ {
init_osm2pgsql($dbh, $state->{timestamp} . ' ' . $state->{sequenceNumber}, "$dir/$fn"); init_osm2pgsql($dbh, $state->{timestamp} . ' ' . $state->{sequenceNumber}, "$dir/$fn");
@ -72,10 +82,12 @@ sub run_update
init_imposm3($dbh, $state->{timestamp} . ' ' . $state->{sequenceNumber}, "$dir/$fn"); init_imposm3($dbh, $state->{timestamp} . ' ' . $state->{sequenceNumber}, "$dir/$fn");
} }
$dbh->commit; $dbh->commit;
info("OSM database initialized with version: ".$state->{timestamp}); info("База данных OSM $ENV{PG_ENV_OSM_DB} инициализирована версией: ".$state->{timestamp});
} }
else else
{ {
$version = [ split /\s+/, $version ];
$version = { timestamp => $version->[0], sequenceNumber => $version->[1] };
my $apply = load_geofabrik_deltas($version, $state, $url_updates, $dir); my $apply = load_geofabrik_deltas($version, $state, $url_updates, $dir);
chdir($dir); chdir($dir);
if ($method eq 'osm2pgsql-carto') if ($method eq 'osm2pgsql-carto')
@ -88,53 +100,63 @@ sub run_update
} }
update_state($dbh, $state); update_state($dbh, $state);
$dbh->commit; $dbh->commit;
info("OSM database updated to version: ".$state->{timestamp}); info("База данных OSM $ENV{PG_ENV_OSM_DB} обновлена до версии: ".$state->{timestamp});
if ($ENV{OSM_EXPIRE}) if ($ENV{OSM_EXPIRE})
{ {
system("cat $dir/expire.list | render_expired --map=osm_carto --touch-from=11"); system("cat $dir/expire.list | render_expired --map=osm_carto --touch-from=11");
system("cat $dir/expire.list | render_expired --map=osm_bright --touch-from=11"); system("cat $dir/expire.list | render_expired --map=osm_bright --touch-from=11");
} }
} }
if ($ENV{OSM_UPDATE_FILE})
{
apply_deltas_file($url_latest, $url_updates, $dir);
}
} }
sub parse_geofabrik_state sub parse_geofabrik_state
{ {
my ($url_updates, $dir) = @_; my ($url_updates, $dir) = @_;
system("curl -s -f '$url_updates/state.txt' -o $dir/state.txt"); system("curl -s -f '$url_updates/state.txt' -o $dir/state.txt");
if (!-r "$dir/state.txt") if (-r "$dir/state.txt")
{ {
fatal("Error downloading $url_updates/state.txt"); return parse_geofabrik_state_file("$dir/state.txt");
} }
else fatal("Не удалось скачать файл $url_updates/state.txt");
}
sub parse_geofabrik_state_file
{
my ($file) = @_;
my $state;
if (open FD, "<$file")
{ {
my $state; local $/ = undef;
if (open FD, "<$dir/state.txt") $state = <FD>;
close FD;
$state = { map { (split /\s*=\s*/, $_, 2) } grep { !/^\s*(#.*)?$/so && /=/so } split /\n/, $state };
if (!$state->{timestamp} || !$state->{sequenceNumber})
{ {
local $/ = undef; fatal("Файл состояния репликации OSM некорректный, должен содержать строки timestamp=<дата_ISO8601> и sequenceNumber=<число>");
$state = <FD>;
close FD;
$state = { map { (split /\s*=\s*/, $_, 2) } grep { !/^\s*(#.*)?$/so && /=/so } split /\n/, $state };
if (!$state->{timestamp} || !$state->{sequenceNumber})
{
fatal("State file incorrect, should have timestamp=<ISO8601 date> and sequenceNumber=<integer>");
}
$state->{timestamp} =~ s/\\//g;
return $state;
} }
$state->{timestamp} =~ s/\\//g;
return $state;
} }
fatal("Error downloading $url_updates/state.txt"); return undef;
} }
sub load_geofabrik_deltas sub load_geofabrik_deltas
{ {
my ($version, $state, $url_updates, $dir) = @_; my ($old_state, $state, $url_updates, $dir) = @_;
my ($timestamp, $i) = split /\s+/, $version; my $i = $old_state->{sequenceNumber};
my $apply = []; my $apply = [];
while ($i <= $state->{sequenceNumber}) while ($i < $state->{sequenceNumber})
{ {
my $subdir = sprintf("%03d/%03d", $i / 1000000, ($i / 1000) % 1000); my $subdir = sprintf("%03d/%03d", $i / 1000000, ($i / 1000) % 1000);
my $fn = sprintf("%03d.osc.gz", $i % 1000); my $fn = sprintf("%03d.osc.gz", $i % 1000);
info("Downloading $url_updates/$subdir/$fn"); if (!-e "$dir/$subdir/$fn")
{
info("Скачивается файл дельты $url_updates/$subdir/$fn");
}
system("mkdir -p $dir/$subdir && curl -C - -s -f '$url_updates/$subdir/$fn' -o $dir/$subdir/$fn"); system("mkdir -p $dir/$subdir && curl -C - -s -f '$url_updates/$subdir/$fn' -o $dir/$subdir/$fn");
if (-e "$dir/$subdir/$fn") if (-e "$dir/$subdir/$fn")
{ {
@ -142,7 +164,8 @@ sub load_geofabrik_deltas
} }
else else
{ {
fatal("Delta not available: $url_updates/$subdir/$fn\n"); unlink("$dir/$subdir/$fn") if -e "$dir/$subdir/$fn";
fatal("Не удалось скачать файл дельты $url_updates/$subdir/$fn\n");
} }
$i++; $i++;
} }
@ -174,7 +197,7 @@ sub init_osm2pgsql
my $carto_dir = '/usr/share/mapnik/openstreetmap-carto-'.$ENV{OSM_CARTO_VERSION}; my $carto_dir = '/usr/share/mapnik/openstreetmap-carto-'.$ENV{OSM_CARTO_VERSION};
if (!$ENV{OSM_CARTO_VERSION}) if (!$ENV{OSM_CARTO_VERSION})
{ {
fatal("osm-carto path not specified"); fatal("Не задан путь к osm-carto");
} }
my $cmd = my $cmd =
"PGPASSWORD='".$ENV{PG_ENV_OSM_PASSWORD}."' osm2pgsql -I -s -c --hstore". "PGPASSWORD='".$ENV{PG_ENV_OSM_PASSWORD}."' osm2pgsql -I -s -c --hstore".
@ -185,7 +208,7 @@ sub init_osm2pgsql
system($cmd); system($cmd);
if ($?) if ($?)
{ {
fatal("$cmd failed"); fatal("Загрузка полного дампа с помощью osm2pgsql не удалась");
} }
local $/ = undef; local $/ = undef;
my $fd; my $fd;
@ -208,7 +231,7 @@ sub init_imposm3
system($cmd); system($cmd);
if ($?) if ($?)
{ {
fatal("$cmd failed"); fatal("Загрузка полного дампа с помощью imposm3 не удалась");
} }
my $indexes = "SET SEARCH_PATH TO import, public; my $indexes = "SET SEARCH_PATH TO import, public;
CREATE INDEX IF NOT EXISTS osm_polygon_area ON osm_polygon (st_area(geometry)); CREATE INDEX IF NOT EXISTS osm_polygon_area ON osm_polygon (st_area(geometry));
@ -240,7 +263,7 @@ sub apply_deltas_osm2pgsql
system($cmd); system($cmd);
if ($?) if ($?)
{ {
fatal("$cmd failed"); fatal("Загрузка дельт osm2pgsql не удалась");
} }
} }
} }
@ -251,7 +274,7 @@ sub apply_deltas_imposm3
my $carto_dir = '/usr/share/mapnik/openstreetmap-carto-'.$ENV{OSM_CARTO_VERSION}; my $carto_dir = '/usr/share/mapnik/openstreetmap-carto-'.$ENV{OSM_CARTO_VERSION};
if (!$ENV{OSM_CARTO_VERSION}) if (!$ENV{OSM_CARTO_VERSION})
{ {
fatal("osm-carto path not specified"); fatal("Не задан путь к osm-carto");
} }
if (@$apply) if (@$apply)
{ {
@ -263,11 +286,31 @@ sub apply_deltas_imposm3
system($cmd); system($cmd);
if ($?) if ($?)
{ {
fatal("$cmd failed"); fatal("Загрузка дельт imposm3 не удалась");
} }
} }
} }
sub apply_deltas_file
{
my ($url_latest, $url_updates, $dir) = @_;
my ($full_fn) = $url_latest =~ /([^\/]+)$/so;
my $old_state = parse_geofabrik_state_file("$dir/$full_fn.state.txt");
my $state = parse_geofabrik_state($url_updates, $dir);
my $apply = load_geofabrik_deltas($old_state, $state, $url_updates, $dir);
if (@$apply)
{
my $cmd = "osmconvert $dir/$full_fn '".join("' '", @$apply)."' -o $dir/new-$full_fn";
system($cmd);
if ($?)
{
fatal("Обновление локального файла .osm.pbf не удалось");
}
system("mv $dir/new-$full_fn $dir/$full_fn");
system("cp $dir/state.txt $full_fn.state.txt");
}
}
sub info sub info
{ {
my ($msg) = @_; my ($msg) = @_;