📜 ⬆️ ⬇️

Russification and multilingual OpenStreetMap cards

While developing web maps that use OpenStreetMap data, there is often a question about how to display maps with correct Russian names. This problem does not arise if your maps show only Russia . However, if you look, for example, at a map of China , then you are unlikely to enjoy such an abundance of hieroglyphs, and vain attempts to find Beijing on such a map are not likely to be crowned with success.



It is known that the freedom-loving project OpenStreetMap allows you to save the names of geographical objects in different languages. For this purpose, special tags are used, such as name: ru , name: en or name: es , and most importantly, they are filled in by OpenStreetMap participants. Of course, the most detailed inscriptions are created by users in the language they speak: in Russia - in Russian, in China - in Chinese, in African countries - in local languages. There is little chance that some street in Nigeria will have a Russian translation, but the main geographical objects (countries, cities, rivers, etc.) have translations. This small cartographic information is quite enough for a Russian-speaking user to open, for example, a map of China and find the main names on it. Thus, your resource will become a little more user friendly.

If you aim to find sensible instructions on how to use these translation tags on your own web maps, then in the whole space of the Runet or even the Internet there is only scattered information about various aspects of the localization of OpenStreetMap web maps, but you can hardly find the boxed instructions. On the one hand, this problem is not the most difficult architectural issue, since The technology was developed within the framework of the OpenStreetMap project itself. But on the other hand, in the completed projects of localized web maps, for example, in the well-known Rostelecom Satellite , the localization problem was solved, and the problems faced by the developers and how they were resolved remain unresponsive to the IT public.
')


There are even web- projects with multilingual support, but they are not brought to a logical, user-friendly end.



This article aims to bring together this scattered information in the form of complete instructions on the localization of OpenStreetMap maps. The article provides minimal information about the deployment of tile north based on OpenStreetMap data, since this topic was disclosed in detail by other publications on Habré , on Gis-Lab and in other special projects . Within the framework of this article we will deploy the storage of tiles (tiles-pictures, of which the image of a web map is “stitched”) according to the well-known instructions , but without going into details or specifying details. Emerging questions about the deployment of tile storage, look primarily in the instructions mentioned.

So, we will need packages (with all the necessary dependencies):

A full tile server (with Apache httpd, mod_tile and renderd) is not required to be deployed for the purpose of this article. But the guiding vectors for the deployment of the tile server will be given below, you will find detailed instructions on the above links.

1. Get vector data OpenStreetMap


We will import this data (using the osm2pgsql utility) into the Postgresql database, and then the Mapnik rendering package will generate raster tiles.

So, download the map in a given region in XML format (OSM file) or in compressed binary PBF format from one of the OpenStreetMap mirrors.
The planet file can be downloaded with the commands:

wget http://download.bbbike.org/osm/planet/planet-latest.osm.pbf.md5 wget http://download.bbbike.org/osm/planet/planet-latest.osm.pbf md5sum planet-latest.osm.pbf 

You should keep in mind that this data takes up a significant amount. For example, a file containing vector data for the entire planet Planet.osm in uncompressed OSM XML takes more than 1TB, in XML format, compressed bz2, takes 41.8GB, and in binary PBF format 18.1GB. All of these formats store the same data. Naturally, if you need only a specific region, and not the entire planet, then these numbers will be much smaller, but the proportions of the data volumes in the formats, OSM XML, OSM XML, compressed bz2, and in the PBF format, will remain the same. You should keep in mind that before importing into Postgresql, vector data in OSM XML format, compressed BZ2, and binary PBF format will be pre-processed (unpacked, parsed), which will increase the time to import the downloaded file into the Postgresql database. So, on my Core i5 quad-core machine with 16GB of RAM, 2TB HDD (Ubuntu 14.04 64x), the import of Planet.osm.pbf took 2 weeks. Importing a similar Planet.osm.bz2 file took an order of magnitude less time. According to my own feelings, the osm2pgsql utility is demanding on the amount of RAM (for optimal memory management, read about the features of the --cache and --cache-strategy utilities of the osm2pgsql utility), the frequency and number of processor cores (at the time of parsing the file, to control the processor core load, see the --number-processes utility utility of osm2pgsql), as well as the speed of the hard disk (at the time of inserting data and creating indexes in Postgresql, for optimization, see the --disable-parallel-indexing utility utility of osm2pgsql). If you have an SSD, then importing data into the Postgresql database will be much faster.

For the purposes of this article, we will use the region of China, since it is not Russified by default and has a relatively small size, which is convenient at the stage of debugging technology. The China vector data files can be downloaded from the geofabrik.de project site with the command:

 wget http://download.geofabrik.de/asia/china-latest.osm.pbf 

2. Update the resulting OpenStreetMap vector data.


The fact is that in the time that has passed since the data file was packaged until it was deployed on your machine, some changes could be made on OpenStreetMap. Therefore, before importing, we update the data file with the osmupdate command from the osmcutils package:

 osmupdate china-latest.osm.pbf new-china-latest.osm.pbf 

If your OSM file does not contain a timestamp, then most likely the osmupdate utility will return an error. In this case (if you know the timestamp of your file - it is sometimes published on the file download page) you need to run the command in the following format:

 osmupdate china-latest.osm.pbf 2015-05-13T14:48:07Z new-china-latest.osm.pbf 

The osmupdate utility itself downloads the diff file and applies it to the file specified in the first argument of the call command. It is not recommended to run the osmupdate utility with large files in OSM XML format (for example, planet-latest.osm.bz2), since Preliminary processing of the file by the osmconvert utility from the same osmcutils package is necessary, and the osmupdate utility with the received converted file will work for several days. For large files, it is recommended to use the PBF format. But keep in mind that importing such a file into Postgresql will take longer than importing an OSM XML file. So say, a double-edged sword. Personally, I always choose the PBF format.

3. Configure the support of the necessary languages ​​in the settings of the utility osm2pgsql


By default, any languages ​​are not installed in the /usr/share/osm2pgsql/osm2pgsql/default.style style file. For inscriptions on the map, the name tag is used, which is specified in the /usr/share/osm2pgsql/osm2pgsql/default.style file with the line:

 node,way name text linear 

After this we add support for, for example, Russian, English and Spanish languages:

 node,way name:ru text linear node,way name:en text linear node,way name:es text linear 

These three lines will tell the osm2pgsql utility to import from the PBF file also the values ​​of the tags name: ru , name: en , name: se . Other localized names will be ignored.

4. Create a Postgresql database to store OpenStreetMap vector data


You must first configure trust-authentication (in order not to enter a password, this article does not require this) and create the necessary database users (see the instruction ). In this article, we will limit ourselves to trust authentication and the standard postgres user. So, create a database, for example, china and connect the necessary extensions to it:

 createdb -U postgres china psql -U postgres -d china -c 'CREATE EXTENSION hstore; CREATE EXTENSION postgis;' 

5. Import the vector data into the Postgresql database.


Import is carried out by the utility osm2pgsql. Utility key descriptions can be found in the utility help. Some explanatory information is provided on an English-language resource .

 osm2pgsql -s -m -d china -U postgres --drop new-china-latest.osm.pbf 

The --drop key allows you to reduce the disk space occupied by the database, thus sacrificing the ability to subsequently update the data in the database from fresh PBF or OSM XML files. In the case of China, the database size has decreased from about 500MB to 92MB. To see the size of the database, enter the command in the psql console connected to any existing database:

 SELECT pg_size_pretty( pg_database_size( 'china' ) ); 

6. Create views in the database to display the translated geographical names.


Using the instruction, create several SQL views. For the generation of “Russian” tiles (tiles containing Russian signatures) we will use the prefix china_ru, for the generation of “English” tiles - china_en, for the generation of “Spanish” tiles - china_es. Below is the SQL script for creating SQL representations for generating only “Russian” tiles. Dear reader, I think, he will deal with SQL representations for generating "English" and "Spanish" tiles, taking the "Russian» VIEW as a basis.

 CREATE VIEW china_ru_point AS SELECT data.osm_id, data.access, data."addr:housename", data."addr:housenumber", data."addr:interpolation", data.admin_level, data.aerialway, data.aeroway, data.amenity, data.area, data.barrier, data.bicycle, data.brand, data.bridge, data.boundary, data.building, data.capital, data.construction, data.covered, data.culvert, data.cutting, data.denomination, data.disused, data.ele, data.embankment, data.foot, data."generator:source", data.harbour, data.highway, data.historic, data.horse, data.intermittent, data.junction, data.landuse, data.layer, data.leisure, data.lock, data.man_made, data.military, data.motorcar, CASE data."name:ru" IS NULL WHEN true THEN '' ELSE data."name:ru" || CASE data.name IS NULL WHEN true THEN '' ELSE E'\n' END END || CASE data.name IS NULL WHEN true THEN '' ELSE data.name END AS name, data."natural", data.office, data.oneway, data.operator, data.place, data.poi, data.population, data.power, data.power_source, data.public_transport, data.railway, data.ref, data.religion, data.route, data.service, data.shop, data.sport, data.surface, data.toll, data.tourism, data."tower:type", data.tunnel, data.water, data.waterway, data.wetland, data.width, data.wood, data.z_order, data.way FROM planet_osm_point AS data; CREATE VIEW china_ru_line AS SELECT data.osm_id, data.access, data."addr:housename", data."addr:housenumber", data."addr:interpolation", data.admin_level, data.aerialway, data.aeroway, data.amenity, data.area, data.barrier, data.bicycle, data.brand, data.bridge, data.boundary, data.building, data.construction, data.covered, data.culvert, data.cutting, data.denomination, data.disused, data.embankment, data.foot, data."generator:source", data.harbour, data.highway, data.historic, data.horse, data.intermittent, data.junction, data.landuse, data.layer, data.leisure, data.lock, data.man_made, data.military, data.motorcar, CASE data."name:ru" IS NULL WHEN true THEN '' ELSE data."name:ru" || CASE data.name IS NULL WHEN true THEN '' ELSE E'\n' END END || CASE data.name IS NULL WHEN true THEN '' ELSE data.name END AS name, data."natural", data.office, data.oneway, data.operator, data.place, data.population, data.power, data.power_source, data.public_transport, data.railway, data.ref, data.religion, data.route, data.service, data.shop, data.sport, data.surface, data.toll, data.tourism, data."tower:type", data.tracktype, data.tunnel, data.water, data.waterway, data.wetland, data.width, data.wood, data.z_order, data.way FROM planet_osm_line AS data; CREATE VIEW china_ru_polygon AS SELECT data.osm_id, data.access, data."addr:housename", data."addr:housenumber", data."addr:interpolation", data.admin_level, data.aerialway, data.aeroway, data.amenity, data.area, data.barrier, data.bicycle, data.brand, data.bridge, data.boundary, data.building, data.construction, data.covered, data.culvert, data.cutting, data.denomination, data.disused, data.embankment, data.foot, data."generator:source", data.harbour, data.highway, data.historic, data.horse, data.intermittent, data.junction, data.landuse, data.layer, data.leisure, data.lock, data.man_made, data.military, data.motorcar, CASE data."name:ru" IS NULL WHEN true THEN '' ELSE data."name:ru" || CASE data.name IS NULL WHEN true THEN '' ELSE E'\n' END END || CASE data.name IS NULL WHEN true THEN '' ELSE data.name END AS name, data."natural", data.office, data.oneway, data.operator, data.place, data.population, data.power, data.power_source, data.public_transport, data.railway, data.ref, data.religion, data.route, data.service, data.shop, data.sport, data.surface, data.toll, data.tourism, data."tower:type", data.tunnel, data.water, data.waterway, data.wetland, data.width, data.wood, data.z_order, data.way, data.way_area FROM planet_osm_polygon AS data; CREATE VIEW china_ru_roads AS SELECT data.osm_id, data.access, data."addr:housename", data."addr:housenumber", data."addr:interpolation", data.admin_level, data.aerialway, data.aeroway, data.amenity, data.area, data.barrier, data.bicycle, data.brand, data.bridge, data.boundary, data.building, data.construction, data.covered, data.culvert, data.cutting, data.denomination, data.disused, data.embankment, data.foot, data."generator:source", data.harbour, data.highway, data.historic, data.horse, data.intermittent, data.junction, data.landuse, data.layer, data.leisure, data.lock, data.man_made, data.military, data.motorcar, CASE data."name:ru" IS NULL WHEN true THEN '' ELSE data."name:ru" || CASE data.name IS NULL WHEN true THEN '' ELSE E'\n' END END || CASE data.name IS NULL WHEN true THEN '' ELSE data.name END AS name, data."natural", data.office, data.oneway, data.operator, data.place, data.population, data.power, data.power_source, data.public_transport, data.railway, data.ref, data.religion, data.route, data.service, data.shop, data.sport, data.surface, data.toll, data.tourism, data."tower:type", data.tunnel, data.water, data.waterway, data.wetland, data.width, data.wood, data.z_order, data.way FROM planet_osm_roads AS data; INSERT INTO geometry_columns VALUES ('', 'public', 'china_ru_point', 'way', 2, 900913, 'POINT'); INSERT INTO geometry_columns VALUES ('', 'public', 'china_ru_line', 'way', 2, 900913, 'LINESTRING'); INSERT INTO geometry_columns VALUES ('', 'public', 'china_ru_polygon', 'way', 2, 900913, 'POLYGON'); INSERT INTO geometry_columns VALUES ('', 'public', 'china_ru_roads', 'way', 2, 900913, 'LINESTRING'); 

In this case, we will create signatures on the map in the format “name: ru \ nname”, where E '\ n' is, as is known, the carriage return to a new line. For a more sophisticated placement of signatures on the map, read the Mapnik TextSymbolizer documentation. In this case, you will need to change the display styles of the signatures on the map, generated in accordance with these instructions in the next paragraph.

7. Install scripts from OpenStreetMap to generate tiles


Let us choose the directory / home / osm / mapnik for storing all the necessary Mapnik scripts. We will download OpenStreetMap scripts into it to generate tiles, extort world shapes (shape files) used to simplify the generation of tiles on small scales, and create Mapnik XML style files to attach to the database:

 svn co http://svn.openstreetmap.org/applications/rendering/mapnik /home/osm/mapnik /home/osm/mapnik/get-coastlines.sh 

Next, we will need to create 3 tile rendering style files, respectively, for Russian, English and Spanish geographical object signatures. To do this, we set a special variable of the MAPNIK_PREFIX system environment, which allows you to set the prefix of the vector data tables in the Postgres database and which the script /home/osm/mapnik/generate_xml.py will read and substitute into pseudo queries to the database.

 export MAPNIK_PREFIX='china_ru' /home/osm/mapnik/generate_xml.py /home/osm/mapnik/osm.xml china_ru.xml --dbname china --user postgres --accept-none export MAPNIK_PREFIX='china_en' /home/osm/mapnik/generate_xml.py /home/osm/mapnik/osm.xml china_en.xml --dbname china --user postgres --accept-none export MAPNIK_PREFIX='china_es' /home/osm/mapnik/generate_xml.py /home/osm/mapnik/osm.xml china_es.xml --dbname china --user postgres --accept-none 

These teams will create 3 special styles for rendering tiles, allowing you to read the data for rendering from the Russian, English or Spanish representations we need.

8. Denote in the script of generating tiles the region of China for generating tiles


We find in the script /home/osm/mapnik/generate_tiles_multiprocess.py these lines:

 bbox = (-180.0,-90.0, 180.0,90.0) #          render_tiles(bbox, mapfile, tile_dir, 0, 5, "World") #     0-  5-  

After these lines we will write:

 bbox = (85.0,19.7,132.5,40.8) #       render_tiles(bbox, mapfile, tile_dir, 6, 15, "China") #     6-  15-  exit() 

This will allow China to generate tiles from the 6th to the 15th scale containing signatures in the language we need. The exit () instruction allows you to opt out of further rendering the default areas in the script /home/osm/mapnik/generate_tiles_multiprocess.py (Muenchen, Muenchen +, Muenchen ++, Nuernberg, Karlsruhe, Karlsruhe +, Augsburg, Augsburg +, Europe +). There is also a /home/osm/mapnik/generate_tiles.py script that starts the rendering process in one stream. But we will not use it, because If there are multiple processor cores, the /home/osm/mapnik/generate_tiles_multiprocess.py script will generally work faster.

9. Generate tiles for selected locations


We need to create directories where Mapnik will put the generated tiles:

 mkdir /home/osm/mapnik/tiles mkdir /home/osm/mapnik/tiles/ru mkdir /home/osm/mapnik/tiles/en mkdir /home/osm/mapnik/tiles/es 

We have to start generating tiles three times, changing the values ​​of the variables of the system environment MAPNIK_MAP_FILE and MAPNIK_TILE_DIR. The script will read these variables and pass them to Mapnik to adjust the tile rendering:

 export MAPNIK_MAP_FILE=/home/osm/mapnik/china_ru.xml export MAPNIK_TILE_DIR=/home/osm/mapnik/tiles/ru/ exec python /home/osm/mapnik/generate_tiles_multiprocess.py export MAPNIK_MAP_FILE=/home/osm/mapnik/china_en.xml export MAPNIK_TILE_DIR=/home/osm/mapnik/tiles/en/ exec python /home/osm/mapnik/generate_tiles_multiprocess.py export MAPNIK_MAP_FILE=/home/osm/mapnik/china_es.xml export MAPNIK_TILE_DIR=/home/osm/mapnik/tiles/es/ exec python /home/osm/mapnik/generate_tiles_multiprocess.py 

Tiles are generated.

10. Display the tiles on the web map


To do this, create a simple html-page, in which we will connect the created tile storages. The page file should be placed next to the generated / home / osm / mapnik / tiles tile directory. To display web maps, we will use the LeafletJS javascript framework:

 <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" /> <script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script> </head> <body> <div id="map" style="height: calc(100vh - 15px)"></div> <script> var map = L.map('map', { center: [34.7, 111.7], zoom: 6 }); L.control.layers({ "ru" : L.tileLayer('tiles/ru/{z}/{x}/{y}.png').addTo(map), "en" : L.tileLayer('tiles/en/{z}/{x}/{y}.png'), "es" : L.tileLayer('tiles/es/{z}/{x}/{y}.png') }, null).addTo(map); </script> </body> </html> 

This page will be executed locally. In the upper right corner, a control should be available, allowing you to select a tile source. Russian, English and Spanish versions of maps will be as follows:





11. Tile rendering on the fly


You will need to configure several mod_tile modules of the Apache web server, each of which will be responsible for its mapping - / ru - for rendering Russian tiles, / en - for rendering, respectively, English tiles, and so on. Details of mod_tile, httpd and renderd settings are in the instructions from Gis-Lab . As part of the demonstration of the example of localization of OpenStreetMap maps, I do not consider it necessary to deploy a full-fledged tile server.

Notes



findings


The problem of localization of maps OpenStreetMap is not very difficult. There are several approaches to solving this problem. This article offers a very specific working version. I am sure that a respected reader will have many questions on the content of this article, and they will have their own ideas for branching the proposed algorithm. The author of the article does not claim to be the only correct method for solving the problem of Russification / localization of OpenStreetMap maps and is happy to read about your “tricks” in solving this issue.

Source: https://habr.com/ru/post/259141/


All Articles