pax_global_header00006660000000000000000000000064126546031340014516gustar00rootroot0000000000000052 comment=ddddda4a58ab98c7e55ec7828be2ebdf14e3d0f7 pyosmium-2.6.0/000077500000000000000000000000001265460313400134055ustar00rootroot00000000000000pyosmium-2.6.0/.gitignore000066400000000000000000000000141265460313400153700ustar00rootroot00000000000000build *.pyc pyosmium-2.6.0/.travis.yml000066400000000000000000000025171265460313400155230ustar00rootroot00000000000000#----------------------------------------------------------------------------- # # Configuration for continuous integration service at travis-ci.org # #----------------------------------------------------------------------------- language: cpp sudo: false matrix: include: - os: linux compiler: clang env: USE_PYTHON_VERSION=2 - os: linux compiler: clang env: USE_PYTHON_VERSION=3 - os: linux compiler: gcc env: USE_PYTHON_VERSION=2 - os: linux compiler: gcc env: USE_PYTHON_VERSION=3 # http://docs.travis-ci.com/user/apt/ addons: apt: sources: - boost-latest - ubuntu-toolchain-r-test packages: - g++-4.8 - gcc-4.8 - libboost-python1.55-dev - libboost1.55-dev - libsparsehash-dev - python-nose - python3 - python3-dev - python3-nose install: - git clone --quiet --depth 1 https://github.com/osmcode/libosmium.git ../libosmium script: - if [ "${CXX}" = 'g++' ]; then CXX=g++-4.8; CC=gcc-4.8; fi - python${USE_PYTHON_VERSION} --version - python${USE_PYTHON_VERSION} setup.py build - cd test - python${USE_PYTHON_VERSION} run_tests.py pyosmium-2.6.0/CHANGELOG.md000066400000000000000000000025021265460313400152150ustar00rootroot00000000000000 # Change Log All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). ## [unreleased] - ### Added ### Changed ### Fixed ## [2.6.0] - 2016-02-04 ### Added - Experimental write support, see documentation - Multiple examples for writing data ### Changed - Use current libosmium - Improve timestamp to datetime conversion - Simplified package structure that uses the compiled libs directly ## [2.5.4] - 2015-12-03 ### Changed - Use current libosmium - README updates ## [2.5.3] - 2015-11-17 ### Changed - Use current libosmium ## [2.4.1] - 2015-08-31 ### Changed - Use current libosmium ## [2.3.0] - 2015-08-18 ### Changed - Use current libosmium ## [2.2.0] - 2015-07-04 ### Changed - Use current libosmium ### Fixed - Exception not caught in test. [unreleased]: https://github.com/osmcode/pyosmium/compare/v2.6.0...HEAD [2.6.0]: https://github.com/osmcode/pyosmium/compare/v2.5.4...v2.6.0 [2.5.4]: https://github.com/osmcode/pyosmium/compare/v2.5.3...v2.5.4 [2.5.3]: https://github.com/osmcode/pyosmium/compare/v2.4.1...v2.5.3 [2.4.1]: https://github.com/osmcode/pyosmium/compare/v2.3.0...v2.4.1 [2.3.0]: https://github.com/osmcode/pyosmium/compare/v2.2.0...v2.3.0 [2.2.0]: https://github.com/osmcode/pyosmium/compare/v2.1.0...v2.2.0 pyosmium-2.6.0/LICENSE.TXT000066400000000000000000000024171265460313400150740ustar00rootroot00000000000000 Copyright (c) 2014, Sarah Hoffmann All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pyosmium-2.6.0/README.md000066400000000000000000000036261265460313400146730ustar00rootroot00000000000000# pyosmium Provides Python bindings for the [Libosmium](https://github.com/osmcode/libosmium) C++ library, a library for working with OpenStreetMap data in a fast and flexible manner. [![Build Status](https://secure.travis-ci.org/osmcode/pyosmium.png)](http://travis-ci.org/osmcode/pyosmium) ## Dependencies Python >= 2.7 is supported but a version >= 3.3 is strongly recommended. pyosmium uses [Boost.Python](http://www.boost.org/doc/libs/1_56_0/libs/python/doc/index.html) to create the bindings. On Debian/Ubuntu install `libboost-python-dev`. OS X run `brew install boost-python` or `brew install boost-python --with-python3` depending on which python version you want to use – You can also (re)install both. You have to compile with the same compiler version python is compiled with on your system, otherwise it might not work. Libosmium is expected to reside in the same directory as pyosmium or to be installed globally. ## Installation To compile the bindings, run python setup.py build To compile and install the bindings, run python setup.py install ## Examples The `example` directory contains small examples on how to use the library. They are mostly ports of the examples in Libosmium and osmium-contrib. ## Testing There is a small test suite in the test directory. This provides regression test for the python bindings, it is not meant to be a test suite for Libosmium. You'll need the Python `nose` module. On Debian/Ubuntu install the package `python-nose`. The suite can be run with: cd test python run_tests.py ## Documentation To build the documentation you need [Sphinx](http://sphinx-doc.org/). On Debian/Ubuntu install `python-sphinx` or `python3-sphinx`. First compile the bindings as described above and then run: cd doc make html ## License Pyosmium is available under the BSD 2-Clause License. See LICENSE.TXT. ## Authors Sarah Hoffmann (lonvia@denofr.de) pyosmium-2.6.0/doc/000077500000000000000000000000001265460313400141525ustar00rootroot00000000000000pyosmium-2.6.0/doc/.gitignore000066400000000000000000000000071265460313400161370ustar00rootroot00000000000000_build pyosmium-2.6.0/doc/Makefile000066400000000000000000000151621265460313400156170ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Pyosmium.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Pyosmium.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Pyosmium" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Pyosmium" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." pyosmium-2.6.0/doc/conf.py000066400000000000000000000206341265460313400154560ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Pyosmium documentation build configuration file, created by # sphinx-quickstart on Tue Mar 10 18:09:49 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import sysconfig import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) build_dir = "../build/lib.%s-%d.%d" % ( sysconfig.get_platform(), sys.version_info[0], sys.version_info[1]) # insert after the current directory sys.path.insert(0, os.path.normpath(os.path.join(os.path.abspath('.'), build_dir))) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.todo', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Pyosmium' copyright = '2015-2016, Sarah Hoffmann' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '2.6' # The full version, including alpha/beta/rc tags. release = '2.6.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Pyosmiumdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'Pyosmium.tex', 'Pyosmium Documentation', 'Sarah Hoffmann', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'pyosmium', 'Pyosmium Documentation', ['Sarah Hoffmann'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'Pyosmium', 'Pyosmium Documentation', 'Sarah Hoffmann', 'Pyosmium', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False autodoc_member_order = 'bysource' autodoc_defaut_flags = ['members', 'undoc-members'] pyosmium-2.6.0/doc/index.rst000066400000000000000000000010741265460313400160150ustar00rootroot00000000000000.. Pyosmium documentation master file, created by sphinx-quickstart on Tue Mar 10 18:09:49 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to Pyosmium's documentation! ==================================== Pyosmium is a library to process OSM files in different formats. It is a wrapper of the C++ library `osmium `_ and profits from its fast implementation. .. toctree:: :maxdepth: 2 intro reference * :ref:`genindex` * :ref:`search` pyosmium-2.6.0/doc/intro.rst000066400000000000000000000201761265460313400160450ustar00rootroot00000000000000Basic Usage =========== The following chapter gives a practical introduction on how to use Pyosmium. It is assumed that you already have a basic knowledge about the `OSM data model`_. For a more detailed introduction into the design of the osmium library, the reader is referred to the `osmium documentation`_. .. _OSM data model: http://wiki.openstreetmap.org/wiki/Elements .. _osmium documentation: http://osmcode.org/libosmium/manual/libosmium-manual.html Reading OSM Data ---------------- Using Handler Classes ^^^^^^^^^^^^^^^^^^^^^ OSM file parsing by osmium is built around the concept of handlers. A handler is a class with a set of callback functions. Each function processes exactly one type of object as it is read from the file. Let's start with a very simple handler that counts the nodes in the input file:: import osmium class CounterHandler(osmium.SimpleHandler): def __init__(self): osmium.SimpleHandler.__init__(self) self.num_nodes = 0 def node(self, n): self.num_nodes += 1 A handler first of all needs to inherit from one of the handler classes. At the moment this is always :py:class:`osmium.SimpleHandler`. Then it needs to implement functions for each object type it wants to process. In out case it is exactly one function `node()`. All other potential callbacks can be safely ignored. Now the handler needs to be applied to an OSM file. The easiest way to accomplish that is to call the :py:meth:`~osmium.SimpleHandler.apply_file` convenience function, which in its simplest form only requires the file name as a parameter. The main routine of the node counting application therefore looks like this:: if __name__ == '__main__': h = CounterHandler() h.apply_file("test.osm.pbf") print("Number of nodes: %d" % h.num_nodes) That already finishes our node counting program. Inspecting the OSM objects ^^^^^^^^^^^^^^^^^^^^^^^^^^ Counting nodes is actually boring because it completely ignores the content of the nodes. So let's change the handler to only count hotels (normally tagged with ``tourism=hotel``). They may be tagged as nodes, ways or relations, so handler functions for all three types need to be implemented:: import osmium class HotelCounterHandler(osmium.SimpleHandler): def __init__(self): osmium.SimpleHandler.__init__(self) self.num_nodes = 0 def count_hotel(self, tags): if tags['tourism'] == 'hotel': self.num_nodes += 1 def node(self, n): self.count_hotel(n.tags) def way(self, w): self.count_hotel(w.tags) def relation(self, r): self.count_hotel(r.tags) A reference to the object is always given as the only parameter to the handler functions. The objects have some common methods and attributes, listed in :py:class:`osmium.osm.OSMObject`, and some that are specific to each type. As all objects have tags, it is possible to reuse the same implementation for all types. The main function remains the same. It is important to remember that the object references that are handed to the handler are only temporary. They will become invalid as soon as the function returns. Handler functions *must* copy any data that should be kept for later use into their own data structures. This also includes attributes like tag lists. Handling Geometries ^^^^^^^^^^^^^^^^^^^ Because of the way that OSM data is structured, osmium needs to internally cache node geometries, when the handler wants to process the geometries of ways and areas. The :py:meth:`~!osmium.SimpleHandler.apply_file` method cannot deduct by itself, if this cache is needed. Therefore locations need to be explicitly enabled by setting the location parameter to true:: h.apply_file("test.osm.pbf", locations=True, idx='sparse_mem_array') The third parameter `idx` is optional and states what kind of cache osmium is supposed to use. The default `sparse_mem_array` is a good choice for small to medium size extracts of OSM data. If you plan to process the whole planet file, `dense_mmap_array` is better suited. If you want the cache to be persistent across invocations, you can use `dense_file_array` giving an additional file location for the cache like that:: h.apply_file("test.osm.pbf", locations=True, idx='sparse_file_array,example.nodecache') where `example.nodecache` is the name of the cache file. Interfacing with Shapely ^^^^^^^^^^^^^^^^^^^^^^^^ Pyosmium is a library for processing OSM files and therefore offers almost no functionality for processing geometries further. For this other libraries exist. To interface with these libraries you can simply convert the osmium geometries into WKB or WKT format and import the result. The following example uses the libgeos wrapper `Shapely`_ to compute the total way length:: import osmium import shapely.wkb as wkblib # A global factory that creates WKB from a osmium geometry wkbfab = osmium.geom.WKBFactory() class WayLenHandler(osmium.SimpleHandler): def __init__(self): osmium.SimpleHandler.__init__(self) self.total = 0 def way(self, w): wkb = wkbfab.create_linestring(w) line = wkblib.loads(wkb, hex=True) # Length is computed in WGS84 projection, which is practically meaningless. # Lets pretend we didn't notice, it is an example after all. self.total += line.length if __name__ == '__main__': h = WayLenHandler() h.apply_file("test.osm.pbf", locations=True) print("Total length: %f" % h.total) .. _Shapely: http://toblerity.org/shapely/index.html Writing OSM Data ---------------- :py:class:`osmium.SimpleWriter` is the main class that takes care of writing out OSM data to a file. The file name must be given when the writer is constructed. Its suffix determines the format of the data. For example:: writer = osmium.SimpleWriter('nodes.osm.bz2') opens a new writer for a packed OSM XML file. Objects can be written by using one of the writers ``add_*`` functions. A simple handler, that only writes out all the nodes from the input file into out new ``nodes.osm.bz2`` file would look like this:: import osmium class NodeWriter(osmium.SimpleHandler): def __init__(self, writer): osmium.SimpleHandler.__init__(self) self.writer = writer def node(self, n): self.writer.add_node(n) This example shows that an unmodified object can be written out directly to the writer. Normally, however, you want to modify some data. The native osmium OSM types are immutable and cannot be changed directly. Therefore you have create a copy that can be changed. The ``node``, ``way`` and ``relation`` objects offer a convenient ``replace()`` function to achieve exactly that. The function makes a copy and a the same time replaces all attibutes where new values are given as parameters to the function. Let's say you want to remove all the user names from your nodes before saving them to the new file (maybe to save some space), then the ``node()`` handler callback above needs to be changed like that:: class NodeWriter(osmium.SimpleHandler): ... def node(self, n): self.writer.add_node(n.replace(user="")) ``replace()`` creates a new instance of an ``osmium.osm.mutable.`` object. These class a real python versions of the native object types in ``osmium.osm``. They have exactly the same attributes but they are mutable. A writer is able to process the mutable datatypes just like the native osmium types. In fact, a writer is able to process any python object. It just expects suitably named attributes and will simply assume sensible default values for attributes that are missing. .. note:: It is important to understand that ``replace()`` only makes a shallow copy of the object. Tag, node and member lists are still native osmium objects. Normally this is what you want because the writer is much faster writing these native objects than pythonized copies. However, it means that you cannot use ``replace()`` to create a copy of the object that can be kept after the handler callback has finished. pyosmium-2.6.0/doc/ref_geom.rst000066400000000000000000000005241265460313400164700ustar00rootroot00000000000000``geom`` - Geometry Helper Functions ------------------------------------ This module provides various helper functions for geometry handling. Geometry Factories ^^^^^^^^^^^^^^^^^^ .. autoclass:: osmium.geom.WKBFactory :members: :undoc-members: Other Functions ^^^^^^^^^^^^^^^ .. autofunction:: osmium.geom.haversine_distance pyosmium-2.6.0/doc/ref_index.rst000066400000000000000000000013161265460313400166500ustar00rootroot00000000000000``index`` - Data Stores ----------------------- The ``index`` submodule provides efficient storage containers for preprocessed OSM data. Node Location Storage ^^^^^^^^^^^^^^^^^^^^^ Node location can be cached in a ``LocationTable``. There are different implementations available which should be choosen according to the size of data and whether or not the cache should be permanent. See the Osmium manual for a detailed explaination. The compiled in types can be listed with the ``map_types`` function, new storages can be created with ``create_map``. .. autofunction:: osmium.index.map_types .. autofunction:: osmium.index.create_map .. autoclass:: osmium.index.LocationTable :members: :undoc-members: pyosmium-2.6.0/doc/ref_io.rst000066400000000000000000000003561265460313400161530ustar00rootroot00000000000000``io`` - Data In- and Output ---------------------------- This module exposes the generic file reader. .. autoclass:: osmium.io.Reader :members: :undoc-members: .. autoclass:: osmium.io.Header :members: :undoc-members: pyosmium-2.6.0/doc/ref_osm.rst000066400000000000000000000051701265460313400163410ustar00rootroot00000000000000``osm`` - Basic Datatypes ------------------------- The ``osm`` submodule contains definition of the basic data types used throughout the library. Native OSM Objects ^^^^^^^^^^^^^^^^^^ Native OSM object classes are lightwight wrappers around the osmium OSM data classes. They are immutable and generally bound to the life-time of the buffer they are saved in. There are five classes representing the basic OSM entities. .. autoclass:: osmium.osm.OSMObject :members: :undoc-members: .. autoclass:: osmium.osm.Node :members: :undoc-members: .. autoclass:: osmium.osm.Way :members: :undoc-members: .. autoclass:: osmium.osm.Relation :members: :undoc-members: .. autoclass:: osmium.osm.Area :members: :undoc-members: .. autoclass:: osmium.osm.Changeset :members: :undoc-members: .. _mutable-objects: Mutable OSM Objects ^^^^^^^^^^^^^^^^^^^ The objects in ``osmium.osm.mutable`` are Python versions of the native OSM objects that can be modified. You can use these classes as a base class for your own objects or to modify objects read from a file. .. autoclass:: osmium.osm.mutable.OSMObject :members: :undoc-members: .. autoclass:: osmium.osm.mutable.Node :members: :undoc-members: .. autoclass:: osmium.osm.mutable.Way :members: :undoc-members: .. autoclass:: osmium.osm.mutable.Relation :members: :undoc-members: Node Reference Lists ^^^^^^^^^^^^^^^^^^^^ Line geometries in OSM are simply a sequence of nodes. To simplify processing osmium returns such node sequences using a special datatype that contains a reference to the node id and also the location geometry. The latter is only valid if the node locations have been cached by a location handler. .. autoclass:: osmium.osm.NodeRef :members: :undoc-members: .. autoclass:: osmium.osm.NodeRefList :members: :undoc-members: .. autoclass:: osmium.osm.WayNodeList :members: :undoc-members: .. autoclass:: osmium.osm.OuterRing :members: :undoc-members: .. autoclass:: osmium.osm.InnerRing :members: :undoc-members: Other OSM Entity Attributes ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Some of the attributes of the OSM entities are represented with more complex classes. .. autoclass:: osmium.osm.Box :members: :undoc-members: .. autoclass:: osmium.osm.Location :members: :undoc-members: .. autoclass:: osmium.osm.RelationMember :members: :undoc-members: .. autoclass:: osmium.osm.RelationMemberList :members: :undoc-members: .. autoclass:: osmium.osm.Tag :members: :undoc-members: .. autoclass:: osmium.osm.TagList :members: :undoc-members: pyosmium-2.6.0/doc/ref_osmium.rst000066400000000000000000000031511265460313400170510ustar00rootroot00000000000000``osmium`` - Processing OSM files --------------------------------- Osmium processes files by reading data from a file and applying them to a processing chain. Pyosmium offers a simplified wrapper to this interface with the ``SimpleHandler`` class from which an OSM file processor can easily be derived. For more fine grained control of the processing chain, the more basic functions and processors are exported as well in this module. Input Handlers ^^^^^^^^^^^^^^ An input handler implements provides the base class for writing custom data processors. They take input data, usually from a file, and forward it to handler functions. .. autoclass:: osmium.SimpleHandler :members: :undoc-members: SimpleWriter ^^^^^^^^^^^^ The writer class can be used to create an OSM file. The writer is able to handle native ``osmium.osm`` objects as well as any Python object that exposes the same attributes. It is not necessary to implement the full list of attributes as any missing attributes will be replaced with a sensible default value when writing. See :ref:`mutable-objects` for a detailed discussion what data formats are understood for each attribute. .. warning:: Writers are considerably faster in handling native osmium data types than Python objects. You should therefore avoid converting objects whereever possible. This is not only true for the OSM data types like Node, Way and Relation but also for tag lists, node lists and member lists. .. autoclass:: osmium.SimpleWriter :members: :undoc-members: Low-level Functions and Classes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: osmium.apply pyosmium-2.6.0/doc/reference.rst000066400000000000000000000006241265460313400166440ustar00rootroot00000000000000Pyosmium Reference ================== Pyosmium is a thin wrapper to the osmium library. Where possible it follows its structure and naming scheme. This reference provides a short description of the exported classes and interfaces. More details and background information can be found in the osmium manual. .. toctree:: :maxdepth: 2 ref_osmium ref_osm ref_io ref_index ref_geom pyosmium-2.6.0/examples/000077500000000000000000000000001265460313400152235ustar00rootroot00000000000000pyosmium-2.6.0/examples/amenity_list.py000066400000000000000000000021361265460313400203000ustar00rootroot00000000000000""" Extract all objects with an amenity tag from an osm file and list them with their name and position. This example shows how geometries from osmium objects can be imported into shapely using the WKBFactory. """ import osmium as o import sys import shapely.wkb as wkblib wkbfab = o.geom.WKBFactory() class AmenityListHandler(o.SimpleHandler): def print_amenity(amenity, tags, lon, lat): name = tags['name'] if 'name' in tags else '' print("%f %f %-15s %s" % (lon, lat, tags['amenity'], name)) def node(self, n): if 'amenity' in n.tags: self.print_amenity(n.tags, n.location.lon, n.location.lat) def area(self, a): if 'amenity' in a.tags: wkb = wkbfab.create_multipolygon(a) poly = wkblib.loads(wkb, hex=True) centroid = poly.representative_point() self.print_amenity(a.tags, centroid.x, centroid.y) if __name__ == '__main__': if len(sys.argv) != 2: print("Usage: python amenity_list.py ") sys.exit(-1) handler = AmenityListHandler() handler.apply_file(sys.argv[1]) pyosmium-2.6.0/examples/convert.py000066400000000000000000000013071265460313400172560ustar00rootroot00000000000000""" Converts a file from one format to another. This example shows how to write objects to a file. """ import osmium as o import sys class Convert(o.SimpleHandler): def __init__(self, writer): o.SimpleHandler.__init__(self) self.writer = writer def node(self, n): self.writer.add_node(n) def way(self, w): self.writer.add_way(w) def relation(self, r): self.writer.add_relation(r) if __name__ == '__main__': if len(sys.argv) != 3: print("Usage: python convert.py ") sys.exit(-1) writer = o.SimpleWriter(sys.argv[2]) handler = Convert(writer) handler.apply_file(sys.argv[1]) writer.close() pyosmium-2.6.0/examples/create_nodecache.py000066400000000000000000000005151265460313400210320ustar00rootroot00000000000000import osmium as o import sys if len(sys.argv) != 3: print("Usage: python create_nodecache.py ") exit(-1) reader = o.io.Reader(sys.argv[1], o.osm.osm_entity_bits.NODE) idx = o.index.create_map("sparse_file_array," + sys.argv[2]) lh = o.NodeLocationsForWays(idx) o.apply(reader, lh) reader.close() pyosmium-2.6.0/examples/filter_coastlines.py000066400000000000000000000026611265460313400213130ustar00rootroot00000000000000""" Filter all objects with a coastline tag. This example shows how to write objects to a file. We need to go twice over the file. First read the ways, filter the ones we are interested in and remember the nodes required. Then, in a second run all the relevant nodes and ways are written out. """ import osmium as o import sys class WayFilter(o.SimpleHandler): def __init__(self): o.SimpleHandler.__init__(self) self.nodes = set() def way(self, w): if 'natural' in w.tags and w.tags['natural'] == 'coastline': for n in w.nodes: self.nodes.add(n.ref) class CoastlineWriter(o.SimpleHandler): def __init__(self, writer, nodes): o.SimpleHandler.__init__(self) self.writer = writer self.nodes = nodes def node(self, n): if n.id in self.nodes: self.writer.add_node(n) def way(self, w): if 'natural' in w.tags and w.tags['natural'] == 'coastline': self.writer.add_way(w) if __name__ == '__main__': if len(sys.argv) != 3: print("Usage: python filter_coastlines.py ") sys.exit(-1) # go through the ways to find all relevant nodes ways = WayFilter(writer) ways.apply_file(sys.argv[1]) # go through the file again and write out the data writer = o.SimpleWriter(sys.argv[2]) CoastlineWriter(writer, ways.nodes).apply_file(sys.argv[1]) writer.close() pyosmium-2.6.0/examples/normalize_boolean.py000066400000000000000000000036771265460313400213110ustar00rootroot00000000000000""" This example shows how to filter and modify tags and write the rusults back. It changes all tag values 'yes/no' to '1/0'. """ import osmium as o import sys class BoolNormalizer(o.SimpleHandler): def __init__(self, writer): o.SimpleHandler.__init__(self) self.writer = writer def normalize(self, o): # if there are no tags we are done if len(o.tags) == 0: return o # new tags should be kept in a list so that the order is preserved newtags = [] # pyosmium is much faster writing an original osmium object than # a osmium.mutable.*. Therefore, keep track if the tags list was # actually changed. modified = False for t in o.tags: if t.v == 'yes': # custom tags should be added as a key/value tuple newtags.append((t.k, '1')) modified = True elif t.v == 'no': newtags.append((t.k, '0')) modified = True else: # if the tag is not modified, simply readd it to the list newtags.append(t) if modified: # We have changed tags. Create a new object as a copy of the # original one with the tag list replaced. return o.replace(tags=newtags) else: # Nothing changed, so simply return the original object # and discard the tag list we just created. return o def node(self, o): self.writer.add_node(self.normalize(o)) def way(self, o): self.writer.add_way(self.normalize(o)) def relation(self, o): self.writer.add_relation(self.normalize(o)) if __name__ == '__main__': if len(sys.argv) != 3: print("Usage: python normalize_boolean.py ") sys.exit(-1) writer = o.SimpleWriter(sys.argv[2]) BoolNormalizer(writer).apply_file(sys.argv[1]) writer.close() pyosmium-2.6.0/examples/osm_diff_stats.py000066400000000000000000000024171265460313400206050ustar00rootroot00000000000000""" Simple example that counts the number of changes in an osm diff file. Shows how to detect the different kind of modifications. """ import osmium as o import sys class Stats(object): def __init__(self): self.added = 0 self.modified = 0 self.deleted = 0 def add(self, o): if o.deleted: self.deleted += 1 elif o.version == 1: self.added += 1 else: self.modified += 1 def outstats(self, prefix): print("%s added: %d" % (prefix, self.added)) print("%s modified: %d" % (prefix, self.modified)) print("%s deleted: %d" % (prefix, self.deleted)) class FileStatsHandler(o.SimpleHandler): def __init__(self): o.SimpleHandler.__init__(self) self.nodes = Stats() self.ways = Stats() self.rels = Stats() def node(self, n): self.nodes.add(n) def way(self, w): self.ways.add(w) def relation(self, r): self.rels.add(r) if __name__ == '__main__': if len(sys.argv) != 2: print("Usage: python osm_file_stats.py ") sys.exit(-1) h = FileStatsHandler() h.apply_file(sys.argv[1]) h.nodes.outstats("Nodes") h.ways.outstats("Ways") h.rels.outstats("Relations") pyosmium-2.6.0/examples/osm_file_stats.py000066400000000000000000000014161265460313400206120ustar00rootroot00000000000000""" Simple example that counts the number of objects in an osm file. Shows how to write a handler for the different types of objects. """ import osmium as o import sys class FileStatsHandler(o.SimpleHandler): def __init__(self): o.SimpleHandler.__init__(self) self.nodes = 0 self.ways = 0 self.rels = 0 def node(self, n): self.nodes += 1 def way(self, w): self.ways += 1 def relation(self, r): self.rels += 1 if __name__ == '__main__': if len(sys.argv) != 2: print("Usage: python osm_file_stats.py ") sys.exit(-1) h = FileStatsHandler() h.apply_file(sys.argv[1]) print("Nodes: %d" % h.nodes) print("Ways: %d" % h.ways) print("Relations: %d" % h.rels) pyosmium-2.6.0/examples/pub_names.py000066400000000000000000000011131265460313400175420ustar00rootroot00000000000000""" Search for pubs in an osm file and list their names. """ import osmium import sys class NamesHandler(osmium.SimpleHandler): def output_pubs(self, tags): if 'amenity' in tags and tags['amenity'] == 'pub': if 'name' in tags: print(tags['name']) def node(self, n): self.output_pubs(n.tags) def way(self, w): self.output_pubs(w.tags) if __name__ == '__main__': if len(sys.argv) != 2: print("Usage: python pub_names.py ") sys.exit(-1) h = NamesHandler() h.apply_file(sys.argv[1]) pyosmium-2.6.0/examples/road_length.py000066400000000000000000000020151265460313400200610ustar00rootroot00000000000000""" Compute the total length of highways in an osm file. Shows how extract the geometry of a way. """ import osmium as o import sys class RoadLengthHandler(o.SimpleHandler): def __init__(self): o.SimpleHandler.__init__(self) self.length = 0.0 def way(self, w): if 'highway' in w.tags: try: self.length += o.geom.haversine_distance(w.nodes) except o.InvalidLocationError: # A location error might occur if the osm file is an extract # where nodes of ways near the boundary are missing. print("WARNING: way %d incomplete. Ignoring." % w.id) if __name__ == '__main__': if len(sys.argv) != 2: print("Usage: python road_length.py ") sys.exit(-1) h = RoadLengthHandler() # As we need the geometry, the node locations need to be cached. Therefore # set 'locations' to true. h.apply_file(sys.argv[1], locations=True) print('Total way length: %.2f km' % (h.length/1000)) pyosmium-2.6.0/examples/use_nodecache.py000066400000000000000000000012731265460313400203650ustar00rootroot00000000000000import osmium as o import sys class WayHandler(o.SimpleHandler): def __init__(self, idx): o.SimpleHandler.__init__(self) self.idx = idx def way(self, w): for n in w.nodes: n.lat, n.lon # throws an exception if the coordinates are missing loc = idx.get(n.ref) print("%d %s" %(w.id, len(w.nodes))) if len(sys.argv) != 3: print("Usage: python create_nodecache.py ") exit() reader = o.io.Reader(sys.argv[1], o.osm.osm_entity_bits.WAY) idx = o.index.create_map("sparse_file_array," + sys.argv[2]) lh = o.NodeLocationsForWays(idx) lh.ignore_errors() o.apply(reader, lh, WayHandler(idx)) reader.close() pyosmium-2.6.0/lib/000077500000000000000000000000001265460313400141535ustar00rootroot00000000000000pyosmium-2.6.0/lib/generic_handler.hpp000066400000000000000000000124701265460313400200010ustar00rootroot00000000000000#ifndef PYOSMIUM_GENERIC_HANDLER_HPP #define PYOSMIUM_GENERIC_HANDLER_HPP #include #include #include #include #include #include #include #include typedef osmium::index::map::Map index_type; class BaseHandler : public osmium::handler::Handler { protected: enum pre_handler { no_handler, location_handler, area_handler }; public: // handler functions virtual void node(const osmium::Node&) const = 0; virtual void way(const osmium::Way&) const = 0; virtual void relation(const osmium::Relation&) const = 0; virtual void changeset(const osmium::Changeset&) const = 0; virtual void area(const osmium::Area&) const = 0; private: void apply_with_location(osmium::io::Reader &r, const std::string &idx) { const auto& map_factory = osmium::index::MapFactory::instance(); std::unique_ptr index = map_factory.create_map(idx); osmium::handler::NodeLocationsForWays location_handler(*index); location_handler.ignore_errors(); osmium::apply(r, location_handler, *this); } void apply_with_area(osmium::io::Reader &r, osmium::area::MultipolygonCollector &collector, const std::string &idx) { const auto& map_factory = osmium::index::MapFactory::instance(); std::unique_ptr index = map_factory.create_map(idx); osmium::handler::NodeLocationsForWays location_handler(*index); location_handler.ignore_errors(); osmium::apply(r, location_handler, *this, collector.handler([this](const osmium::memory::Buffer& area_buffer) { osmium::apply(area_buffer, *this); }) ); } protected: void apply(const std::string &filename, osmium::osm_entity_bits::type types, pre_handler pre = no_handler, const std::string &idx = "sparse_mem_array") { switch (pre) { case no_handler: { osmium::io::Reader reader(filename, types); osmium::apply(reader, *this); reader.close(); break; } case location_handler: { osmium::io::Reader reader(filename, types); apply_with_location(reader, idx); reader.close(); break; } case area_handler: { osmium::area::Assembler::config_type assembler_config; osmium::area::MultipolygonCollector collector(assembler_config); osmium::io::Reader reader1(filename); collector.read_relations(reader1); reader1.close(); osmium::io::Reader reader2(filename); apply_with_area(reader2, collector, idx); reader2.close(); break; } } } }; using namespace boost::python; struct SimpleHandlerWrap: BaseHandler, wrapper { void node(const osmium::Node& node) const { if (override f = this->get_override("node")) f(boost::ref(node)); } void default_node(const osmium::Node&) const { } void way(const osmium::Way& way) const { if (override f = this->get_override("way")) f(boost::ref(way)); } void default_way(const osmium::Way&) const { } void relation(const osmium::Relation& rel) const { if (override f = this->get_override("relation")) f(boost::ref(rel)); } void default_relation(const osmium::Relation&) const { } void changeset(const osmium::Changeset& cs) const { if (override f = this->get_override("changeset")) f(boost::ref(cs)); } void default_changeset(const osmium::Changeset&) const { } void area(const osmium::Area& area) const { if (override f = this->get_override("area")) f(boost::ref(area)); } void default_area(const osmium::Area&) const { } void apply_file(const std::string &filename, bool locations = false, const std::string &idx = "sparse_mem_array") { osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::nothing; BaseHandler::pre_handler handler = locations? BaseHandler::location_handler :BaseHandler::no_handler; if (this->get_override("area")) { entities = osmium::osm_entity_bits::object; handler = BaseHandler::area_handler; } else { if (locations || this->get_override("node")) entities |= osmium::osm_entity_bits::node; if (this->get_override("way")) entities |= osmium::osm_entity_bits::way; if (this->get_override("relation")) entities |= osmium::osm_entity_bits::relation; } if (this->get_override("changeset")) entities |= osmium::osm_entity_bits::changeset; apply(filename, entities, handler, idx); } }; #endif pyosmium-2.6.0/lib/generic_writer.hpp000066400000000000000000000215351265460313400177020ustar00rootroot00000000000000#ifndef PYOSMIUM_GENERIC_WRITER_HPP #define PYOSMIUM_GENERIC_WRITER_HPP #include #include #include #include #include #include class SimpleWriterWrap { enum { BUFFER_WRAP = 4096 }; public: SimpleWriterWrap(const char* filename, size_t bufsz=4096*1024) : writer(filename), buffer(bufsz < 2*BUFFER_WRAP ? 2*BUFFER_WRAP : bufsz, osmium::memory::Buffer::auto_grow::yes) {} virtual ~SimpleWriterWrap() { close(); } void add_osmium_object(const osmium::OSMObject& o) { buffer.add_item(o); flush_buffer(); } void add_node(boost::python::object o) { boost::python::extract node(o); if (node.check()) { buffer.add_item(node()); } else { osmium::builder::NodeBuilder builder(buffer); if (hasattr(o, "location")) { osmium::Node& n = builder.object(); n.set_location(get_location(o.attr("location"))); } set_common_attributes(o, builder); if (hasattr(o, "tags")) set_taglist(o.attr("tags"), builder); } flush_buffer(); } void add_way(const boost::python::object& o) { boost::python::extract way(o); if (way.check()) { buffer.add_item(way()); } else { osmium::builder::WayBuilder builder(buffer); set_common_attributes(o, builder); if (hasattr(o, "nodes")) set_nodelist(o.attr("nodes"), &builder); if (hasattr(o, "tags")) set_taglist(o.attr("tags"), builder); } flush_buffer(); } void add_relation(boost::python::object o) { boost::python::extract rel(o); if (rel.check()) { buffer.add_item(rel()); } else { osmium::builder::RelationBuilder builder(buffer); set_common_attributes(o, builder); if (hasattr(o, "members")) set_memberlist(o.attr("members"), &builder); if (hasattr(o, "tags")) set_taglist(o.attr("tags"), builder); } flush_buffer(); } void close() { if (buffer) { writer(std::move(buffer)); writer.close(); buffer = osmium::memory::Buffer(); } } private: void set_object_attributes(const boost::python::object& o, osmium::OSMObject& t) { if (hasattr(o, "id")) t.set_id(boost::python::extract(o.attr("id"))); if (hasattr(o, "visible")) t.set_visible(boost::python::extract(o.attr("visible"))); if (hasattr(o, "version")) t.set_version(boost::python::extract(o.attr("version"))); if (hasattr(o, "changeset")) t.set_changeset(boost::python::extract(o.attr("changeset"))); if (hasattr(o, "uid")) t.set_uid_from_signed(boost::python::extract(o.attr("uid"))); if (hasattr(o, "timestamp")) { boost::python::object ts = o.attr("timestamp"); boost::python::extract ots(ts); if (ots.check()) { t.set_timestamp(ots()); } else { if (hasattr(ts, "timestamp")) { double epoch = boost::python::extract(ts.attr("timestamp")()); t.set_timestamp(osmium::Timestamp(uint32_t(epoch))); } else { // XXX terribly inefficient because of the double string conversion // but the only painless method for converting a datetime // in python < 3.3. if (hasattr(ts, "strftime")) ts = ts.attr("strftime")("%Y-%m-%dT%H:%M:%SZ"); t.set_timestamp(osmium::Timestamp(boost::python::extract(ts))); } } } } template void set_common_attributes(const boost::python::object& o, T& builder) { set_object_attributes(o, builder.object()); if (hasattr(o, "user")) { auto s = boost::python::extract(o.attr("user")); builder.add_user(s); } else { builder.add_user("", 0); } } template void set_taglist(const boost::python::object& o, T& obuilder) { // original taglist boost::python::extract otl(o); if (otl.check()) { if (otl().size() > 0) obuilder.add_item(&otl()); return; } // dict boost::python::extract tagdict(o); if (tagdict.check()) { auto items = tagdict().items(); auto len = boost::python::len(items); if (len == 0) return; osmium::builder::TagListBuilder builder(buffer, &obuilder); auto iter = items.attr("__iter__")(); for (int i = 0; i < len; ++i) { #if PY_VERSION_HEX < 0x03000000 auto tag = iter.attr("next")(); #else auto tag = iter.attr("__next__")(); #endif builder.add_tag(boost::python::extract(tag[0]), boost::python::extract(tag[1])); } return; } // any other iterable auto l = boost::python::len(o); if (l == 0) return; osmium::builder::TagListBuilder builder(buffer, &obuilder); for (int i = 0; i < l; ++i) { auto tag = o[i]; boost::python::extract ot(tag); if (ot.check()) { builder.add_tag(ot); } else { builder.add_tag(boost::python::extract(tag[0]), boost::python::extract(tag[1])); } } } void set_nodelist(const boost::python::object& o, osmium::builder::WayBuilder *builder) { // original nodelist boost::python::extract onl(o); if (onl.check()) { if (onl().size() > 0) builder->add_item(&onl()); return; } auto len = boost::python::len(o); if (len == 0) return; osmium::builder::WayNodeListBuilder wnl_builder(buffer, builder); for (int i = 0; i < len; ++i) { boost::python::extract ref(o[i]); if (ref.check()) wnl_builder.add_node_ref(ref()); else wnl_builder.add_node_ref(boost::python::extract(o[i])); } } void set_memberlist(const boost::python::object& o, osmium::builder::RelationBuilder *builder) { // original nodelist boost::python::extract oml(o); if (oml.check()) { if (oml().size() > 0) builder->add_item(&oml()); return; } auto len = boost::python::len(o); if (len == 0) return; osmium::builder::RelationMemberListBuilder rml_builder(buffer, builder); for (int i = 0; i < len; ++i) { auto member = o[i]; auto type = osmium::char_to_item_type(boost::python::extract(member[0])()[0]); auto id = boost::python::extract(member[1])(); auto role = boost::python::extract(member[2])(); rml_builder.add_member(type, id, role); } } osmium::Location get_location(const boost::python::object& o) { boost::python::extract ol(o); if (ol.check()) return ol; // default is a tuple with two floats return osmium::Location(boost::python::extract(o[0]), boost::python::extract(o[1])); } bool hasattr(const boost::python::object& obj, char const *attr) { return PyObject_HasAttrString(obj.ptr(), attr) && (obj.attr(attr) != boost::python::object()); } void flush_buffer() { buffer.commit(); if (buffer.committed() > buffer.capacity() - BUFFER_WRAP) { osmium::memory::Buffer new_buffer(buffer.capacity(), osmium::memory::Buffer::auto_grow::yes); using std::swap; swap(buffer, new_buffer); writer(std::move(new_buffer)); } } osmium::io::Writer writer; osmium::memory::Buffer buffer; }; #endif // PYOSMIUM_GENERIC_WRITER_HPP pyosmium-2.6.0/lib/geom.cc000066400000000000000000000063701265460313400154170ustar00rootroot00000000000000#include #include #include #include class WKBFactory : public osmium::geom::WKBFactory<> { public: WKBFactory() : osmium::geom::WKBFactory<>(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex) {} }; BOOST_PYTHON_MODULE(geom) { using namespace boost::python; docstring_options doc_options(true, true, false); enum_("use_nodes") .value("UNIQUE", osmium::geom::use_nodes::unique) .value("ALL", osmium::geom::use_nodes::all) ; enum_("direction") .value("BACKWARD", osmium::geom::direction::backward) .value("FORWARD", osmium::geom::direction::forward) ; def("haversine_distance", static_cast(&osmium::geom::haversine::distance), arg("list"), "Compute the distance using the Haversine algorithm which takes the " "curvature of earth into account. If a :py:class:`WayNodeList` is given " "as a parameter the total length of the way in meters is computed."); class_("WKBFactory", "Factory that creates WKB from osmium geometries.") .add_property("epsg", &WKBFactory::epsg, "(read-only) EPSG number of the output geometry.") .add_property("proj_string", &WKBFactory::proj_string, "(read-only) projection string of the output geometry.") .def("create_point", static_cast(&WKBFactory::create_point), (arg("self"), arg("location")), "Create a point geometry from a :py:class:`osmium.osm.Location`.") .def("create_point", static_cast(&WKBFactory::create_point), (arg("self"), arg("node")), "Create a point geometry from a :py:class:`osmium.osm.Node`.") .def("create_point", static_cast(&WKBFactory::create_point), (arg("self"), arg("ref")), "Create a point geometry from a :py:class:`osmium.osm.NodeRef`.") .def("create_linestring", static_cast(&WKBFactory::create_linestring), (arg("self"), arg("list"), arg("use_nodes")=osmium::geom::use_nodes::unique, arg("direction")=osmium::geom::direction::forward), "Create a LineString geometry from a :py:class:`osmium.osm.WayNodeList`.") .def("create_linestring", static_cast(&WKBFactory::create_linestring), (arg("self"), arg("way"), arg("use_nodes")=osmium::geom::use_nodes::unique, arg("direction")=osmium::geom::direction::forward), "Create a LineString geometry from a :py:class:`osmium.osm.Way`.") .def("create_multipolygon", &WKBFactory::create_multipolygon, (arg("self"), arg("area")), "Create a MultiPolygon geometry from a :py:class:`osmium.osm.Area`.") ; } pyosmium-2.6.0/lib/index.cc000066400000000000000000000040021265460313400155650ustar00rootroot00000000000000#include #include #include using namespace boost::python; typedef osmium::index::map::Map LocationTable; LocationTable *create_map(const std::string& config_string) { const auto& map_factory = osmium::index::MapFactory::instance(); return map_factory.create_map(config_string).release(); } std::vector map_types() { const auto& map_factory = osmium::index::MapFactory::instance(); return map_factory.map_types(); } BOOST_PYTHON_MODULE(index) { docstring_options doc_options(true, true, false); class_("LocationTable", "A map from a node ID to a location object. This implementation works " "only with positive node IDs.", no_init) .def("set", &LocationTable::set, (arg("self"), arg("id"), arg("loc")), "Set the location for a given node id.") .def("get", &LocationTable::get, (arg("self"), arg("id")), "Return the location for a given id.") .def("used_memory", &LocationTable::used_memory, arg("self"), "Return the size (in bytes) currently allocated by this location table.") .def("clear", &LocationTable::clear, arg("self"), "Remove all entries from the location table.") ; def("create_map", &create_map, return_value_policy(), (arg("map_type")), "Create a new location store. The string parameter takes the type " "and, where required, additional arguments separated by comma. For " "example, to create a array cache backed by a file ``foo.store``, " "the map_type should be ``dense_file_array,foo.store``."); def("map_types", &map_types, "Return a list of strings with valid types for the location table."); } pyosmium-2.6.0/lib/io.cc000066400000000000000000000026011265460313400150700ustar00rootroot00000000000000#include #include #include "osm.cc" BOOST_PYTHON_MODULE(io) { using namespace boost::python; docstring_options doc_options(true, true, false); class_("Header", "File header with global information about the file.") .add_property("has_multiple_object_versions", &osmium::io::Header::has_multiple_object_versions, make_function(&osmium::io::Header::set_has_multiple_object_versions, return_value_policy()), "True if there may be more than one version of the same " "object in the file. This happens normally only in history " "files.") ; class_("Reader", "A class that reads OSM data from a file.", init()) .def(init()) .def("eof", &osmium::io::Reader::eof, arg("self"), "Check if the end of file has been reached.") .def("close", &osmium::io::Reader::close, arg("self"), "Close any open file handles. The reader is unusable afterwards.") .def("header", &osmium::io::Reader::header, arg("self"), "Return the header with file information, see :py:class:`osmium.io.Header`.") ; } pyosmium-2.6.0/lib/osm.cc000066400000000000000000000446521265460313400152730ustar00rootroot00000000000000 #include #include #include #include #include #include "std_pair.hpp" inline const char *get_tag_by_key(osmium::TagList const& obj, const char *value) { const char* v = obj.get_value_by_key(value); if (!v) PyErr_SetString(PyExc_KeyError, "No tag with that key."); return v; } inline bool taglist_contains_tag(osmium::TagList const& obj, const char *value) { const char* v = obj.get_value_by_key(value); return v; } inline const char member_item_type(osmium::RelationMember& obj) { return item_type_to_char(obj.type()); } // Converter for osmium::Timestamp -> datetime.datetime struct Timestamp_to_python { static PyObject* convert(osmium::Timestamp const& s) { #if PY_VERSION_HEX >= 0x03000000 static auto fconv = boost::python::import("datetime").attr("datetime").attr("fromtimestamp"); static boost::python::object utc = boost::python::import("datetime").attr("timezone").attr("utc"); return boost::python::incref(fconv(s.seconds_since_epoch(), utc).ptr()); #else static auto fconv = boost::python::import("datetime").attr("datetime").attr("utcfromtimestamp"); return boost::python::incref(fconv(s.seconds_since_epoch()).ptr()); #endif } }; BOOST_PYTHON_MODULE(_osm) { using namespace boost::python; docstring_options doc_options(true, true, false); to_python_converter(); std_pair_to_python_converter(); enum_("osm_entity_bits") .value("NOTHING", osmium::osm_entity_bits::nothing) .value("NODE", osmium::osm_entity_bits::node) .value("WAY", osmium::osm_entity_bits::way) .value("RELATION", osmium::osm_entity_bits::relation) .value("AREA", osmium::osm_entity_bits::area) .value("OBJECT", osmium::osm_entity_bits::object) .value("CHANGESET", osmium::osm_entity_bits::changeset) .value("ALL", osmium::osm_entity_bits::all) ; class_("Location", "A geographic coordinate in WGS84 projection. A location doesn't " "have to be necessarily valid.") .def(init()) .add_property("x", &osmium::Location::x, "(read-only) X coordinate (longitude) as a fixed-point integer.") .add_property("y", &osmium::Location::y, "(read-only) Y coordinate (latitude) as a fixed-point integer.") .add_property("lon", &osmium::Location::lon, "(read-only) Longitude (x coordinate) as floating point number.") .add_property("lat", &osmium::Location::lat, "(read-only) Latitude (y coordinate) as floating point number.") .def("valid", &osmium::Location::valid, args("self"), "Check that the location is a valid WGS84 coordinate, i.e. " "that it is within the usual bounds.") ; class_("Box", "A bounding box around a geographic area. Such a box consists of two " ":py:class:`osmium.osm.Location`s. Those locations may be invalid in " "which case the box is considered invalid, too.") .def(init()) .def(init()) .add_property("bottom_left", make_function(static_cast(&osmium::Box::bottom_left), return_value_policy()), "(read-only) Bottom-left corner of the bounding box.") .add_property("top_right", make_function(static_cast(&osmium::Box::top_right), return_value_policy()), "(read-only) Top-right corner of the bounding box.") .def("extend", make_function(static_cast(&osmium::Box::extend), return_value_policy()), //(arg("self"), arg("location")), "Extend the box to include the given location. If the location " "is invalid the box remains unchanged. If the box is invalid, it " "will contain only the location after the operation.") .def("extend", make_function(static_cast(&osmium::Box::extend), return_value_policy()), //(arg("self"), arg("box")), "Extend the box to include the given box. If the box to be added " "is invalid the input box remains unchanged. If the input box is invalid, it " "will become equal to the box that was added.") .def("valid", &osmium::Box::valid, args("self"), "Check if the box coordinates are defined and with the usual bounds.") .def("size", &osmium::Box::size, args("self"), "Return the size in square degrees.") .def("contains", &osmium::Box::contains, (arg("self"), arg("location")), "Check if the given location is inside the box.") ; class_("Tag", "A single OSM tag.", no_init) .add_property("k", &osmium::Tag::key, "(read-only) Tag key.") .add_property("v", &osmium::Tag::value, "(read-only) Tag value.") ; class_("TagList", "A fixed list of tags. The list is exported as an unmutable, " "dictionary-like object where the keys are tag strings and the " "items are :py:class:`osmium.osm.Tag`.", no_init) .def("__len__", &osmium::TagList::size) .def("__getitem__", &get_tag_by_key) .def("__contains__", &taglist_contains_tag) .def("__iter__", iterator>()) ; class_("NodeRef", "A reference to a OSM node that also caches the nodes location.") .add_property("x", &osmium::NodeRef::x, "(read-only) X coordinate (longitude) as a fixed-point integer.") .add_property("y", &osmium::NodeRef::y, "(read-only) Y coordinate (latitude) as a fixed-point integer.") .add_property("lon", &osmium::NodeRef::lon, "(read-only) Longitude (x coordinate) as floating point number.") .add_property("lat", &osmium::NodeRef::lat, "(read-only) Latitude (y coordinate) as floating point number.") .add_property("ref", &osmium::NodeRef::ref, "(read-only) Id of the referenced node.") .add_property("location", static_cast(&osmium::NodeRef::location), "(read-only) Node coordinates as a :py:class:`osmium.osm.Location` object.") ; class_("RelationMember", "Member of a relation.", no_init) .add_property("ref", static_cast(&osmium::RelationMember::ref), "OSM ID of the object. Only unique within the type.") .add_property("type", &member_item_type, "Type of object referenced, a node, way or relation.") .add_property("role", &osmium::RelationMember::role, "The role of the member within the relation, a free-text string. " "If no role is set then the string is empty.") ; class_("RelationMemberList", "An immutable sequence of relation members :py:class:`osmium.osm.RelationMember`.", no_init) .def("__len__", &osmium::RelationMemberList::size) .def("__iter__", iterator>()) ; class_("NodeRefList", "A list of node references, implemented as " "an immutable sequence of :py:class:`osmium.osm.NodeRef`. This class " "is normally not used directly, use one of its subclasses instead.", no_init) .def("__len__", &osmium::NodeRefList::size) .def("__getitem__", &osmium::NodeRefList::operator[], return_value_policy()) .def("__iter__", iterator>()) .def("is_closed", &osmium::NodeRefList::is_closed, args("self"), "True if the start and end node are the same (synonym for " "``ends_have_same_id``).") .def("ends_have_same_id", &osmium::NodeRefList::ends_have_same_id, args("self"), "True if the start and end node are exactly the same.") .def("ends_have_same_location", &osmium::NodeRefList::ends_have_same_location, args("self"), "True if the start and end node of the way are at the same location. " "Throws an exception if the location of one of the nodes is missing.") ; class_, boost::noncopyable>("WayNodeList", "List of nodes in a way. For its members see :py:class:`osmium.osm.NodeRefList`.", no_init) ; class_, boost::noncopyable>("OuterRing", "List of nodes in an outer ring. For its members see :py:class:`osmium.osm.NodeRefList`.", no_init) ; class_, boost::noncopyable>("InnerRing", "List of nodes in an inner ring. For its members see :py:class:`osmium.osm.NodeRefList`.", no_init) ; class_("OSMObject", "This is the base class for all OSM entity classes below and contains " "all common attributes.", no_init) .add_property("id", &osmium::OSMObject::id, "(read-only) OSM id of the object.") .add_property("deleted", &osmium::OSMObject::deleted, "(read-only) True if the object is no longer visible.") .add_property("visible", &osmium::OSMObject::visible, "(read-only) True if the object is visible.") .add_property("version", &osmium::OSMObject::version, "(read-only) Version number of the object.") .add_property("changeset", &osmium::OSMObject::changeset, "(read-only) Id of changeset where this version of the " "object was created.") .add_property("uid", &osmium::OSMObject::uid, "(read-only) Id of the user that created this version " "of the object. Only this ID uniquely identifies users.") .add_property("timestamp", &osmium::OSMObject::timestamp, "(read-only) Date when this version has been created, " "returned as a ``datetime.datetime``.") .add_property("user", &osmium::OSMObject::user, "(read-only) Name of the user that created this version. " "Be aware that user names can change, so that the same " "user ID may appear with different names and vice versa. ") .add_property("tags", make_function(&osmium::OSMObject::tags, return_value_policy()), "(read-only) List of tags describing the object. " "See :py:class:`osmium.osm.TagList`.") .def("positive_id", &osmium::OSMObject::positive_id, arg("self"), "Get the absolute value of the id of this object.") .def("user_is_anonymous", &osmium::OSMObject::user_is_anonymous, arg("self"), "Check if the user anonymous. If true, the uid does not uniquely " "identify a single user but only the group of all anonymous users " "in general.") ; class_, boost::noncopyable>("Node", "Represents a single OSM node. It inherits from OSMObjects and " "adds a single attribute, the location.", no_init) .add_property("location", static_cast(&osmium::Node::location), "The geographic coordinates of the node. " "See :py:class:`osmium.osm.Location`.") ; class_, boost::noncopyable>("Way", "Represents a OSM way. It inherits the attributes from OSMObjects and " "adds an ordered list of nodes that describes the way.", no_init) .add_property("nodes", make_function(static_cast(&osmium::Way::nodes), return_value_policy()), "(read-only) Ordered list of nodes. See :py:class:`osmium.osm.WayNodeList`.") .def("is_closed", &osmium::Way::is_closed, args("self"), "True if the start and end node are the same (synonym for " "``ends_have_same_id``).") .def("ends_have_same_id", &osmium::Way::ends_have_same_id, args("self"), "True if the start and end node are exactly the same.") .def("ends_have_same_location", &osmium::Way::ends_have_same_location, args("self"), "True if the start and end node of the way are at the same location." "Throws an exception if the location of one of the nodes is missing.") ; class_, boost::noncopyable>("Relation", "Represents a OSM relation. It inherits the attributes from OSMObjects " "and adds an ordered list of members.", no_init) .add_property("members", make_function(static_cast(&osmium::Relation::members), return_value_policy()), "(read-only) Ordered list of relation members. " "See :py:class:`osmium.osm.RelationMemberList`") ; class_, boost::noncopyable>("Area", "Areas are a special kind of meta-object representing a polygon. " "They can either be derived from closed ways or from relations " "that represent multipolygons. They also inherit the attributes " "of OSMObjects and in addition contain polygon geometries. Areas have " "their own unique id space. This is computed as the OSM id times 2 " "and for relations 1 is added,", no_init) .def("from_way", &osmium::Area::from_way, args("self"), "Return true if the area was created from a way, false if it was " "created from a relation of multipolygon type.") .def("orig_id", &osmium::Area::orig_id, args("self"), "Compute the original OSM id of this object. Note that this is not " "necessarily unique because the object might be a way or relation " "which have an overlapping id space.") .def("is_multipolygon", &osmium::Area::is_multipolygon, args("self"), "Return true if this area is a true multipolygon, i.e. it consists " "of multiple outer rings.") .def("num_rings", &osmium::Area::num_rings, args("self"), "Return a tuple with the number of outer rings and inner rings.") .def("outer_rings", range >( &osmium::Area::cbegin, &osmium::Area::cend), "Return an iterator over all outer rings of the multipolygon.") .def("inner_rings", range >( &osmium::Area::cbegin, &osmium::Area::cend), "Return an iterator over all inner rings of the multipolygon.") ; class_("Changeset", "A changeset description.", no_init) .add_property("id", &osmium::Changeset::id, "(read-only) Unique ID of the changeset.") .add_property("uid", &osmium::Changeset::uid, "(read-only) User ID of the changeset creator.") .add_property("created_at", &osmium::Changeset::created_at, "(read-only) Timestamp when the changeset was first opened.") .add_property("closed_at", &osmium::Changeset::closed_at, "(read-only) Timestamp when the changeset was finalized. May be " "None when the changeset is still open/") .add_property("open", &osmium::Changeset::open, "(read-only) True when the changeset is still open.") .add_property("num_changes", &osmium::Changeset::num_changes, "(read-only) The total number of objects changed in this " "Changeset.") .add_property("bounds", make_function(static_cast(&osmium::Changeset::bounds), return_value_policy()), "(read-only) The bounding box of the area that was edited.") .add_property("user", &osmium::Changeset::user, "(read-only) Name of the user that created the changeset. " "Be aware that user names can change, so that the same " "user ID may appear with different names and vice versa. ") .add_property("tags", make_function(&osmium::Changeset::tags, return_value_policy()), "(read-only) List of tags describing the changeset. " "See :py:class:`osmium.osm.TagList`.") .def("user_is_anonymous", &osmium::Changeset::user_is_anonymous, arg("self"), "Check if the user anonymous. If true, the uid does not uniquely " "identify a single user but only the group of all anonymous users " "in general.") ; } pyosmium-2.6.0/lib/osmium.cc000066400000000000000000000151461265460313400160020ustar00rootroot00000000000000#include #include #include #include #include #include "generic_writer.hpp" #include "generic_handler.hpp" template void apply_reader_simple(osmium::io::Reader &rd, T &h) { osmium::apply(rd, h); } template void apply_reader_simple_with_location(osmium::io::Reader &rd, osmium::handler::NodeLocationsForWays &l, BaseHandler &h) { osmium::apply(rd, l, h); } PyObject *invalidLocationExceptionType = NULL; PyObject *notFoundExceptionType = NULL; void translator1(osmium::invalid_location const&) { PyErr_SetString(invalidLocationExceptionType, "Invalid location"); } void translator2(osmium::not_found const&) { PyErr_SetString(notFoundExceptionType, "Element not found in index"); } PyObject* createExceptionClass(const char* name, PyObject* baseTypeObj = PyExc_Exception) { using std::string; namespace bp = boost::python; string scopeName = bp::extract(bp::scope().attr("__name__")); string qualifiedName0 = scopeName + "." + name; char* qualifiedName1 = const_cast(qualifiedName0.c_str()); PyObject* typeObj = PyErr_NewException(qualifiedName1, baseTypeObj, 0); if(!typeObj) bp::throw_error_already_set(); bp::scope().attr(name) = bp::handle<>(bp::borrowed(typeObj)); return typeObj; } #include "index.cc" BOOST_PYTHON_MODULE(_osmium) { using namespace boost::python; docstring_options doc_options(true, true, false); invalidLocationExceptionType = createExceptionClass("InvalidLocationError", PyExc_RuntimeError); register_exception_translator(&translator1); notFoundExceptionType = createExceptionClass("NotFoundError", PyExc_KeyError); register_exception_translator(&translator2); class_, boost::noncopyable>("NodeLocationsForWays", init()) .def("ignore_errors", &osmium::handler::NodeLocationsForWays::ignore_errors) ; class_("SimpleHandler", "The most generic of OSM data handlers. For each data type " "a callback can be implemented where the object is processed. Note that " "all objects that are handed into the handler are only readable and are " "only valid until the end of the callback is reached. Any data that " "should be retained must be copied into other data structures.") .def("node", &BaseHandler::node, &SimpleHandlerWrap::default_node, (arg("self"), arg("node")), "Handler called for node objects.") .def("way", &BaseHandler::way, &SimpleHandlerWrap::default_way, (arg("self"), arg("way")), "Handler called for way objects. If the geometry of the way is " "needed then ``locations`` must be set to true when calling " "apply_file.") .def("relation", &BaseHandler::relation, &SimpleHandlerWrap::default_relation, (arg("self"), arg("relation")), "Handler called for relation objects.") .def("changeset", &BaseHandler::changeset, &SimpleHandlerWrap::default_changeset, (arg("self"), arg("changeset")), "Handler called for changeset objects.") .def("area", &BaseHandler::area, &SimpleHandlerWrap::default_area, (arg("self"), arg("area")), "Handler called for area objects.") .def("apply_file", &SimpleHandlerWrap::apply_file, (arg("self"), arg("filename"), arg("locations")=false, arg("idx")="sparse_mem_array"), "Apply the handler to the given file. If locations is true, then\n" "a location handler will be applied before, which saves the node\n" "positions. In that case, the type of this position index can be\n" "further selected in idx. If an area callback is implemented, then\n" "the file will be scanned twice and a location handler and a\n" "handler for assembling multipolygones and areas from ways will\n" "be executed.") ; def("apply", &apply_reader_simple, "Apply a chain of handlers."); def("apply", &apply_reader_simple>); def("apply", &apply_reader_simple_with_location); class_("SimpleWriter", "The most generic class to write osmium objects into a file. The writer " "takes a file name as its mandatory parameter. The file must not yet " "exists. The file type to output is determined from the file extension. " "The second (optional) parameter is the buffer size. osmium caches the " "output data in an internal memory buffer before writing it on disk. This " "parameter allows to change the default buffer size of 4MB. Larger buffers " "are normally better but you should be aware that there are normally multiple " "buffers in use during the write process.", init()) .def(init()) .def("add_node", &SimpleWriterWrap::add_node, (arg("self"), arg("node")), "Add a new node to the file. The node may be an ``osmium.osm.Node`` object, " "an ``osmium.osm.mutable.Node`` object or any other Python object that " "implements the same attributes.") .def("add_way", &SimpleWriterWrap::add_way, (arg("self"), arg("way")), "Add a new way to the file. The way may be an ``osmium.osm.Way`` object, " "an ``osmium.osm.mutable.Way`` object or any other Python object that " "implements the same attributes.") .def("add_relation", &SimpleWriterWrap::add_relation, (arg("self"), arg("relation")), "Add a new relation to the file. The relation may be an " "``osmium.osm.Relation`` object, an ``osmium.osm.mutable.Way`` " "object or any other Python object that implements the same attributes.") .def("close", &SimpleWriterWrap::close, args("self"), "Flush the remaining buffers and close the writer. While it is not " "strictly necessary to call this function explicitly, it is still " "strongly recommended to close the writer as soon as possible, so " "that the buffer memory can be freed.") ; } pyosmium-2.6.0/lib/std_pair.hpp000066400000000000000000000021671265460313400164770ustar00rootroot00000000000000#ifndef PYOSMIUM_STD_PAIR_HPP #define PYOSMIUM_STD_PAIR_HPP // Borrowed from Boost Python examples. // Copyright Ralf W. Grosse-Kunstleve 2002-2004. Distributed under the Boost // Software License, Version 1.0. #include #include #include #include namespace { // Avoid cluttering the global namespace. // Converts a std::pair instance to a Python tuple. template struct std_pair_to_tuple { static PyObject* convert(std::pair const& p) { return boost::python::incref( boost::python::make_tuple(p.first, p.second).ptr()); } static PyTypeObject const *get_pytype () {return &PyTuple_Type; } }; // Helper for convenience. template struct std_pair_to_python_converter { std_pair_to_python_converter() { boost::python::to_python_converter< std::pair, std_pair_to_tuple, true //std_pair_to_tuple has get_pytype >(); } }; } // namespace anonymous #endif pyosmium-2.6.0/osmium/000077500000000000000000000000001265460313400147165ustar00rootroot00000000000000pyosmium-2.6.0/osmium/__init__.py000066400000000000000000000001471265460313400170310ustar00rootroot00000000000000from osmium._osmium import * import osmium.io import osmium.osm import osmium.index import osmium.geom pyosmium-2.6.0/osmium/osm/000077500000000000000000000000001265460313400155145ustar00rootroot00000000000000pyosmium-2.6.0/osmium/osm/__init__.py000066400000000000000000000006761265460313400176360ustar00rootroot00000000000000from ._osm import * import osmium.osm.mutable def create_mutable_node(node, **args): return osmium.osm.mutable.Node(base=node, **args) def create_mutable_way(node, **args): return osmium.osm.mutable.Way(base=node, **args) def create_mutable_relation(node, **args): return osmium.osm.mutable.Relation(base=node, **args) Node.replace = create_mutable_node Way.replace = create_mutable_way Relation.replace = create_mutable_relation pyosmium-2.6.0/osmium/osm/mutable.py000066400000000000000000000062211265460313400175200ustar00rootroot00000000000000class OSMObject(object): """Mutable version of ``osmium.osm.OSMObject``. It exposes the following attributes ``id``, ``version``, ``visible``, ``changeset``, ``timestamp``, ``uid`` and ``tags``. Timestamps may be strings or datetime objects. Tags can be an osmium.osm.TagList, a dict-like object or a list of tuples, where each tuple contains a (key value) string pair. If the ``base`` parameter is given in the constructor, then the object will be initialised first from the attributes of this base object. """ def __init__(self, base=None, id=None, version=None, visible=None, changeset=None, timestamp=None, uid=None, tags=None): if base is None: self.id = id self.version = version self.visible = visible self.changeset = changeset self.timestamp = timestamp self.uid = uid self.tags = tags else: self.id = base.id if id is None else id self.version = base.version if version is None else version self.visible = base.visible if visible is None else visible self.changeset = base.changeset if changeset is None else changeset self.timestamp = base.timestamp if timestamp is None else timestamp self.uid = base.uid if uid is None else uid self.tags = base.tags if tags is None else tags class Node(OSMObject): """The mutable version of ``osmium.osm.Node``. It inherits all attributes from osmium.osm.mutable.OSMObject and adds a `location` attribute. This may either be an `osmium.osm.Location` or a tuple of lon/lat coordinates. """ def __init__(self, base=None, location=None, **attrs): OSMObject.__init__(self, base=base, **attrs) if base is None: self.location = location else: self.location = loctation if location is not None else base.location class Way(OSMObject): """The mutable version of ``osmium.osm.Way``. It inherits all attributes from osmium.osm.mutable.OSMObject and adds a `nodes` attribute. This may either be and ``osmium.osm.NodeList`` or a list consisting of ``osmium.osm.NodeRef`` or simple node ids. """ def __init__(self, base=None, nodes=None, **attrs): OSMObject.__init__(self, base=base, **attrs) if base is None: self.nodes = nodes else: self.nodes = nodes if nodes is not None else base.nodes class Relation(OSMObject): """The mutable version of ``osmium.osm.Relation``. It inherits all attributes from osmium.osm.mutable.OSMObject and adds a `members` attribute. This may either be an ``osmium.osm.RelationMemberList`` or a list consisting of ``osmium.osm.RelationMember`` or tuples of (type, id, role). The member type should be a single character 'n', 'w' or 'r'. """ def __init__(self, base=None, members=None, **attrs): OSMObject.__init__(self, base=base, **attrs) if base is None: self.members = members else: self.members = members if members is not None else base.members pyosmium-2.6.0/setup.py000066400000000000000000000044331265460313400151230ustar00rootroot00000000000000from distutils.core import setup, Extension from os import environ from sys import version_info as pyversion, platform as osplatform from ctypes.util import find_library includes = [] libs = [] libdirs = [] ## boost dependencies includes.append('/usr/include') if osplatform in ["linux", "linux2"]: libdirs.append('/usr/lib/x86_64-linux-gnu/') # try to find the boost library matching the python version suffixes = [ # Debian naming convention for version installed in parallel "-py%d%d" % (pyversion.major, pyversion.minor), # standard suffix for Python3 "%d" % (pyversion.major), # standard naming "", # former naming schema? "-mt" ] for suf in suffixes: lib = find_library("boost_python%s" % suf) if lib is not None: libs.append("boost_python%s" % suf) break else: raise Exception("Cannot find boost_python library") ### osmium dependencies includes.append('../libosmium/include') osmium_libs = ('expat', 'pthread', 'z', 'bz2') libs.extend(osmium_libs) extensions = [] extra_compile_args = [ '-std=c++11', '-D_LARGEFILE_SOURCE', '-D_FILE_OFFSET_BITS=64' ] extensions.append(Extension('osmium._osmium', sources = ['lib/osmium.cc'], include_dirs = includes, libraries = libs, library_dirs = libdirs, language = 'c++', extra_compile_args = extra_compile_args )) packages = ['osmium'] for ext in ('io', 'index', 'geom'): extensions.append(Extension('osmium.%s' % ext, sources = ['lib/%s.cc' % ext], include_dirs = includes, libraries = libs, library_dirs = libdirs, language = 'c++', extra_compile_args = extra_compile_args )) for ext in ('osm', ): extensions.append(Extension('osmium.%s._%s' % (ext, ext), sources = ['lib/%s.cc' % ext], include_dirs = includes, libraries = libs, library_dirs = libdirs, language = 'c++', extra_compile_args = extra_compile_args )) packages.append('osmium.%s' % ext) setup (name = 'pyosmium', version = '2.6.0', description = 'Provides python bindings for libosmium.', packages = packages, ext_modules = extensions) pyosmium-2.6.0/test/000077500000000000000000000000001265460313400143645ustar00rootroot00000000000000pyosmium-2.6.0/test/helpers.py000066400000000000000000000107231265460313400164030ustar00rootroot00000000000000""" Provides some helper functions for test. """ import random import tempfile import os import osmium def _complete_object(o): """Takes a hash with an incomplete OSM object description and returns a complete one. """ if o['type'] == 'C': ret = { 'created_at' : "2005-04-09T19:54:13Z", 'num_changes' : 2, 'closed_at' : "2005-04-09T20:54:39Z", 'open' : "false", 'min_lon' : -0.1465242, 'min_lat' : 51.5288506, 'max_lon' : -0.1464925, 'max_lat' : 51.5288620, 'user' : "Steve", 'uid' : "1", 'tags' : None } else: ret = { 'version' : '1', 'timestamp': "2012-05-01T15:06:20Z", 'changeset' : "11470653", 'uid' : "122294", 'user' : "foo", 'tags' : {} } ret.update(o) if ret['type'] == 'N': if 'lat' not in ret: ret['lat'] = random.random()*180 - 90 if 'lon' not in ret: ret['lon'] = random.random()*360 - 180 return ret def _write_osm_obj(fd, obj): if obj['type'] == 'N': fd.write(('\n'.encode('utf-8')) else: fd.write('>\n'.encode('utf-8')) for k,v in iter(obj['tags'].items()): fd.write((' \n' % (k, v)).encode('utf-8')) fd.write('\n'.encode('utf-8')) elif obj['type'] == 'W': fd.write(('\n' % obj).encode('utf-8')) for nd in obj['nodes']: fd.write(('\n' % (nd,)).encode('utf-8')) for k,v in iter(obj['tags'].items()): fd.write((' \n' % (k, v)).encode('utf-8')) fd.write('\n'.encode('utf-8')) elif obj['type'] == 'R': fd.write(('\n' % obj).encode('utf-8')) for mem in obj['members']: fd.write((' \n' % mem).encode('utf-8')) for k,v in iter(obj['tags'].items()): fd.write((' \n' % (k, v)).encode('utf-8')) fd.write('\n'.encode('utf-8')) elif obj['type'] == 'C': fd.write(('\n'.encode('utf-8')) else: fd.write('>\n'.encode('utf-8')) for k,v in iter(obj['tags'].items()): fd.write((' \n' % (k, v)).encode('utf-8')) fd.write('\n'.encode('utf-8')) def create_osm_file(data): """Creates a temporary osm XML file. The data is a list of OSM objects, each described by a hash of attributes. Most attributes are optional and will be filled with sensitive values, if missing. Mandatory are only `type` and `id`. For ways, nodes are obligatory and for relations the memberlist. """ data.sort(key=lambda x:('NWR'.find(x['type']), x['id'])) with tempfile.NamedTemporaryFile(dir='/tmp', suffix='.osm', delete=False) as fd: fname = fd.name fd.write("\n".encode('utf-8')) fd.write('\n'.encode('utf-8')) fd.write('\t\n'.encode('utf-8')) for obj in data: _write_osm_obj(fd, _complete_object(obj)) fd.write('\n'.encode('utf-8')) return fname def osmobj(kind, **args): ret = dict(args) ret['type'] = kind return ret class HandlerTestBase: apply_locations = False apply_idx = 'sparse_mem_array' def test_func(self): fn = create_osm_file(self.data) try: self.Handler().apply_file(fn, self.apply_locations, self.apply_idx) finally: os.remove(fn) pyosmium-2.6.0/test/run_tests.py000066400000000000000000000010021265460313400167550ustar00rootroot00000000000000import sys import sysconfig import os # use some black magic to find the libraries in the build directory # borrowed from http://stackoverflow.com/questions/14320220/testing-python-c-libraries-get-build-path build_dir = "../../build/lib.%s-%d.%d" % ( sysconfig.get_platform(), sys.version_info[0], sys.version_info[1]) # insert after the current directory sys.path.insert(1, os.path.normpath(os.path.join(os.path.realpath(__file__), build_dir))) import nose nose.main() pyosmium-2.6.0/test/test_geom.py000066400000000000000000000024711265460313400167300ustar00rootroot00000000000000from nose.tools import * import unittest from helpers import create_osm_file, osmobj, HandlerTestBase import osmium as o wkbfab = o.geom.WKBFactory() class TestWkbCreateNode(HandlerTestBase, unittest.TestCase): data = [osmobj('N', id=1)] class Handler(o.SimpleHandler): def node(self, n): wkb = wkbfab.create_point(n) class TestWkbCreateWay(HandlerTestBase, unittest.TestCase): data = [osmobj('N', id=1, lat=0, lon=0), osmobj('N', id=2, lat=0, lon=1), osmobj('N', id=3, lat=1, lon=0), osmobj('W', id=1, nodes = [1,2,3])] apply_locations = True class Handler(o.SimpleHandler): def way(self, w): wkb = wkbfab.create_linestring(w) wkb = wkbfab.create_linestring(w, direction=o.geom.direction.BACKWARD) wkb = wkbfab.create_linestring(w, use_nodes=o.geom.use_nodes.ALL) class TestWkbCreatePoly(HandlerTestBase, unittest.TestCase): data = [osmobj('N', id=1, lat=0, lon=0), osmobj('N', id=2, lat=0, lon=1), osmobj('N', id=3, lat=1, lon=0), osmobj('W', id=23, nodes = [1,2,3,1], tags = { "area" : "yes" }), ] apply_locations = True class Handler(o.SimpleHandler): def area(self, a): wkb = wkbfab.create_multipolygon(a) pyosmium-2.6.0/test/test_io.py000066400000000000000000000036041265460313400164070ustar00rootroot00000000000000from nose.tools import * import unittest import os from helpers import create_osm_file, osmobj import osmium as o class TestReaderFromFile(unittest.TestCase): def _run_file(self, fn): try: rd = o.io.Reader(fn) o.apply(rd, o.SimpleHandler()) rd.close() finally: os.remove(fn) def test_node_only(self): self._run_file(create_osm_file([osmobj('N', id=1)])) def test_way_only(self): self._run_file(create_osm_file([osmobj('W', id=1, nodes=[1,2,3])])) def test_relation_only(self): self._run_file(create_osm_file([osmobj('R', id=1, members=[('way', 1, '')])])) def test_node_with_tags(self): self._run_file(create_osm_file([osmobj('N', id=1, tags=dict(foo='bar', name='xx'))])) def test_way_with_tags(self): self._run_file(create_osm_file([osmobj('W', id=1, nodes=[1,2,3], tags=dict(foo='bar', name='xx'))])) def test_relation_with_tags(self): self._run_file(create_osm_file([osmobj('R', id=1, members=[('way', 1, '')], tags=dict(foo='bar', name='xx'))])) def test_broken_timestamp(self): fn = create_osm_file([osmobj('N', id=1, timestamp='x')]) try: rd = o.io.Reader(fn) with assert_raises(ValueError): o.apply(rd, o.SimpleHandler()) rd.close() finally: os.remove(fn) class TestFileHeader(unittest.TestCase): def test_file_header(self): fn = create_osm_file([osmobj('N', id=1)]) try: rd = o.io.Reader(fn) h = rd.header() assert_false(h.has_multiple_object_versions) rd.close() finally: os.remove(fn) if __name__ == '__main__': unittest.main() pyosmium-2.6.0/test/test_osm.py000066400000000000000000000142151265460313400165760ustar00rootroot00000000000000from nose.tools import * import unittest import os import sys from datetime import datetime if sys.version_info[0] == 3: from datetime import timezone def mkdate(*args): return datetime(*args, tzinfo=timezone.utc) else: def mkdate(*args): return datetime(*args) from helpers import create_osm_file, osmobj, HandlerTestBase import osmium as o class TestLocation(unittest.TestCase): def test_invalid_location(self): loc = o.osm.Location() assert_false(loc.valid()) def test_valid_location(self): loc = o.osm.Location(1,10) assert_equals(loc.lon, 1, 0.0001) assert_equals(loc.lat, 10, 0.00001) assert_equals(loc.x, 10000000) assert_equals(loc.y, 100000000) class TestNodeAttributes(HandlerTestBase, unittest.TestCase): data = [osmobj('N', id=1, version=5, changeset=58674, uid=42, timestamp='2014-01-31T06:23:35Z', user='anonymous')] class Handler(o.SimpleHandler): def node(self, n): assert_equals(n.id, 1) assert_equals(n.deleted, False) assert_equals(n.visible, True) assert_equals(n.version, 5) assert_equals(n.changeset, 58674) assert_equals(n.uid, 42) assert_equals(n.user_is_anonymous(), False) assert_equals(n.timestamp, mkdate(2014, 1, 31, 6, 23, 35)) assert_equals(n.user, 'anonymous') assert_equals(n.positive_id(), 1) class TestNodePositiveId(HandlerTestBase, unittest.TestCase): data = [osmobj('N', id=-34, version=5, changeset=58674, uid=42, timestamp='2014-01-31T06:23:35Z', user='anonymous')] class Handler(o.SimpleHandler): def node(self, n): assert_equals(n.positive_id(), 34) class TestWayAttributes(HandlerTestBase, unittest.TestCase): apply_locations = True data = [osmobj('N', id=1, lat=0, lon=0), osmobj('N', id=3, lat=1, lon=1), osmobj('W', id=1, version=5, changeset=58674, uid=42, timestamp='2014-01-31T06:23:35Z', user='anonymous', nodes = [1,2,3])] class Handler(o.SimpleHandler): def way(self, n): assert_equals(n.id, 1) assert_equals(n.deleted, False) assert_equals(n.visible, True) assert_equals(n.version, 5) assert_equals(n.changeset, 58674) assert_equals(n.uid, 42) assert_equals(n.user_is_anonymous(), False) assert_equals(n.timestamp, mkdate(2014, 1, 31, 6, 23, 35)) assert_equals(n.user, 'anonymous') assert_equals(n.positive_id(), 1) assert_false(n.is_closed()) assert_false(n.ends_have_same_id()) assert_false(n.ends_have_same_location()) class TestRelationAttributes(HandlerTestBase, unittest.TestCase): data = [osmobj('R', id=1, version=5, changeset=58674, uid=42, timestamp='2014-01-31T06:23:35Z', user='anonymous', members=[('way',1,'')])] class Handler(o.SimpleHandler): def relation(self, n): assert_equals(n.id, 1) assert_equals(n.deleted, False) assert_equals(n.visible, True) assert_equals(n.version, 5) assert_equals(n.changeset, 58674) assert_equals(n.uid, 42) assert_equals(n.user_is_anonymous(), False) assert_equals(n.timestamp, mkdate(2014, 1, 31, 6, 23, 35)) assert_equals(n.user, 'anonymous') assert_equals(n.positive_id(), 1) class TestAreaFromWayAttributes(HandlerTestBase, unittest.TestCase): data = [osmobj('N', id=1, lat=0, lon=0), osmobj('N', id=2, lat=0, lon=1), osmobj('N', id=3, lat=1, lon=0), osmobj('W', id=23, version=5, changeset=58674, uid=42, timestamp='2014-01-31T06:23:35Z', user='anonymous', nodes = [1,2,3,1], tags = { "area" : "yes" }), ] class Handler(o.SimpleHandler): def area(self, n): assert_equals(n.id, 46) assert_equals(n.deleted, False) assert_equals(n.visible, True) assert_equals(n.version, 5) assert_equals(n.changeset, 58674) assert_equals(n.uid, 42) assert_equals(n.user_is_anonymous(), False) assert_equals(n.timestamp, mkdate(2014, 1, 31, 6, 23, 35)) assert_equals(n.user, 'anonymous') assert_equals(n.positive_id(), 46) assert_equals(n.orig_id(), 23) assert_equals(n.from_way(), True) assert_equals(n.is_multipolygon(), False) assert_equals(n.num_rings(), (1, 0)) assert_equals(len(list(n.outer_rings())), 1) oring = list(n.outer_rings())[0] assert_equals(len(list(oring)), 4) assert_equals(set((1,2,3)), set([x.ref for x in oring])) assert_true(oring.is_closed()) assert_true(oring.ends_have_same_id()) assert_true(oring.ends_have_same_location()) assert_equals(len(list(n.inner_rings())), 0) class TestChangesetAttributes(HandlerTestBase, unittest.TestCase): data = [osmobj('C', id=34, created_at="2005-04-09T19:54:13Z", num_changes=2, closed_at="2005-04-09T20:54:39Z", open="false", min_lon=-0.1465242, min_lat=51.5288506, max_lon=-0.1464925, max_lat=51.5288620, user="Steve", uid="1") ] class Handler(o.SimpleHandler): def changeset(self,c): assert_equals(34, c.id) assert_equals(1, c.uid) assert_false(c.user_is_anonymous()) assert_equals("Steve", c.user) assert_equals(mkdate(2005, 4, 9, 19, 54, 13), c.created_at) assert_equals(mkdate(2005, 4, 9, 20, 54, 39), c.closed_at) assert_false(c.open) assert_equals(2, c.num_changes) assert_equals(0, len(c.tags)) assert_equals(-1464925, c.bounds.top_right.x) assert_equals(515288620, c.bounds.top_right.y) assert_equals(-1465242, c.bounds.bottom_left.x) assert_equals(515288506, c.bounds.bottom_left.y) pyosmium-2.6.0/test/test_writer.py000066400000000000000000000107111265460313400173110ustar00rootroot00000000000000from nose.tools import * import unittest import tempfile import os from contextlib import contextmanager from datetime import datetime from collections import OrderedDict import logging import sys import osmium as o log = logging.getLogger(__name__) if sys.version_info[0] == 3: from datetime import timezone def mkdate(*args): return datetime(*args, tzinfo=timezone.utc) else: def mkdate(*args): return datetime(*args) @contextmanager def WriteExpect(expected): fname = tempfile.mktemp(dir='/tmp', suffix='.opl') writer = o.SimpleWriter(fname, 1024*1024) try: yield writer finally: writer.close() with open(fname, 'r') as fd: line = fd.readline().strip() assert_equals(line, expected) os.remove(fname) class O(object): def __init__(self, **params): for k,v in params.items(): setattr(self, k, v) class TestWriteSimpleAttributes(unittest.TestCase): test_data_simple_attr = ( (O(id=None), '0 v0 dV c0 t i0 u T'), (O(visible=None), '0 v0 dV c0 t i0 u T'), (O(version=None), '0 v0 dV c0 t i0 u T'), (O(uid=None), '0 v0 dV c0 t i0 u T'), (O(user=None), '0 v0 dV c0 t i0 u T'), (O(timestamp=None), '0 v0 dV c0 t i0 u T'), (O(id=1), '1 v0 dV c0 t i0 u T'), (O(id=-99), '-99 v0 dV c0 t i0 u T'), (O(visible=True), '0 v0 dV c0 t i0 u T'), (O(visible=False), '0 v0 dD c0 t i0 u T'), (O(version=23), '0 v23 dV c0 t i0 u T'), (O(user="Schmidt"), '0 v0 dV c0 t i0 uSchmidt T'), (O(user=""), '0 v0 dV c0 t i0 u T'), (O(uid=987), '0 v0 dV c0 t i987 u T'), (O(timestamp='2012-04-14T20:58:35Z'), '0 v0 dV c0 t2012-04-14T20:58:35Z i0 u T'), (O(timestamp=mkdate(2009, 4, 14, 20, 58, 35)), '0 v0 dV c0 t2009-04-14T20:58:35Z i0 u T'), (O(timestamp='1970-01-01T00:00:01Z'), '0 v0 dV c0 t1970-01-01T00:00:01Z i0 u T'), ) def test_node_simple_attr(self): for node, out in self.test_data_simple_attr: with WriteExpect('n' + out + ' x y') as w: w.add_node(node) def test_way_simple_attr(self): for way, out in self.test_data_simple_attr: with WriteExpect('w' + out + ' N') as w: w.add_way(way) def test_relation_simple_attr(self): for rel, out in self.test_data_simple_attr: with WriteExpect('r' + out + ' M') as w: w.add_relation(rel) class TestWriteTags(unittest.TestCase): test_data_tags = ( (None, 'T'), ([], 'T'), ({}, 'T'), ((("foo", "bar"), ), 'Tfoo=bar'), ((("foo", "bar"), ("2", "1")), 'Tfoo=bar,2=1'), ({'test' : 'drive'}, 'Ttest=drive'), (OrderedDict((('a', 'b'), ('c', '3'))), 'Ta=b,c=3'), ) def test_node_tags(self): for tags, out in self.test_data_tags: with WriteExpect('n0 v0 dV c0 t i0 u ' + out + ' x y') as w: w.add_node(O(tags=tags)) def test_way_tags(self): for tags, out in self.test_data_tags: with WriteExpect('w0 v0 dV c0 t i0 u ' + out + ' N') as w: w.add_way(O(tags=tags)) def test_relation_tags(self): for tags, out in self.test_data_tags: with WriteExpect('r0 v0 dV c0 t i0 u ' + out + ' M') as w: w.add_relation(O(tags=tags)) class TestWriteNode(unittest.TestCase): def test_location_tuple(self): with WriteExpect('n0 v0 dV c0 t i0 u T x1.0000000 y2.0000000') as w: w.add_node(O(location=(1, 2))) def test_location_none(self): with WriteExpect('n0 v0 dV c0 t i0 u T x y') as w: w.add_node(O(location=None)) class TestWriteWay(unittest.TestCase): def test_node_list(self): with WriteExpect('w0 v0 dV c0 t i0 u T Nn1,n2,n3,n-4') as w: w.add_way(O(nodes=(1, 2, 3, -4))) def test_node_list_none(self): with WriteExpect('w0 v0 dV c0 t i0 u T N') as w: w.add_way(O(nodes=None)) class TestWriteRelation(unittest.TestCase): def test_relation_members(self): with WriteExpect('r0 v0 dV c0 t i0 u T Mn34@foo,r200@,w1111@x') as w: w.add_relation(O(members=(('n', 34, 'foo'), ('r', 200, ''), ('w', 1111, 'x') ))) def test_relation_members_None(self): with WriteExpect('r0 v0 dV c0 t i0 u T M') as w: w.add_relation(O(members=None)) if __name__ == '__main__': unittest.main()