327 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			327 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #ifndef OSMIUM_IO_FILE_HPP
 | |
| #define OSMIUM_IO_FILE_HPP
 | |
| 
 | |
| /*
 | |
| 
 | |
| This file is part of Osmium (http://osmcode.org/libosmium).
 | |
| 
 | |
| Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
 | |
| 
 | |
| Boost Software License - Version 1.0 - August 17th, 2003
 | |
| 
 | |
| Permission is hereby granted, free of charge, to any person or organization
 | |
| obtaining a copy of the software and accompanying documentation covered by
 | |
| this license (the "Software") to use, reproduce, display, distribute,
 | |
| execute, and transmit the Software, and to prepare derivative works of the
 | |
| Software, and to permit third-parties to whom the Software is furnished to
 | |
| do so, all subject to the following:
 | |
| 
 | |
| The copyright notices in the Software and this entire statement, including
 | |
| the above license grant, this restriction and the following disclaimer,
 | |
| must be included in all copies of the Software, in whole or in part, and
 | |
| all derivative works of the Software, unless such copies or derivative
 | |
| works are solely in the form of machine-executable object code generated by
 | |
| a source language processor.
 | |
| 
 | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
| FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 | |
| SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 | |
| FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 | |
| ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | |
| DEALINGS IN THE SOFTWARE.
 | |
| 
 | |
| */
 | |
| 
 | |
| #include <cstddef>
 | |
| #include <sstream>
 | |
| #include <string>
 | |
| #include <vector>
 | |
| 
 | |
| #include <osmium/io/error.hpp>
 | |
| #include <osmium/io/file_format.hpp>
 | |
| #include <osmium/io/file_compression.hpp>
 | |
| #include <osmium/util/options.hpp>
 | |
| 
 | |
| namespace osmium {
 | |
| 
 | |
|     /**
 | |
|      * @brief Everything related to input and output of OSM data.
 | |
|      */
 | |
|     namespace io {
 | |
| 
 | |
|         namespace detail {
 | |
| 
 | |
|             inline std::vector<std::string> split(const std::string& in, const char delim) {
 | |
|                 std::vector<std::string> result;
 | |
|                 std::stringstream ss(in);
 | |
|                 std::string item;
 | |
|                 while (std::getline(ss, item, delim)) {
 | |
|                     result.push_back(item);
 | |
|                 }
 | |
|                 return result;
 | |
|             }
 | |
| 
 | |
|         } // namespace detail
 | |
| 
 | |
|         /**
 | |
|          * This class describes an OSM file in one of several different formats.
 | |
|          *
 | |
|          * If the filename is empty or "-", this means stdin or stdout is used.
 | |
|          */
 | |
|         class File : public osmium::util::Options {
 | |
| 
 | |
|         private:
 | |
| 
 | |
|             std::string m_filename;
 | |
| 
 | |
|             const char* m_buffer;
 | |
|             size_t m_buffer_size;
 | |
| 
 | |
|             std::string m_format_string;
 | |
| 
 | |
|             file_format m_file_format {file_format::unknown};
 | |
| 
 | |
|             file_compression m_file_compression {file_compression::none};
 | |
| 
 | |
|             bool m_has_multiple_object_versions {false};
 | |
| 
 | |
|         public:
 | |
| 
 | |
|             /**
 | |
|              * Create File using type and encoding from filename or given
 | |
|              * format specification.
 | |
|              *
 | |
|              * @param filename Filename including suffix. The type and encoding
 | |
|              *                 of the file will be taken from the suffix.
 | |
|              *                 An empty filename or "-" means stdin or stdout.
 | |
|              * @param format File format as string. See the description of the
 | |
|              *               parse_format() function for details. If this is
 | |
|              *               empty the format will be deduced from the suffix
 | |
|              *               of the filename.
 | |
|              */
 | |
|             explicit File(const std::string& filename = "", const std::string& format = "") :
 | |
|                 Options(),
 | |
|                 m_filename(filename),
 | |
|                 m_buffer(nullptr),
 | |
|                 m_buffer_size(0),
 | |
|                 m_format_string(format) {
 | |
| 
 | |
|                 // stdin/stdout
 | |
|                 if (m_filename == "-") {
 | |
|                     m_filename = "";
 | |
|                 }
 | |
| 
 | |
|                 // if filename is a URL, default to XML format
 | |
|                 const std::string protocol = m_filename.substr(0, m_filename.find_first_of(':'));
 | |
|                 if (protocol == "http" || protocol == "https") {
 | |
|                     m_file_format = file_format::xml;
 | |
|                 }
 | |
| 
 | |
|                 if (format.empty()) {
 | |
|                     detect_format_from_suffix(m_filename);
 | |
|                 } else {
 | |
|                     parse_format(format);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             /**
 | |
|              * Create File using buffer pointer and size and type and encoding
 | |
|              * from given format specification.
 | |
|              *
 | |
|              * @param buffer Pointer to buffer with data.
 | |
|              * @param size   Size of buffer.
 | |
|              * @param format File format as string. See the description of the
 | |
|              *               parse_format() function for details.
 | |
|              */
 | |
|             explicit File(const char* buffer, size_t size, const std::string& format = "") :
 | |
|                 Options(),
 | |
|                 m_filename(),
 | |
|                 m_buffer(buffer),
 | |
|                 m_buffer_size(size),
 | |
|                 m_format_string(format) {
 | |
|                 if (format != "") {
 | |
|                     parse_format(format);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             File(const File&) = default;
 | |
|             File& operator=(const File&) = default;
 | |
| 
 | |
|             File(File&&) = default;
 | |
|             File& operator=(File&&) = default;
 | |
| 
 | |
|             ~File() = default;
 | |
| 
 | |
|             const char* buffer() const noexcept {
 | |
|                 return m_buffer;
 | |
|             }
 | |
| 
 | |
|             size_t buffer_size() const noexcept {
 | |
|                 return m_buffer_size;
 | |
|             }
 | |
| 
 | |
|             void parse_format(const std::string& format) {
 | |
|                 std::vector<std::string> options = detail::split(format, ',');
 | |
| 
 | |
|                 // if the first item in the format list doesn't contain
 | |
|                 // an equals sign, it is a format
 | |
|                 if (!options.empty() && options[0].find_first_of('=') == std::string::npos) {
 | |
|                     detect_format_from_suffix(options[0]);
 | |
|                     options.erase(options.begin());
 | |
|                 }
 | |
| 
 | |
|                 for (auto& option : options) {
 | |
|                     const size_t pos = option.find_first_of('=');
 | |
|                     if (pos == std::string::npos) {
 | |
|                         set(option, true);
 | |
|                     } else {
 | |
|                         std::string value = option.substr(pos+1);
 | |
|                         option.erase(pos);
 | |
|                         set(option, value);
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 if (get("history") == "true") {
 | |
|                     m_has_multiple_object_versions = true;
 | |
|                 } else if (get("history") == "false") {
 | |
|                     m_has_multiple_object_versions = false;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             void detect_format_from_suffix(const std::string& name) {
 | |
|                 std::vector<std::string> suffixes = detail::split(name, '.');
 | |
| 
 | |
|                 if (suffixes.empty()) return;
 | |
| 
 | |
|                 // if the last suffix is one of a known set of compressions,
 | |
|                 // set that compression
 | |
|                 if (suffixes.back() == "gz") {
 | |
|                     m_file_compression = file_compression::gzip;
 | |
|                     suffixes.pop_back();
 | |
|                 } else if (suffixes.back() == "bz2") {
 | |
|                     m_file_compression = file_compression::bzip2;
 | |
|                     suffixes.pop_back();
 | |
|                 }
 | |
| 
 | |
|                 if (suffixes.empty()) return;
 | |
| 
 | |
|                 // if the last suffix is one of a known set of formats,
 | |
|                 // set that format
 | |
|                 if (suffixes.back() == "pbf") {
 | |
|                     m_file_format = file_format::pbf;
 | |
|                     suffixes.pop_back();
 | |
|                 } else if (suffixes.back() == "xml") {
 | |
|                     m_file_format = file_format::xml;
 | |
|                     suffixes.pop_back();
 | |
|                 } else if (suffixes.back() == "opl") {
 | |
|                     m_file_format = file_format::opl;
 | |
|                     suffixes.pop_back();
 | |
|                 } else if (suffixes.back() == "json") {
 | |
|                     m_file_format = file_format::json;
 | |
|                     suffixes.pop_back();
 | |
|                 } else if (suffixes.back() == "o5m") {
 | |
|                     m_file_format = file_format::o5m;
 | |
|                     suffixes.pop_back();
 | |
|                 } else if (suffixes.back() == "o5c") {
 | |
|                     m_file_format = file_format::o5m;
 | |
|                     m_has_multiple_object_versions = true;
 | |
|                     set("o5c_change_format", true);
 | |
|                     suffixes.pop_back();
 | |
|                 } else if (suffixes.back() == "debug") {
 | |
|                     m_file_format = file_format::debug;
 | |
|                     suffixes.pop_back();
 | |
|                 }
 | |
| 
 | |
|                 if (suffixes.empty()) return;
 | |
| 
 | |
|                 if (suffixes.back() == "osm") {
 | |
|                     if (m_file_format == file_format::unknown) m_file_format = file_format::xml;
 | |
|                     suffixes.pop_back();
 | |
|                 } else if (suffixes.back() == "osh") {
 | |
|                     if (m_file_format == file_format::unknown) m_file_format = file_format::xml;
 | |
|                     m_has_multiple_object_versions = true;
 | |
|                     suffixes.pop_back();
 | |
|                 } else if (suffixes.back() == "osc") {
 | |
|                     if (m_file_format == file_format::unknown) m_file_format = file_format::xml;
 | |
|                     m_has_multiple_object_versions = true;
 | |
|                     set("xml_change_format", true);
 | |
|                     suffixes.pop_back();
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             /**
 | |
|              * Check file format etc. for consistency and throw exception if
 | |
|              * there is a problem.
 | |
|              *
 | |
|              * @throws osmium::io_error
 | |
|              */
 | |
|             const File& check() const {
 | |
|                 if (m_file_format == file_format::unknown) {
 | |
|                     std::string msg = "Could not detect file format";
 | |
|                     if (!m_format_string.empty())  {
 | |
|                         msg += " from format string '";
 | |
|                         msg += m_format_string;
 | |
|                         msg += "'";
 | |
|                     }
 | |
|                     if (m_filename.empty()) {
 | |
|                         msg += " for stdin/stdout";
 | |
|                     } else {
 | |
|                         msg += " for filename '";
 | |
|                         msg += m_filename;
 | |
|                         msg += "'";
 | |
|                     }
 | |
|                     msg += ".";
 | |
|                     throw io_error(msg);
 | |
|                 }
 | |
|                 return *this;
 | |
|             }
 | |
| 
 | |
|             file_format format() const noexcept {
 | |
|                 return m_file_format;
 | |
|             }
 | |
| 
 | |
|             File& set_format(file_format format) noexcept {
 | |
|                 m_file_format = format;
 | |
|                 return *this;
 | |
|             }
 | |
| 
 | |
|             file_compression compression() const noexcept {
 | |
|                 return m_file_compression;
 | |
|             }
 | |
| 
 | |
|             File& set_compression(file_compression compression) noexcept {
 | |
|                 m_file_compression = compression;
 | |
|                 return *this;
 | |
|             }
 | |
| 
 | |
|             bool has_multiple_object_versions() const noexcept {
 | |
|                 return m_has_multiple_object_versions;
 | |
|             }
 | |
| 
 | |
|             File& set_has_multiple_object_versions(bool value) noexcept {
 | |
|                 m_has_multiple_object_versions = value;
 | |
|                 return *this;
 | |
|             }
 | |
| 
 | |
|             File& filename(const std::string& filename) {
 | |
|                 if (filename == "-") {
 | |
|                     m_filename = "";
 | |
|                 } else {
 | |
|                     m_filename = filename;
 | |
|                 }
 | |
|                 return *this;
 | |
|             }
 | |
| 
 | |
|             const std::string& filename() const noexcept {
 | |
|                 return m_filename;
 | |
|             }
 | |
| 
 | |
|         }; // class File
 | |
| 
 | |
|     } // namespace io
 | |
| 
 | |
| } // namespace osmium
 | |
| 
 | |
| #endif // OSMIUM_IO_FILE_HPP
 |