Codebase list pyosmium / c105af4
New upstream version 3.1.0 Bas Couwenberg 3 years ago
22 changed file(s) with 354 addition(s) and 235 deletion(s). Raw diff Collapse all Expand all
0 name: 'Install pyosmium dependencies'
1
2 runs:
3 using: "composite"
4
5 steps:
6 - name: Install pip dependencies
7 run: |
8 python -m pip install --upgrade pip
9 pip install nose shapely setuptools
10 shell: bash
11
12 - name: Install package dependencies
13 run: |
14 git clone --quiet --depth 1 https://github.com/osmcode/libosmium.git contrib/libosmium
15 git clone --quiet --depth 1 https://github.com/mapbox/protozero.git contrib/protozero
16 git clone --quiet --depth 1 https://github.com/pybind/pybind11.git contrib/pybind11
17 shell: bash
0 name: CI
1
2 on: [ push, pull_request ]
3
4 jobs:
5 build-default:
6 runs-on: ubuntu-20.04
7
8 strategy:
9 matrix:
10 python-version: [3.6, 3.7, 3.8]
11
12 steps:
13 - uses: actions/checkout@v2
14
15 - name: Set up Python ${{ matrix.python-version }}
16 uses: actions/setup-python@v2
17 with:
18 python-version: ${{ matrix.python-version }}
19
20 - name: Install packages
21 run: sudo apt-get install -y -qq libboost-dev libexpat1-dev zlib1g-dev libbz2-dev libproj-dev libgeos-dev
22
23 - uses: ./.github/actions/install-dependencies
24
25 - name: Build package
26 run: python setup.py build
27 shell: bash
28
29 - name: Run tests
30 run: python run_tests.py
31 shell: bash
32 working-directory: test
33
34
35 build-ubuntu-1604:
36 runs-on: ubuntu-16.04
37
38 strategy:
39 matrix:
40 compiler: [gcc, clang]
41
42 steps:
43 - uses: actions/checkout@v2
44
45 - uses: actions/setup-python@v2
46 with:
47 python-version: 3.5
48
49 - name: Install packages
50 run: sudo apt-get install -y -qq libboost-dev libexpat1-dev zlib1g-dev libbz2-dev libproj-dev libgeos-dev
51
52 - uses: ./.github/actions/install-dependencies
53
54 - name: Build package
55 run: python setup.py build
56 shell: bash
57 env:
58 CC: gcc-5
59 CXX: g++-5
60 if: ${{ matrix.compiler == 'gcc' }}
61
62 - name: Build package
63 run: python setup.py build
64 shell: bash
65 env:
66 CC: clang-6.0
67 CXX: clang++-6.0
68 if: ${{ matrix.compiler == 'clang' }}
69
70 - name: Run tests
71 run: python run_tests.py
72 shell: bash
73 working-directory: test
74
75 build-ubuntu-2004:
76 runs-on: ubuntu-20.04
77
78 strategy:
79 matrix:
80 compiler: [gcc, clang]
81
82 steps:
83 - uses: actions/checkout@v2
84
85 - uses: actions/setup-python@v2
86 with:
87 python-version: 3.9
88
89 - name: Install packages
90 run: sudo apt-get install -y -qq libboost-dev libexpat1-dev zlib1g-dev libbz2-dev libproj-dev libgeos-dev
91
92 - uses: ./.github/actions/install-dependencies
93
94 - name: Build package
95 run: python setup.py build
96 shell: bash
97 env:
98 CC: gcc-10
99 CXX: g++-10
100 if: ${{ matrix.compiler == 'gcc' }}
101
102 - name: Build package
103 run: python setup.py build
104 shell: bash
105 env:
106 CC: clang-10
107 CXX: clang++-10
108 if: ${{ matrix.compiler == 'clang' }}
109
110 - name: Run tests
111 run: python run_tests.py
112 shell: bash
113 working-directory: test
114
115 build-macos:
116 runs-on: macos-latest
117
118 steps:
119 - uses: actions/checkout@v2
120
121 - uses: actions/setup-python@v2
122 with:
123 python-version: 3
124
125 - name: Install packages
126 run: brew install boost geos
127 shell: bash
128
129 - uses: ./.github/actions/install-dependencies
130
131 - name: Build package
132 run: python setup.py build
133 shell: bash
134
135 - name: Run tests
136 run: python run_tests.py
137 shell: bash
138 working-directory: test
139
140
+0
-84
.travis.yml less more
0 #-----------------------------------------------------------------------------
1 #
2 # Configuration for continuous integration service at travis-ci.org
3 #
4 #-----------------------------------------------------------------------------
5
6
7 matrix:
8 include:
9 - os: linux
10 compiler: "clang-3.4"
11 dist: xenial
12 language: cpp
13 - os: linux
14 compiler: "clang-7"
15 dist: bionic
16 language: cpp
17 - os: linux
18 compiler: "gcc-5"
19 dist: xenial
20 language: cpp
21 - os: linux
22 compiler: "gcc-8"
23 dist: bionic
24 language: cpp
25 - os: linux
26 compiler: gcc
27 python: 3.4
28 dist: xenial
29 language: python
30 - os: linux
31 compiler: gcc
32 python: 3.5
33 dist: bionic
34 language: python
35 - os: linux
36 compiler: gcc
37 python: 3.6
38 dist: bionic
39 language: python
40 - os: linux
41 compiler: gcc
42 python: 3.7
43 dist: bionic
44 language: python
45 - os: linux
46 compiler: gcc
47 python: 3.8
48 dist: bionic
49 language: python
50 - os: osx
51 osx_image: xcode7
52 compiler: clang
53 language: cpp
54 - os: osx
55 osx_image: xcode10.1
56 compiler: clang
57 language: cpp
58
59 # http://docs.travis-ci.com/user/apt/
60 addons:
61 apt:
62 packages:
63 - libboost-dev
64 - python3
65 - python3-dev
66 - python3-pip
67
68 install:
69 - git clone --quiet --depth 1 https://github.com/osmcode/libosmium.git contrib/libosmium
70 - git clone --quiet --depth 1 https://github.com/mapbox/protozero.git contrib/protozero
71 - git clone --quiet --depth 1 https://github.com/pybind/pybind11.git contrib/pybind11
72 - if [ "$TRAVIS_OS_NAME" = 'osx' ]; then
73 pip3 install --user -q nose mock shapely setuptools;
74 else
75 pip3 install -q nose mock shapely setuptools;
76 fi
77
78 script:
79 - python3 --version
80 - python3 setup.py build
81 - cd test
82 - python3 run_tests.py
83
22
33 All notable changes to this project will be documented in this file.
44 This project adheres to [Semantic Versioning](http://semver.org/).
5
6 ## [3.1.0] - 2020-11-03
7
8 ### Added
9
10 ### Changed
11
12 - improved help messages for update tools
13 - update to pybind11 2.6
14
15 ### Fixed
16
17 - pyosmium-up-to-date: fix missing flag when applying changes to history files
18 - smaller linting and documentation issues
519
620 ## [3.0.1] - 2020-07-25
721
00 cmake_minimum_required(VERSION 2.8.12)
11 project(pyosmium)
2 set(PACKAGE_VERSION 3.0.1)
23
34 list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
45
67 include_directories(SYSTEM ${OSMIUM_INCLUDE_DIRS} ${PROTOZERO_INCLUDE_DIR})
78 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib)
89
10 if(NOT "${CMAKE_CXX_STANDARD}")
11 if(MSVC)
12 set(CMAKE_CXX_STANDARD 14)
13 else()
14 set(CMAKE_CXX_STANDARD 11)
15 endif()
16 endif()
17
18 # required for pybind11 < 2.6
919 if(MSVC)
10 set(PYBIND11_CPP_STANDARD /std:c++14)
20 set(PYBIND11_CPP_STANDARD /std=c++${CMAKE_CXX_STANDARD})
1121 else()
12 set(PYBIND11_CPP_STANDARD -std=c++11)
22 set(PYBIND11_CPP_STANDARD -std=c++${CMAKE_CXX_STANDARD})
1323 endif()
24
25 message(STATUS "Building in C++${CMAKE_CXX_STANDARD} mode")
1426
1527 if(PYBIND11_PREFIX)
1628 add_subdirectory(${PYBIND11_PREFIX} contrib/pybind11)
33 library, a library for working with OpenStreetMap data in a fast and flexible
44 manner.
55
6 [![Travis Build Status](https://api.travis-ci.org/osmcode/pyosmium.svg)](http://travis-ci.org/osmcode/pyosmium)
6 [![Github Actions Build Status](https://github.com/osmcode/pyosmium/workflows/CI/badge.svg)](https://github.com/osmcode/pyosmium/actions?query=workflow%3ACI)
77 [![Appeveyor Build status](https://ci.appveyor.com/api/projects/status/ch3gwxucycytako4/branch/master?svg=true)](https://ci.appveyor.com/project/lonvia/pyosmium/branch/master)
88
99 ## Installation
3232 for planet-wide updates. There are change files for
3333 minutely, hourly and daily intervals available.
3434
35 * `Geofabrik <http://download.geofabrik.de>`_ offers daily change files
35 * `Geofabrik <https://download.geofabrik.de>`_ offers daily change files
3636 for all its updates. See the extract page for a link to the replication URL.
3737 Note that change files go only about 3 months back. Older files are deleted.
3838
39 * `openstreetmap.fr <http://download.geofabrik.de>`_ offers minutely change
40 files for all its extracts.
39 * download.openstreetmap.fr offers
40 `minutely change files <https://download.openstreetmap.fr/replication/>`_
41 for all its `extracts <https://download.openstreetmap.fr/extracts/>`_.
4142
42 For other services also check out the list of services on the
43 For other services also check out the list of providers on the
4344 `OSM wiki <https://wiki.openstreetmap.org/wiki/Planet.osm>`_.
4445
4546 Updating a planet or extract
1212
1313 class AmenityListHandler(o.SimpleHandler):
1414
15 def print_amenity(amenity, tags, lon, lat):
15 def print_amenity(self, tags, lon, lat):
1616 name = tags.get('name', '')
1717 print("%f %f %-15s %s" % (lon, lat, tags['amenity'], name))
1818
66 class NamesHandler(osmium.SimpleHandler):
77
88 def output_pubs(self, tags):
9 if tags.get('amenity') == 'pub':
10 if 'name' in tags:
11 print(tags['name'])
9 if tags.get('amenity') == 'pub' and 'name' in tags:
10 print(tags['name'])
1211
1312 def node(self, n):
1413 self.output_pubs(n.tags)
88
99 def way(self, w):
1010 for n in w.nodes:
11 n.lat, n.lon # throws an exception if the coordinates are missing
1211 loc = idx.get(n.ref)
13 print("%d %s" %(w.id, len(w.nodes)))
12 print("%d %s" % (w.id, len(w.nodes)))
1413
1514 if len(sys.argv) != 3:
1615 print("Usage: python create_nodecache.py <osm file> <node cache>")
5858
5959 py::class_<osmium::io::Writer>(m, "Writer",
6060 "Class for writing OSM data to a file. This class just encapsulates an "
61 "OSM file,. Have a look `osmium.SimpleWriter` for a high-level interface "
61 "OSM file. Have a look `osmium.SimpleWriter` for a high-level interface "
6262 "for writing out data.")
6363 .def(py::init<std::string>())
6464 .def(py::init<osmium::io::File>())
5252 class MergeInputReader
5353 {
5454 public:
55 void apply(BaseHandler& handler, std::string const &idx = "",
56 bool simplify = true)
55 void apply(BaseHandler& handler, std::string const &idx, bool simplify)
5756 {
5857 if (idx.empty())
5958 apply_without_location(handler, simplify);
6261 }
6362
6463 void apply_to_reader(osmium::io::Reader &reader, osmium::io::Writer &writer,
65 bool with_history = true)
64 bool with_history)
6665 {
6766 auto input = osmium::io::make_input_iterator_range<osmium::OSMObject>(reader);
6867 if (with_history) {
109108 }
110109
111110 private:
112 void apply_without_location(BaseHandler& handler, bool simplify = true)
111 void apply_without_location(BaseHandler& handler, bool simplify)
113112 {
114113 if (simplify) {
115114 objects.sort(osmium::object_order_type_id_reverse_version());
132131 }
133132
134133 void apply_with_location(BaseHandler& handler, std::string const &idx,
135 bool simplify = true)
134 bool simplify)
136135 {
137136 using Index_fab =
138137 osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>;
104104 if 'BOOST_PREFIX' in env:
105105 cmake_args += ['-DBOOST_ROOT={}'.format(env['BOOST_PREFIX'])]
106106
107 if 'CMAKE_CXX_STANDARD' in env:
108 cmake_args += ['-DCMAKE_CXX_STANDARD={}'.format(env['CMAKE_CXX_STANDARD'])]
109
107110 if not os.path.exists(self.build_temp):
108111 os.makedirs(self.build_temp)
109112 subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env)
111114
112115 versions = get_versions()
113116
114 if sys.version_info < (3,0):
115 raise RuntimeError("Python 3.3 or larger required.")
117 if sys.version_info < (3,4):
118 raise RuntimeError("Python 3.4 or larger required.")
116119
117120 with open('README.rst', 'r') as descfile:
118121 long_description = descfile.read()
127130 maintainer='Sarah Hoffmann',
128131 maintainer_email='lonvia@denofr.de',
129132 download_url='https://github.com/osmcode/pyosmium',
130 url='http://osmcode.org/pyosmium',
133 url='https://osmcode.org/pyosmium',
131134 keywords=["OSM", "OpenStreetMap", "Osmium"],
132135 license='BSD',
133136 scripts=['tools/pyosmium-get-changes', 'tools/pyosmium-up-to-date'],
1818 __HandlerWithCallbacks.area = staticmethod(area)
1919
2020 return __HandlerWithCallbacks()
21
0 import osmium.osm.mutable
01 from ._osm import *
1 import osmium.osm.mutable
22
33 def create_mutable_node(node, **args):
44 """ Create a mutable node replacing the properties given in the
2525 Way.replace = create_mutable_way
2626 Relation.replace = create_mutable_relation
2727
28 Location.__repr__ = lambda l : 'osmium.osm.Location(x=%r, y=%r)' \
29 % (l.x, l.y) \
28 def _make_repr(attr_list):
29 fmt_string = 'osmium.osm.{0}('\
30 + ', '.join(['{0}={{1.{0}!r}}'.format(x) for x in attr_list])\
31 + ')'
32
33 return lambda o: fmt_string.format(o.__class__.__name__, o)
34
35 def _list_repr(obj):
36 return 'osmium.osm.{}([{}])'.format(obj.__class__.__name__,
37 ', '.join(map(repr, obj)))
38
39 def _list_elipse(obj):
40 objects = ','.join(map(str, obj))
41 if len(objects) > 50:
42 objects = objects[:47] + '...'
43 return objects
44
45 Location.__repr__ = lambda l: 'osmium.osm.Location(x={0.x!r}, y={0.y!r})'.format(l) \
3046 if l.valid() else 'osmium.osm.Location()'
31 Location.__str__ = lambda l : '%f/%f' % (l.lon_without_check(), l.lat_without_check()) \
32 if l.valid() else 'invalid'
47 Location.__str__ = lambda l: '{:f}/{:f}'.format(l.lon_without_check(),
48 l.lat_without_check()) \
49 if l.valid() else 'invalid'
3350
34 Box.__repr__ = lambda b : 'osmium.osm.Box(bottom_left=%r, top_right=%r)' \
35 % (b.bottom_left, b.top_right)
36 Box.__str__ = lambda b : '(%s %s)' % (b.bottom_left, b.top_right)
51 Box.__repr__ = _make_repr(['bottom_left', 'top_right'])
52 Box.__str__ = lambda b: '({0.bottom_left!s} {0.top_right!s})'.format(b)
3753
38 Tag.__repr__ = lambda t : 'osmium.osm.Tag(k=%r, v=%r)' % (t.k, t.v)
54 Tag.__repr__ = _make_repr(['k', 'v'])
55 Tag.__str__ = lambda t: '{0.k}={0.v}'.format(t)
3956
40 Tag.__str__ = lambda t : '%s=%s' % (t.k, t.v)
57 TagList.__repr__ = lambda t: "osmium.osm.TagList({%s})" \
58 % ', '.join(["%r: %r" % (i.k, i.v) for i in t])
59 TagList.__str__ = lambda t: '{' + _list_elipse(t) + '}'
4160
42 TagList.__repr__ = lambda t : "osmium.osm.TagList({%s})" \
43 % ",".join(["%r=%r" % (i.k, i.v) for i in t])
44 TagList.__str__ = lambda t : "{%s}" % ",".join([str(i) for i in t])
61 NodeRef.__repr__ = _make_repr(['ref', 'location'])
62 NodeRef.__str__ = lambda n: '{0.ref:d}@{0.location!s}'.format(n) \
63 if n.location.valid() else str(n.ref)
4564
46 NodeRef.__repr__ = lambda n : 'osmium.osm.NodeRef(ref=%r, location=%r)' % (n.ref, n.location)
47 NodeRef.__str__ = lambda n : '%s@%s' % (n.ref, n.location) if n.location.valid() \
48 else str(n.ref)
65 NodeRefList.__repr__ = _list_repr
66 NodeRefList.__str__ = lambda o: '[' + _list_elipse(o) + ']'
4967
50 NodeRefList.__repr__ = lambda t : "%s([%s])" % (t.__class__.__name__,
51 ",".join([repr(i) for i in t]))
52 NodeRefList.__str__ = lambda t : "[%s]" % ",".join([str(i) for i in t])
68 RelationMember.__repr__ = _make_repr(['ref', 'type', 'role'])
69 RelationMember.__str__ = lambda r: ('{0.type}{0.ref:d}@{0.role}' \
70 if r.role else '{0.type}{0.ref:d}').format(r)
5371
54 RelationMember.__repr__ = lambda r : 'osmium.osm.RelationMember(ref=%r, type=%r, role=%r)' \
55 % (r.ref, r.type, r.role)
56 RelationMember.__str__ = lambda r : '%s%d@%s' % (r.type, r.ref, r.role) \
57 if r.role else '%s%d' % (r.type, r.ref)
72 RelationMemberList.__repr__ = _list_repr
73 RelationMemberList.__str__ = lambda o: '[' + _list_elipse(o) + ']'
5874
59 RelationMemberList.__repr__ = lambda t : "osmium.osm.RelationMemberList([%s])" \
60 % ",".join([repr(i) for i in t])
61 RelationMemberList.__str__ = lambda t : "[%s]" % ",".join([str(i) for i in t])
75 OSMObject.__repr__ = _make_repr(['id', 'deleted', 'visible', 'version', 'changeset',
76 'uid', 'timestamp', 'user', 'tags'])
6277
63 OSMObject.__repr__ = lambda o : '%s(id=%r, deleted=%r, visible=%r, version=%r, changeset=%r, uid=%r, timestamp=%r, user=%r, tags=%r)'% (o.__class__.__name__, o.id, o.deleted, o.visible, o.version, o.changeset, o.uid, o.timestamp, o.user, o.tags)
78 Node.__repr__ = _make_repr(['id', 'deleted', 'visible', 'version', 'changeset',
79 'uid', 'timestamp', 'user', 'tags', 'location'])
80 Node.__str__ = lambda n: 'n{0.id:d}: location={0.location!s} tags={0.tags!s}'\
81 .format(n)
6482
65 def _str_ellipse(s, length=50):
66 s = str(s)
67 return s if len(s) <= length else (s[:length - 4] + '...' + s[-1])
83 Way.__repr__ = _make_repr(['id', 'deleted', 'visible', 'version', 'changeset',
84 'uid', 'timestamp', 'user', 'tags', 'nodes'])
85 Way.__str__ = lambda o: 'w{0.id:d}: nodes={0.nodes!s} tags={0.tags!s}' \
86 .format(o)
6887
69 Node.__repr__ = lambda o : '%s(id=%r, deleted=%r, visible=%r, version=%r, changeset=%r, uid=%r, timestamp=%r, user=%r, tags=%r, location=%r)'% (o.__class__.__name__, o.id, o.deleted, o.visible, o.version, o.changeset, o.uid, o.timestamp, o.user, o.tags, o.location)
70 Node.__str__ = lambda n : 'n%d: location=%s tags=%s' \
71 % (n.id, n.location, _str_ellipse(n.tags))
88 Relation.__repr__ = _make_repr(['id', 'deleted', 'visible', 'version', 'changeset',
89 'uid', 'timestamp', 'user', 'tags', 'members'])
90 Relation.__str__ = lambda o: 'r{0.id:d}: members={0.members!s}, tags={0.tags!s}' \
91 .format(o)
7292
73 Way.__repr__ = lambda o : '%s(id=%r, deleted=%r, visible=%r, version=%r, changeset=%r, uid=%r, timestamp=%r, user=%r, tags=%r, nodes=%r)'% (o.__class__.__name__, o.id, o.deleted, o.visible, o.version, o.changeset, o.uid, o.timestamp, o.user, o.tags, o.nodes)
74 Way.__str__ = lambda o : 'w%d: nodes=%s tags=%s' \
75 % (o.id, _str_ellipse(o.nodes), _str_ellipse(o.tags))
76
77 Relation.__repr__ = lambda o : '%s(id=%r, deleted=%r, visible=%r, version=%r, changeset=%r, uid=%r, timestamp=%r, user=%r, tags=%r, members=%r)'% (o.__class__.__name__, o.id, o.deleted, o.visible, o.version, o.changeset, o.uid, o.timestamp, o.user, o.tags, o.members)
78 Relation.__str__ = lambda o : 'r%d: members=%s, tags=%s' \
79 % (o.id, _str_ellipse(o.members), _str_ellipse(o.tags))
80
81 Changeset.__repr__ = lambda o : '%s(id=%r, uid=%r, created_at=%r, closed_at=%r, open=%r, num_changes=%r, bounds=%r, user=%r, tags=%s)' %(o.__class__.__name__, o.id, o.uid, o.created_at, o.closed_at, o.open, o.num_changes, o.bounds, o.user, o.tags)
82 Changeset.__str__ = lambda o : 'c%d: closed_at=%s, bounds=%s, tags=%s' \
83 % (o.id, o.closed_at, o.bounds, _str_ellipse(o.tags))
93 Changeset.__repr__ = _make_repr(['id', 'uid', 'created_at', 'closed_at', 'open',
94 'num_changes', 'bounds', 'user', 'tags'])
95 Changeset.__str__ = lambda o: 'c{0.id:d}: closed_at={0.closed_at!s}, bounds={0.bounds!s}, tags={0.tags!s}' \
96 .format(o)
99 """
1010
1111 def __init__(self, base=None, id=None, version=None, visible=None, changeset=None,
12 timestamp=None, uid=None, tags=None, user=None):
12 timestamp=None, uid=None, tags=None, user=None):
1313 if base is None:
1414 self.id = id
1515 self.version = version
7272 self.members = members
7373 else:
7474 self.members = members if members is not None else base.members
75
76
00 """ Helper functions to communicate with replication servers.
11 """
22
3 try:
4 import urllib.request as urlrequest
5 except ImportError:
6 import urllib2 as urlrequest
7 try:
8 import urllib.error as urlerror
9 except ImportError:
10 import urllib2 as urlerror
3 import urllib.request as urlrequest
114 import datetime as dt
125 from collections import namedtuple
136 from math import ceil
1710
1811 import logging
1912
20 log = logging.getLogger('pyosmium')
21 log.addHandler(logging.NullHandler())
13 LOG = logging.getLogger('pyosmium')
14 LOG.addHandler(logging.NullHandler())
2215
2316 OsmosisState = namedtuple('OsmosisState', ['sequence', 'timestamp'])
2417 DownloadResult = namedtuple('DownloadResult', ['id', 'reader', 'newest'])
2518
26 class ReplicationServer(object):
19 class ReplicationServer:
2720 """ Represents a server that publishes replication data. Replication
2821 change files allow to keep local OSM data up-to-date without downloading
2922 the full dataset again.
8780 try:
8881 diffdata = self.get_diff_block(current_id)
8982 except:
90 log.debug("Error during diff download. Bailing out.")
83 LOG.debug("Error during diff download. Bailing out.")
9184 diffdata = ''
9285 if len(diffdata) == 0:
9386 if start_id == current_id:
9588 break
9689
9790 left_size -= rd.add_buffer(diffdata, self.diff_type)
98 log.debug("Downloaded change %d. (%d kB available in download buffer)"
99 % (current_id, left_size / 1024))
91 LOG.debug("Downloaded change %d. (%d kB available in download buffer)",
92 current_id, left_size / 1024)
10093 current_id += 1
10194
10295 return DownloadResult(current_id - 1, rd, newest.sequence)
136129 return diffs.id
137130
138131 def apply_diffs_to_file(self, infile, outfile, start_id, max_size=1024,
139 set_replication_header=True, extra_headers={},
132 set_replication_header=True, extra_headers=None,
140133 outformat=None):
141134 """ Download diffs starting with sequence id `start_id`, merge them
142135 with the data from the OSM file named `infile` and write the result
177170 h.set("osmosis_replication_sequence_number", str(diffs.id))
178171 info = self.get_state_info(diffs.id)
179172 h.set("osmosis_replication_timestamp", info.timestamp.strftime("%Y-%m-%dT%H:%M:%SZ"))
180 for k,v in extra_headers.items():
181 h.set(k, v)
173 if extra_headers is not None:
174 for k, v in extra_headers.items():
175 h.set(k, v)
182176
183177 if outformat is None:
184178 of = oio.File(outfile)
185179 else:
186180 of = oio.File(outfile, outformat)
187181
182 of.has_multiple_object_versions = has_history
188183 writer = oio.Writer(of, h)
189184
190 log.debug("Merging changes into OSM file.")
185 LOG.debug("Merging changes into OSM file.")
191186
192187 diffs.reader.apply_to_reader(reader, writer, has_history)
193188
219214 lower = None
220215 lowerid = 0
221216 while lower is None:
222 log.debug("Trying with Id %s" % lowerid)
217 LOG.debug("Trying with Id %s", lowerid)
223218 lower = self.get_state_info(lowerid)
224219
225220 if lower is not None and lower.timestamp >= timestamp:
286281 returns `None`. `retries` sets the number of times the download
287282 is retried when pyosmium detects a truncated state file.
288283 """
289 for retry in range(retries + 1):
284 for _ in range(retries + 1):
290285 try:
291286 response = self.open_url(self.make_request(self.get_state_url(seq)))
292287 except Exception as err:
293 log.debug("Loading state info {} failed with: {}".format(seq, str(err)))
288 LOG.debug("Loading state info %s failed with: %s", seq, str(err))
294289 return None
295290
296291 ts = None
340335 if seq is None:
341336 return self.baseurl + '/state.txt'
342337
343 return '%s/%03i/%03i/%03i.state.txt' % (self.baseurl,
344 seq / 1000000, (seq % 1000000) / 1000, seq % 1000)
338 return '%s/%03i/%03i/%03i.state.txt' % \
339 (self.baseurl, seq / 1000000, (seq % 1000000) / 1000, seq % 1000)
345340
346341
347342 def get_diff_url(self, seq):
348343 """ Returns the URL to the diff file for the given sequence id.
349344 """
350 return '%s/%03i/%03i/%03i.%s' % (self.baseurl,
351 seq / 1000000, (seq % 1000000) / 1000, seq % 1000,
352 self.diff_type)
345 return '%s/%03i/%03i/%03i.%s' % \
346 (self.baseurl,
347 seq / 1000000, (seq % 1000000) / 1000, seq % 1000,
348 self.diff_type)
55 from osmium.io import Reader as oreader
66 from osmium.osm import NOTHING
77
8 log = logging.getLogger('pyosmium')
8 LOG = logging.getLogger('pyosmium')
99
1010 ReplicationHeader = namedtuple('ReplicationHeader',
11 ['url', 'sequence', 'timestamp'])
11 ['url', 'sequence', 'timestamp'])
1212
1313 def get_replication_header(fname):
1414 """ Scans the given file for an Osmosis replication header. It returns
2727 url = h.get("osmosis_replication_base_url")
2828
2929 if url or ts:
30 log.debug("Replication information found in OSM file header.")
30 LOG.debug("Replication information found in OSM file header.")
3131
3232 if url:
33 log.debug("Replication URL: %s" % url)
33 LOG.debug("Replication URL: %s", url)
3434 # the sequence ID is only considered valid, if an URL is given
3535 seq = h.get("osmosis_replication_sequence_number")
3636 if seq:
37 log.debug("Replication sequence: %s" % seq)
37 LOG.debug("Replication sequence: %s", seq)
3838 try:
3939 seq = int(seq)
4040 if seq < 0:
41 log.warning("Sequence id '%d' in OSM file header is negative. Ignored." % seq)
41 LOG.warning("Sequence id '%d' in OSM file header is negative. Ignored.", seq)
4242 seq = None
4343 except ValueError:
44 log.warning("Sequence id '%s' in OSM file header is not a number.Ignored" % seq)
44 LOG.warning("Sequence id '%s' in OSM file header is not a number. Ignored.", seq)
4545 seq = None
4646 else:
4747 seq = None
5050 seq = None
5151
5252 if ts:
53 log.debug("Replication timestamp: %s" % ts)
53 LOG.debug("Replication timestamp: %s", ts)
5454 try:
5555 ts = dt.datetime.strptime(ts, "%Y-%m-%dT%H:%M:%SZ")
5656 ts = ts.replace(tzinfo=dt.timezone.utc)
5757
5858 except ValueError:
59 log.warning("Date in OSM file header is not in ISO8601 format (e.g. 2015-12-24T08:08Z). Ignored")
59 LOG.warning("Date in OSM file header is not in ISO8601 format (e.g. 2015-12-24T08:08Z). Ignored.")
6060 ts = None
6161 else:
6262 ts = None
22 """
33
44 # the major version
5 pyosmium_major = '3.0'
5 pyosmium_major = '3.1'
66 # current release (Pip version)
7 pyosmium_release = '3.0.1'
7 pyosmium_release = '3.1.0'
88
99 # libosmium version shipped with the Pip release
1010 libosmium_version = '2.15.6'
1111 # protozero version shipped with the Pip release
1212 protozero_version = '1.7.0'
1313 # pybind11 version shipped with the Pip release
14 pybind11_version = '2.5.0'
14 pybind11_version = '2.6.0'
3030 self.side_effect = [BytesIO(dedent(s).encode()) for s in files]
3131
3232 def test_get_state_url():
33 svr = rserv.ReplicationServer("http://text.org")
33 svr = rserv.ReplicationServer("https://text.org")
3434
3535 data = [
36 (None, 'http://text.org/state.txt'),
37 (1, 'http://text.org/000/000/001.state.txt'),
38 (999, 'http://text.org/000/000/999.state.txt'),
39 (1000, 'http://text.org/000/001/000.state.txt'),
40 (573923, 'http://text.org/000/573/923.state.txt'),
41 (3290012, 'http://text.org/003/290/012.state.txt'),
36 (None, 'https://text.org/state.txt'),
37 (1, 'https://text.org/000/000/001.state.txt'),
38 (999, 'https://text.org/000/000/999.state.txt'),
39 (1000, 'https://text.org/000/001/000.state.txt'),
40 (573923, 'https://text.org/000/573/923.state.txt'),
41 (3290012, 'https://text.org/003/290/012.state.txt'),
4242 ]
4343
4444 for i, o in data:
6868 txnMax=1219304113
6969 txnActiveList=1219303583,1219304054,1219304104""")
7070
71 res = rserv.ReplicationServer("http://test.io").get_state_info()
71 res = rserv.ReplicationServer("https://test.io").get_state_info()
7272
7373 assert_is_not_none(res)
7474 assert_equals(res.timestamp, mkdate(2017, 8, 26, 11, 4, 2))
8888 sequenceNumber=2594669
8989 timestamp=2017-08-26T11\:04\:02Z"""))
9090
91 res = rserv.ReplicationServer("http://test.io").get_state_info()
91 res = rserv.ReplicationServer("https://test.io").get_state_info()
9292
9393 assert_is_not_none(res)
9494 assert_equals(res.timestamp, mkdate(2017, 8, 26, 11, 4, 2))
109109 sequenceNumber=2594669
110110 timestamp=2017-08-26T11\:04\:02Z"""))
111111
112 res = rserv.ReplicationServer("http://test.io").get_state_info()
112 res = rserv.ReplicationServer("https://test.io").get_state_info()
113113
114114 assert_is_not_none(res)
115115 assert_equals(res.timestamp, mkdate(2017, 8, 26, 11, 4, 2))
130130 sequenceNumber=2594669
131131 timestamp=2017-08-26T11\:04\:02Z"""))
132132
133 res = rserv.ReplicationServer("http://test.io").get_state_info()
133 res = rserv.ReplicationServer("https://test.io").get_state_info()
134134
135135 assert_is_not_none(res)
136136 assert_equals(res.timestamp, mkdate(2017, 8, 26, 11, 4, 2))
161161 sequenceNumber=2594669
162162 timestamp=2017-08-26T11\:04\:02Z"""))
163163
164 res = rserv.ReplicationServer("http://test.io").get_state_info()
164 res = rserv.ReplicationServer("https://test.io").get_state_info()
165165
166166 assert_is_none(res)
167167
173173 def test_get_state_server_timeout(mock):
174174 mock.side_effect = URLError(reason='Mock')
175175
176 svr = rserv.ReplicationServer("http://test.io")
176 svr = rserv.ReplicationServer("https://test.io")
177177 assert_is_none(svr.get_state_info())
178178
179179 @patch('osmium.replication.server.urlrequest.urlopen', new_callable=UrllibMock)
186186 w1
187187 r1
188188 """))
189 svr = rserv.ReplicationServer("http://test.io", "opl")
189 svr = rserv.ReplicationServer("https://test.io", "opl")
190190
191191 h = CountingHandler()
192192 assert_equals(100, svr.apply_diffs(h, 100, 10000))
204204 w1
205205 r1
206206 """))
207 svr = rserv.ReplicationServer("http://test.io", "opl")
207 svr = rserv.ReplicationServer("https://test.io", "opl")
208208
209209 h = CountingHandler()
210210 assert_equals(100, svr.apply_diffs(h, 100, 10000, simplify=False))
221221 w1
222222 r1
223223 """))
224 svr = rserv.ReplicationServer("http://test.io", "opl")
224 svr = rserv.ReplicationServer("https://test.io", "opl")
225225
226226 h = CountingHandler()
227227 assert_equals(100, svr.apply_diffs(h, 100, 10000, simplify=True))
236236 n1 x10.0 y23.0
237237 w1 Nn1,n2
238238 """))
239 svr = rserv.ReplicationServer("http://test.io", "opl")
239 svr = rserv.ReplicationServer("https://test.io", "opl")
240240
241241 class Handler(CountingHandler):
242242 def way(self, w):
266266 w1
267267 r1
268268 """))
269 svr = rserv.ReplicationServer("http://test.io", "opl")
269 svr = rserv.ReplicationServer("https://test.io", "opl")
270270
271271 h = CountingHandler()
272272
287287 w1
288288 r1
289289 """))
290 svr = rserv.ReplicationServer("http://test.io", "opl")
290 svr = rserv.ReplicationServer("https://test.io", "opl")
291291
292292 h = CountingHandler()
293293 diffs = svr.collect_diffs(100, 100000)
305305 n1 x10.0 y23.0
306306 w1 Nn1,n2
307307 """))
308 svr = rserv.ReplicationServer("http://test.io", "opl")
308 svr = rserv.ReplicationServer("https://test.io", "opl")
309309
310310 class Handler(CountingHandler):
311311 def way(self, w):
2626
2727 from argparse import ArgumentParser, RawDescriptionHelpFormatter, ArgumentTypeError
2828 import datetime as dt
29 from sys import version_info as python_version
3029 import socket
3130 from osmium.replication import server as rserv
3231 from osmium.replication import newest_change_from_file
3332 from osmium.replication.utils import get_replication_header
33 from osmium.version import pyosmium_release
3434 from osmium import SimpleHandler, WriteHandler
3535
3636 try:
8484 def from_date(datestr):
8585 try:
8686 date = dt.datetime.strptime(datestr, "%Y-%m-%dT%H:%M:%SZ")
87 if python_version >= (3,0):
88 date = date.replace(tzinfo=dt.timezone.utc)
87 date = date.replace(tzinfo=dt.timezone.utc)
8988 except ValueError:
9089 raise ArgumentTypeError("Date needs to be in ISO8601 format (e.g. 2015-12-24T08:08:08Z).")
9190
132131 usage=None if from_main else 'pyosmium-get-changes [options]',
133132 formatter_class=RawDescriptionHelpFormatter)
134133 parser.add_argument('-v', dest='loglevel', action='count', default=0,
135 help='Increase verbosity')
134 help='Increase verbosity (can be used multiple times)')
136135 parser.add_argument('-o', '--outfile', dest='outfile',
137136 help=h("""Name of diff output file. If omitted, only the
138137 sequence ID will be printed where updates would start."""))
165164 ignore potential replication information in the
166165 header and search for the newest OSM object."""))
167166 parser.add_argument('-d', '--no-deduplicate', action='store_false', dest='simplify',
168 help='Do not deduplicate and sort diffs.')
167 help='Do not deduplicate diffs.')
169168 parser.add_argument('--socket-timeout', dest='socket_timeout', type=int, default=60,
170169 help='Set timeout for file downloads.')
170 parser.add_argument('--version', action='version', version='pyosmium ' + pyosmium_release)
171171
172172 return parser
173173
174174
175175 def main(args):
176176 logging.basicConfig(stream=sys.stderr,
177 format='%(levelname)s: %(message)s')
177 format='%(asctime)s %(levelname)s: %(message)s',
178 datefmt='%Y-%m-%d %H:%M:%S')
178179
179180 options = get_arg_parser(from_main=True).parse_args(args)
180181
1414 subsequent calls to pyosmium-up-to-date will continue the updates from the same
1515 server exactly where they have left of.
1616
17 This program can update normal OSM data files as well as OSM history files.
18 It detects automatically on what type of file it is called.
19
1720 The program returns 0, if updates have been successfully applied up to
1821 the newest data. It returns 1, if some updates have been applied but
1922 there is still data available on the server (either because the size
3437
3538 from argparse import ArgumentParser, RawDescriptionHelpFormatter
3639 import datetime as dt
37 from sys import version_info as python_version
3840 from osmium.replication import server as rserv
3941 from osmium.replication.utils import get_replication_header
4042 from osmium.replication import newest_change_from_file
9395
9496 if not options.force_update:
9597 cmpdate = dt.datetime.utcnow() - dt.timedelta(days=90)
96 if python_version >= (3,0):
97 cmpdate = cmpdate.replace(tzinfo=dt.timezone.utc)
98 cmpdate = cmpdate.replace(tzinfo=dt.timezone.utc)
9899 if ts < cmpdate:
99100 log.error(
100101 """The OSM file is more than 3 months old. You should download a
115116 else:
116117 ofname = outfile
117118
118 extra_headers = { 'generator' : 'pyosmium-up-to-date/' + pyosmium_release }
119 outseqs = svr.apply_diffs_to_file(infile, ofname, startseq,
120 max_size=options.outsize*1024,
121 extra_headers=extra_headers,
122 outformat=options.outformat)
123
124 if outseqs is None:
125 log.info("No new updates found.")
126 return 3
127
128 if outfile is None:
129 os.rename(ofname, infile)
119 try:
120 extra_headers = { 'generator' : 'pyosmium-up-to-date/' + pyosmium_release }
121 outseqs = svr.apply_diffs_to_file(infile, ofname, startseq,
122 max_size=options.outsize*1024,
123 extra_headers=extra_headers,
124 outformat=options.outformat)
125
126 if outseqs is None:
127 log.info("No new updates found.")
128 return 3
129
130 if outfile is None:
131 os.rename(ofname, infile)
132 finally:
133 if outfile is None:
134 try:
135 os.remove(ofname)
136 except FileNotFoundError:
137 pass
130138
131139 log.info("Downloaded until %d. Server has data available until %d." % outseqs)
132140
176184 usage=None if from_main else 'pyosmium-up-to-date [options] <osm file>',
177185 formatter_class=RawDescriptionHelpFormatter)
178186 parser.add_argument('-v', dest='loglevel', action='count', default=0,
179 help='Increase verbosity')
187 help='Increase verbosity (can be used multiple times)')
180188 parser.add_argument('infile', metavar='<osm file>', help="OSM file to update")
181189 parser.add_argument('-o', '--outfile', dest='outfile',
182190 help=h("""Name output of file. If missing, the input file
208216 received cookies will be written to.""")
209217 parser.add_argument('--socket-timeout', dest='socket_timeout', type=int, default=60,
210218 help='Set timeout for file downloads.')
219 parser.add_argument('--version', action='version', version='pyosmium ' + pyosmium_release)
211220
212221 return parser
213222
216225
217226 if __name__ == '__main__':
218227 logging.basicConfig(stream=sys.stderr,
219 format='%(asctime)s %(levelname)s: %(message)s')
228 format='%(asctime)s %(levelname)s: %(message)s',
229 datefmt='%Y-%m-%d %H:%M:%S')
220230
221231 options = get_arg_parser(from_main=True).parse_args()
222232 log.setLevel(max(3 - options.loglevel, 0) * 10)