mirror of
https://github.com/publiclab/mapknitter.git
synced 2025-12-12 11:20:00 +01:00
lots of changes, plus warps (finished warpables) but not yet functioning fully
This commit is contained in:
86
app/models/warp.rb
Normal file
86
app/models/warp.rb
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
class Warp < ActiveRecord::Base
|
||||||
|
|
||||||
|
has_attachment :content_type => :image,
|
||||||
|
:storage => :file_system,:path_prefix => 'public/warpables',
|
||||||
|
#:storage => :s3,
|
||||||
|
:max_size => 5.megabytes,
|
||||||
|
# :resize_to => '320x200>',
|
||||||
|
:processor => :image_science,
|
||||||
|
:thumbnails => { :medium => '500x375', :small => '240x180', :thumb => '100x100>' }
|
||||||
|
|
||||||
|
# validates_as_attachment
|
||||||
|
|
||||||
|
def validate
|
||||||
|
errors.add_to_base("You must choose a file to upload") unless self.filename
|
||||||
|
|
||||||
|
unless self.filename == nil
|
||||||
|
|
||||||
|
# Images should only be GIF, JPEG, or PNG
|
||||||
|
[:content_type].each do |attr_name|
|
||||||
|
enum = attachment_options[attr_name]
|
||||||
|
unless enum.nil? || enum.include?(send(attr_name))
|
||||||
|
errors.add_to_base("You can only upload images (GIF, JPEG, or PNG)")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Images should be less than 5 MB
|
||||||
|
[:size].each do |attr_name|
|
||||||
|
enum = attachment_options[attr_name]
|
||||||
|
unless enum.nil? || enum.include?(send(attr_name))
|
||||||
|
errors.add_to_base("Images should be smaller than 5 MB in size")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def url=(value)
|
||||||
|
self.uploaded_data = UrlUpload.new(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
require 'open-uri'
|
||||||
|
|
||||||
|
# Make it always write to tempfiles, never StringIO
|
||||||
|
OpenURI::Buffer.module_eval {
|
||||||
|
remove_const :StringMax
|
||||||
|
const_set :StringMax, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
class UrlUpload
|
||||||
|
EXTENSIONS = {
|
||||||
|
"image/jpeg" => ["jpg", "jpeg", "jpe"],
|
||||||
|
"image/gif" => ["gif"],
|
||||||
|
"image/png" => ["png"]
|
||||||
|
}
|
||||||
|
attr_reader :original_filename, :attachment_data
|
||||||
|
def initialize(url)
|
||||||
|
@attachment_data = open(url)
|
||||||
|
@original_filename = determine_filename
|
||||||
|
end
|
||||||
|
|
||||||
|
# Pass things like size, content_type, path on to the downloaded file
|
||||||
|
def method_missing(symbol, *args)
|
||||||
|
if self.attachment_data.respond_to? symbol
|
||||||
|
self.attachment_data.send symbol, *args
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def determine_filename
|
||||||
|
# Grab the path - even though it could be a script and not an actual file
|
||||||
|
path = self.attachment_data.base_uri.path
|
||||||
|
# Get the filename from the path, make it lowercase to handle those
|
||||||
|
# crazy Win32 servers with all-caps extensions
|
||||||
|
filename = File.basename(path).downcase
|
||||||
|
# If the file extension doesn't match the content type, add it to the end, changing any existing .'s to _
|
||||||
|
filename = [filename.gsub(/\./, "_"), EXTENSIONS[self.content_type].first].join(".") unless EXTENSIONS[self.content_type].any? {|ext| filename.ends_with?("." + ext) }
|
||||||
|
# Return the result
|
||||||
|
filename
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
9
db/migrate/20100505153420_add_warpable_deleted.rb
Normal file
9
db/migrate/20100505153420_add_warpable_deleted.rb
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
class AddWarpableDeleted < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
add_column :warpables, :deleted, :boolean, :default => false, :null => false
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
remove_column :warpables, :deleted
|
||||||
|
end
|
||||||
|
end
|
||||||
20
db/migrate/20100531175531_create_warpeds.rb
Normal file
20
db/migrate/20100531175531_create_warpeds.rb
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
class CreateWarpeds < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
create_table :warpeds do |t|
|
||||||
|
t.integer :parent_id
|
||||||
|
t.string :content_type
|
||||||
|
t.string :filename
|
||||||
|
t.string :thumbnail
|
||||||
|
t.integer :size
|
||||||
|
t.integer :width
|
||||||
|
t.integer :height
|
||||||
|
t.string :transform_type
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
drop_table :warpeds
|
||||||
|
end
|
||||||
|
end
|
||||||
19
db/migrate/20100531175725_create_warps.rb
Normal file
19
db/migrate/20100531175725_create_warps.rb
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
class CreateWarps < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
create_table :warps do |t|
|
||||||
|
t.integer :parent_id
|
||||||
|
t.string :content_type
|
||||||
|
t.string :filename
|
||||||
|
t.string :thumbnail
|
||||||
|
t.integer :size
|
||||||
|
t.integer :width
|
||||||
|
t.integer :height
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
drop_table :warps
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -6,6 +6,22 @@ class Cartagen
|
|||||||
word
|
word
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.spherical_mercator_lon_to_x(lon,center_lon,scale_factor=10000)
|
||||||
|
(lon - center_lon) * -1 * scale_factor
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.spherical_mercator_x_to_lon(x,center_lon,scale_factor=10000)
|
||||||
|
(x/(-1*scale_factor)) + center_lon
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.spherical_mercator_lat_to_y(lat,scale_factor=10000)
|
||||||
|
180/Math::PI * Math.log(Math.tan(Math::PI/4+lat*(Math::PI/180)/2)) * scale_factor
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.spherical_mercator_y_to_lat(y,scale_factor=10000)
|
||||||
|
180/Math::PI * (2 * Math.atan(Math.exp(y/scale_factor*Math::PI/180)) - Math::PI/2)
|
||||||
|
end
|
||||||
|
|
||||||
# collects coastline ways into collected_way relations;
|
# collects coastline ways into collected_way relations;
|
||||||
# see http://wiki.openstreetmap.org/wiki/Relations/Proposed/Collected_Ways
|
# see http://wiki.openstreetmap.org/wiki/Relations/Proposed/Collected_Ways
|
||||||
def self.collect_ways(features)
|
def self.collect_ways(features)
|
||||||
|
|||||||
279
lib/ogr2osm/SimpleXMLWriter.py
Normal file
279
lib/ogr2osm/SimpleXMLWriter.py
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
#
|
||||||
|
# SimpleXMLWriter
|
||||||
|
# $Id: SimpleXMLWriter.py 2312 2005-03-02 18:13:39Z fredrik $
|
||||||
|
#
|
||||||
|
# a simple XML writer
|
||||||
|
#
|
||||||
|
# history:
|
||||||
|
# 2001-12-28 fl created
|
||||||
|
# 2002-11-25 fl fixed attribute encoding
|
||||||
|
# 2002-12-02 fl minor fixes for 1.5.2
|
||||||
|
# 2004-06-17 fl added pythondoc markup
|
||||||
|
# 2004-07-23 fl added flush method (from Jay Graves)
|
||||||
|
# 2004-10-03 fl added declaration method
|
||||||
|
#
|
||||||
|
# Copyright (c) 2001-2004 by Fredrik Lundh
|
||||||
|
#
|
||||||
|
# fredrik@pythonware.com
|
||||||
|
# http://www.pythonware.com
|
||||||
|
#
|
||||||
|
# --------------------------------------------------------------------
|
||||||
|
# The SimpleXMLWriter module is
|
||||||
|
#
|
||||||
|
# Copyright (c) 2001-2004 by Fredrik Lundh
|
||||||
|
#
|
||||||
|
# By obtaining, using, and/or copying this software and/or its
|
||||||
|
# associated documentation, you agree that you have read, understood,
|
||||||
|
# and will comply with the following terms and conditions:
|
||||||
|
#
|
||||||
|
# Permission to use, copy, modify, and distribute this software and
|
||||||
|
# its associated documentation for any purpose and without fee is
|
||||||
|
# hereby granted, provided that the above copyright notice appears in
|
||||||
|
# all copies, and that both that copyright notice and this permission
|
||||||
|
# notice appear in supporting documentation, and that the name of
|
||||||
|
# Secret Labs AB or the author not be used in advertising or publicity
|
||||||
|
# pertaining to distribution of the software without specific, written
|
||||||
|
# prior permission.
|
||||||
|
#
|
||||||
|
# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||||
|
# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
|
||||||
|
# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
|
||||||
|
# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
|
||||||
|
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||||
|
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||||
|
# OF THIS SOFTWARE.
|
||||||
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
|
##
|
||||||
|
# Tools to write XML files, without having to deal with encoding
|
||||||
|
# issues, well-formedness, etc.
|
||||||
|
# <p>
|
||||||
|
# The current version does not provide built-in support for
|
||||||
|
# namespaces. To create files using namespaces, you have to provide
|
||||||
|
# "xmlns" attributes and explicitly add prefixes to tags and
|
||||||
|
# attributes.
|
||||||
|
#
|
||||||
|
# <h3>Patterns</h3>
|
||||||
|
#
|
||||||
|
# The following example generates a small XHTML document.
|
||||||
|
# <pre>
|
||||||
|
#
|
||||||
|
# from elementtree.SimpleXMLWriter import XMLWriter
|
||||||
|
# import sys
|
||||||
|
#
|
||||||
|
# w = XMLWriter(sys.stdout)
|
||||||
|
#
|
||||||
|
# html = w.start("html")
|
||||||
|
#
|
||||||
|
# w.start("head")
|
||||||
|
# w.element("title", "my document")
|
||||||
|
# w.element("meta", name="generator", value="my application 1.0")
|
||||||
|
# w.end()
|
||||||
|
#
|
||||||
|
# w.start("body")
|
||||||
|
# w.element("h1", "this is a heading")
|
||||||
|
# w.element("p", "this is a paragraph")
|
||||||
|
#
|
||||||
|
# w.start("p")
|
||||||
|
# w.data("this is ")
|
||||||
|
# w.element("b", "bold")
|
||||||
|
# w.data(" and ")
|
||||||
|
# w.element("i", "italic")
|
||||||
|
# w.data(".")
|
||||||
|
# w.end("p")
|
||||||
|
#
|
||||||
|
# w.close(html)
|
||||||
|
# </pre>
|
||||||
|
##
|
||||||
|
|
||||||
|
import re, sys, string
|
||||||
|
|
||||||
|
try:
|
||||||
|
unicode("")
|
||||||
|
except NameError:
|
||||||
|
def encode(s, encoding):
|
||||||
|
# 1.5.2: application must use the right encoding
|
||||||
|
return s
|
||||||
|
_escape = re.compile(r"[&<>\"\x80-\xff]+") # 1.5.2
|
||||||
|
else:
|
||||||
|
def encode(s, encoding):
|
||||||
|
return s.encode(encoding)
|
||||||
|
_escape = re.compile(eval(r'u"[&<>\"\u0080-\uffff]+"'))
|
||||||
|
|
||||||
|
def encode_entity(text, pattern=_escape):
|
||||||
|
# map reserved and non-ascii characters to numerical entities
|
||||||
|
def escape_entities(m):
|
||||||
|
out = []
|
||||||
|
for char in m.group():
|
||||||
|
out.append("&#%d;" % ord(char))
|
||||||
|
return string.join(out, "")
|
||||||
|
return encode(pattern.sub(escape_entities, text), "ascii")
|
||||||
|
|
||||||
|
del _escape
|
||||||
|
|
||||||
|
#
|
||||||
|
# the following functions assume an ascii-compatible encoding
|
||||||
|
# (or "utf-16")
|
||||||
|
|
||||||
|
def escape_cdata(s, encoding=None, replace=string.replace):
|
||||||
|
s = replace(s, "&", "&")
|
||||||
|
s = replace(s, "<", "<")
|
||||||
|
s = replace(s, ">", ">")
|
||||||
|
if encoding:
|
||||||
|
try:
|
||||||
|
return encode(s, encoding)
|
||||||
|
except UnicodeError:
|
||||||
|
return encode_entity(s)
|
||||||
|
return s
|
||||||
|
|
||||||
|
def escape_attrib(s, encoding=None, replace=string.replace):
|
||||||
|
s = replace(s, "&", "&")
|
||||||
|
s = replace(s, "'", "'")
|
||||||
|
s = replace(s, "\"", """)
|
||||||
|
s = replace(s, "<", "<")
|
||||||
|
s = replace(s, ">", ">")
|
||||||
|
if encoding:
|
||||||
|
try:
|
||||||
|
return encode(s, encoding)
|
||||||
|
except UnicodeError:
|
||||||
|
return encode_entity(s)
|
||||||
|
return s
|
||||||
|
|
||||||
|
##
|
||||||
|
# XML writer class.
|
||||||
|
#
|
||||||
|
# @param file A file or file-like object. This object must implement
|
||||||
|
# a <b>write</b> method that takes an 8-bit string.
|
||||||
|
# @param encoding Optional encoding.
|
||||||
|
|
||||||
|
class XMLWriter:
|
||||||
|
|
||||||
|
def __init__(self, file, encoding="us-ascii"):
|
||||||
|
if not hasattr(file, "write"):
|
||||||
|
file = open(file, "w")
|
||||||
|
self.__write = file.write
|
||||||
|
if hasattr(file, "flush"):
|
||||||
|
self.flush = file.flush
|
||||||
|
self.__open = 0 # true if start tag is open
|
||||||
|
self.__tags = []
|
||||||
|
self.__data = []
|
||||||
|
self.__encoding = encoding
|
||||||
|
|
||||||
|
def __flush(self):
|
||||||
|
# flush internal buffers
|
||||||
|
if self.__open:
|
||||||
|
self.__write(">")
|
||||||
|
self.__open = 0
|
||||||
|
if self.__data:
|
||||||
|
data = string.join(self.__data, "")
|
||||||
|
self.__write(escape_cdata(data, self.__encoding))
|
||||||
|
self.__data = []
|
||||||
|
|
||||||
|
##
|
||||||
|
# Writes an XML declaration.
|
||||||
|
|
||||||
|
def declaration(self):
|
||||||
|
encoding = self.__encoding
|
||||||
|
if encoding == "us-ascii" or encoding == "utf-8":
|
||||||
|
self.__write("<?xml version='1.0'?>\n")
|
||||||
|
else:
|
||||||
|
self.__write("<?xml version='1.0' encoding='%s'?>\n" % encoding)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Opens a new element. Attributes can be given as keyword
|
||||||
|
# arguments, or as a string/string dictionary. You can pass in
|
||||||
|
# 8-bit strings or Unicode strings; the former are assumed to use
|
||||||
|
# the encoding passed to the constructor. The method returns an
|
||||||
|
# opaque identifier that can be passed to the <b>close</b> method,
|
||||||
|
# to close all open elements up to and including this one.
|
||||||
|
#
|
||||||
|
# @param tag Element tag.
|
||||||
|
# @param attrib Attribute dictionary. Alternatively, attributes
|
||||||
|
# can be given as keyword arguments.
|
||||||
|
# @return An element identifier.
|
||||||
|
|
||||||
|
def start(self, tag, attrib={}, **extra):
|
||||||
|
self.__flush()
|
||||||
|
tag = escape_cdata(tag, self.__encoding)
|
||||||
|
self.__data = []
|
||||||
|
self.__tags.append(tag)
|
||||||
|
self.__write("<%s" % tag)
|
||||||
|
if attrib or extra:
|
||||||
|
attrib = attrib.copy()
|
||||||
|
attrib.update(extra)
|
||||||
|
attrib = attrib.items()
|
||||||
|
attrib.sort()
|
||||||
|
for k, v in attrib:
|
||||||
|
k = escape_cdata(k, self.__encoding)
|
||||||
|
v = escape_attrib(v, self.__encoding)
|
||||||
|
self.__write(" %s=\"%s\"" % (k, v))
|
||||||
|
self.__open = 1
|
||||||
|
return len(self.__tags)-1
|
||||||
|
|
||||||
|
##
|
||||||
|
# Adds a comment to the output stream.
|
||||||
|
#
|
||||||
|
# @param comment Comment text, as an 8-bit string or Unicode string.
|
||||||
|
|
||||||
|
def comment(self, comment):
|
||||||
|
self.__flush()
|
||||||
|
self.__write("<!-- %s -->\n" % escape_cdata(comment, self.__encoding))
|
||||||
|
|
||||||
|
##
|
||||||
|
# Adds character data to the output stream.
|
||||||
|
#
|
||||||
|
# @param text Character data, as an 8-bit string or Unicode string.
|
||||||
|
|
||||||
|
def data(self, text):
|
||||||
|
self.__data.append(text)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Closes the current element (opened by the most recent call to
|
||||||
|
# <b>start</b>).
|
||||||
|
#
|
||||||
|
# @param tag Element tag. If given, the tag must match the start
|
||||||
|
# tag. If omitted, the current element is closed.
|
||||||
|
|
||||||
|
def end(self, tag=None):
|
||||||
|
if tag:
|
||||||
|
assert self.__tags, "unbalanced end(%s)" % tag
|
||||||
|
assert escape_cdata(tag, self.__encoding) == self.__tags[-1],\
|
||||||
|
"expected end(%s), got %s" % (self.__tags[-1], tag)
|
||||||
|
else:
|
||||||
|
assert self.__tags, "unbalanced end()"
|
||||||
|
tag = self.__tags.pop()
|
||||||
|
if self.__data:
|
||||||
|
self.__flush()
|
||||||
|
elif self.__open:
|
||||||
|
self.__open = 0
|
||||||
|
self.__write(" />")
|
||||||
|
return
|
||||||
|
self.__write("</%s>" % tag)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Closes open elements, up to (and including) the element identified
|
||||||
|
# by the given identifier.
|
||||||
|
#
|
||||||
|
# @param id Element identifier, as returned by the <b>start</b> method.
|
||||||
|
|
||||||
|
def close(self, id):
|
||||||
|
while len(self.__tags) > id:
|
||||||
|
self.end()
|
||||||
|
|
||||||
|
##
|
||||||
|
# Adds an entire element. This is the same as calling <b>start</b>,
|
||||||
|
# <b>data</b>, and <b>end</b> in sequence. The <b>text</b> argument
|
||||||
|
# can be omitted.
|
||||||
|
|
||||||
|
def element(self, tag, text=None, attrib={}, **extra):
|
||||||
|
apply(self.start, (tag, attrib), extra)
|
||||||
|
if text:
|
||||||
|
self.data(text)
|
||||||
|
self.end()
|
||||||
|
|
||||||
|
##
|
||||||
|
# Flushes the output stream.
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
pass # replaced by the constructor
|
||||||
BIN
lib/ogr2osm/SimpleXMLWriter.pyc
Normal file
BIN
lib/ogr2osm/SimpleXMLWriter.pyc
Normal file
Binary file not shown.
769
lib/ogr2osm/ogr2osm.py
Executable file
769
lib/ogr2osm/ogr2osm.py
Executable file
@@ -0,0 +1,769 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
""" ogr2osm beta
|
||||||
|
|
||||||
|
(c) Iván Sánchez Ortega, 2009
|
||||||
|
<ivan@sanchezortega.es>
|
||||||
|
|
||||||
|
|
||||||
|
This piece of crap^H^H^H^Hsoftware is supposed to take just about any vector file
|
||||||
|
as an input thanks to the magic of the OGR libraries, and then output a pretty OSM XML
|
||||||
|
file with that data.
|
||||||
|
|
||||||
|
The cool part is that it will detect way segments shared between several ways, so
|
||||||
|
it will build relations outof thin air. This simplifies the structure of boundaries, for
|
||||||
|
example.
|
||||||
|
|
||||||
|
It is also able to translate attributes to tags, though there is only one such translation
|
||||||
|
scheme by now. In order to translate your own datasets, you should have some basic
|
||||||
|
understanding of python programming. See the files in the translation/ directory.
|
||||||
|
|
||||||
|
An outstanding issue is that elevation in 2.5D features (that can be generated by
|
||||||
|
reprojecting) is ignored completely.
|
||||||
|
|
||||||
|
Usage: specify a filename to be converted (its extension will be changed to .osm), and the
|
||||||
|
the projection the source data is in. You can specify the source projection by using either
|
||||||
|
an EPSG code or a Proj.4 string.
|
||||||
|
|
||||||
|
If the projection is not specified, ogr2osm will try to fetch it from the source data. If
|
||||||
|
there is no projection information in the source data, this will assume EPSG:4326 (WGS84
|
||||||
|
latitude-longitude).
|
||||||
|
|
||||||
|
python ogr2osm.py [options] [filename]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-e, --epsg=... EPSG code, forcing the source data projection
|
||||||
|
-p, --proj4=... PROJ4 string, forcing the source data projection
|
||||||
|
-v, --verbose Shows some seemingly random characters dancing in the screen
|
||||||
|
for every feature that's being worked on.
|
||||||
|
-h, --help Show this message
|
||||||
|
-d, --debug-tags Outputs the tags for every feature parsed
|
||||||
|
-a, --attribute-stats Outputs a summary of the different tags / attributes encountered
|
||||||
|
-t, --translation Select the attribute-tags translation method.
|
||||||
|
See the translations/ diredtory for valid values.
|
||||||
|
|
||||||
|
(-e and -p are mutually exclusive. If both are specified, only the last one will be
|
||||||
|
taken into account)
|
||||||
|
|
||||||
|
For example, if the shapefile foobar.shp has projection EPSG:23030, do:
|
||||||
|
|
||||||
|
python ogr2osm.py foobar.shp -e 23030
|
||||||
|
|
||||||
|
This will do an in-the-fly reprojection from EPSG:23030 to EPSG:4326, and write a file
|
||||||
|
called "foobar.osm"
|
||||||
|
|
||||||
|
|
||||||
|
#####################################################################################
|
||||||
|
# "THE BEER-WARE LICENSE": #
|
||||||
|
# <ivan@sanchezortega.es> wrote this file. As long as you retain this notice you #
|
||||||
|
# can do whatever you want with this stuff. If we meet some day, and you think #
|
||||||
|
# this stuff is worth it, you can buy me a beer in return. #
|
||||||
|
#####################################################################################
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import getopt
|
||||||
|
from SimpleXMLWriter import XMLWriter
|
||||||
|
|
||||||
|
try:
|
||||||
|
from osgeo import ogr
|
||||||
|
except:
|
||||||
|
import ogr
|
||||||
|
|
||||||
|
try:
|
||||||
|
from osgeo import osr
|
||||||
|
except:
|
||||||
|
import osr
|
||||||
|
|
||||||
|
# Some needed constants
|
||||||
|
from ogr import wkbPoint
|
||||||
|
from ogr import wkbLineString
|
||||||
|
from ogr import wkbPolygon
|
||||||
|
from ogr import wkbMultiPoint
|
||||||
|
from ogr import wkbMultiLineString
|
||||||
|
from ogr import wkbMultiPolygon
|
||||||
|
from ogr import wkbGeometryCollection
|
||||||
|
|
||||||
|
from ogr import wkbUnknown
|
||||||
|
from ogr import wkbNone
|
||||||
|
|
||||||
|
from ogr import wkbPoint25D
|
||||||
|
from ogr import wkbLineString25D
|
||||||
|
from ogr import wkbPolygon25D
|
||||||
|
from ogr import wkbMultiPoint25D
|
||||||
|
from ogr import wkbMultiLineString25D
|
||||||
|
from ogr import wkbMultiPolygon25D
|
||||||
|
from ogr import wkbGeometryCollection25D
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Default options
|
||||||
|
sourceEPSG = 4326
|
||||||
|
sourceProj4 = None
|
||||||
|
detectProjection = True
|
||||||
|
useEPSG = False
|
||||||
|
useProj4 = False
|
||||||
|
showProgress = False
|
||||||
|
debugTags = False
|
||||||
|
attributeStats = False
|
||||||
|
translationMethod = None
|
||||||
|
|
||||||
|
# Fetch command line parameters: file and source projection
|
||||||
|
try:
|
||||||
|
(opts, args) = getopt.getopt(sys.argv[1:], "e:p:hvdt:a", ["epsg","proj4","help","verbose","debug-tags","attribute-stats","translation"])
|
||||||
|
except getopt.GetoptError:
|
||||||
|
print __doc__
|
||||||
|
sys.exit(2)
|
||||||
|
for opt, arg in opts:
|
||||||
|
if opt in ("-h", "--help"):
|
||||||
|
print __doc__
|
||||||
|
sys.exit()
|
||||||
|
elif opt in ("-p", "--proj4"):
|
||||||
|
sourceProj4 = arg
|
||||||
|
useProj4 = True
|
||||||
|
useEPSG = False
|
||||||
|
detectProjection = False
|
||||||
|
elif opt in ("-e", "--epsg"):
|
||||||
|
try:
|
||||||
|
sourceEPSG = int(arg)
|
||||||
|
except:
|
||||||
|
print "Error: EPSG code must be numeric (e.g. '4326' instead of 'epsg:4326')"
|
||||||
|
sys.exit(1)
|
||||||
|
detectProjection = False
|
||||||
|
useEPSG = True
|
||||||
|
useProj4 = False
|
||||||
|
elif opt in ("-v", "--verbose"):
|
||||||
|
showProgress=True
|
||||||
|
elif opt in ("-d", "--debug-tags"):
|
||||||
|
debugTags=True
|
||||||
|
elif opt in ("-a", "--attribute-stats"):
|
||||||
|
attributeStats=True
|
||||||
|
attributeStatsTable = {}
|
||||||
|
elif opt in ("-t", "--translation"):
|
||||||
|
translationMethod = arg
|
||||||
|
else:
|
||||||
|
print "Unknown option " + opt
|
||||||
|
|
||||||
|
print (opts,args)
|
||||||
|
file = args[0]
|
||||||
|
fileExtension = file.split('.')[-1]
|
||||||
|
|
||||||
|
|
||||||
|
# FIXME: really complete this table
|
||||||
|
if fileExtension == 'shp':
|
||||||
|
driver = ogr.GetDriverByName('ESRI Shapefile');
|
||||||
|
elif fileExtension == 'gpx':
|
||||||
|
driver = ogr.GetDriverByName('GPX');
|
||||||
|
elif fileExtension == 'dgn':
|
||||||
|
driver = ogr.GetDriverByName('DGN');
|
||||||
|
elif fileExtension == 'gml':
|
||||||
|
driver = ogr.GetDriverByName('GML');
|
||||||
|
elif fileExtension == 'csv':
|
||||||
|
driver = ogr.GetDriverByName('CSV');
|
||||||
|
elif fileExtension == 'sqlite':
|
||||||
|
driver = ogr.GetDriverByName('SQLite');
|
||||||
|
elif fileExtension == 'kml':
|
||||||
|
driver = ogr.GetDriverByName('KML');
|
||||||
|
#elif fileExtension == 'kmz':
|
||||||
|
#driver = ogr.GetDriverByName('KML');
|
||||||
|
else:
|
||||||
|
print "Error: extension " + fileExtension + " is invalid or not implemented yet."
|
||||||
|
|
||||||
|
|
||||||
|
# Strip directories from output file name
|
||||||
|
slashPosition = file.rfind('/')
|
||||||
|
if slashPosition != -1:
|
||||||
|
#print slashPosition
|
||||||
|
outputFile = file[slashPosition+1:]
|
||||||
|
#print outputFile
|
||||||
|
#print len(fileExtension)
|
||||||
|
else:
|
||||||
|
outputFile = file
|
||||||
|
|
||||||
|
outputFile = outputFile[: -len(fileExtension) ] + 'osm'
|
||||||
|
|
||||||
|
|
||||||
|
# 0 means read-only
|
||||||
|
dataSource = driver.Open(file,0);
|
||||||
|
|
||||||
|
if dataSource is None:
|
||||||
|
print 'Could not open ' + file
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
print
|
||||||
|
print "Preparing to convert file " + file + " (extension is " + fileExtension + ") into " + outputFile
|
||||||
|
|
||||||
|
if detectProjection:
|
||||||
|
print "Will try to detect projection from source metadata, or fall back to EPSG:4326"
|
||||||
|
elif useEPSG:
|
||||||
|
print "Will assume that source data is in EPSG:" + str(sourceEPSG)
|
||||||
|
elif useProj4:
|
||||||
|
print "Will assume that source data has the Proj.4 string: " + sourceProj4
|
||||||
|
|
||||||
|
if showProgress:
|
||||||
|
print "Verbose mode is on. Get ready to see lots of dots."
|
||||||
|
|
||||||
|
if debugTags:
|
||||||
|
print "Tag debugging is on. Get ready to see lots of stuff."
|
||||||
|
|
||||||
|
|
||||||
|
# Some variables to hold stuff...
|
||||||
|
nodeIDsByXY = {}
|
||||||
|
nodeTags = {}
|
||||||
|
nodeCoords = {}
|
||||||
|
nodeRefs = {}
|
||||||
|
segmentNodes = {}
|
||||||
|
segmentIDByNodes = {}
|
||||||
|
segmentRefs = {}
|
||||||
|
areaRings = {}
|
||||||
|
areaTags = {}
|
||||||
|
lineSegments = {}
|
||||||
|
lineTags = {}
|
||||||
|
|
||||||
|
# nodeIDsByXY holds a node ID, given a set of coordinates (useful for looking for duplicated nodes)
|
||||||
|
# nodeTags holds the tag pairs given a node ID
|
||||||
|
# nodeCoords holds the coordinates of a given node ID (redundant if nodeIDsByXY is properly iterated through)
|
||||||
|
# nodeRefs holds up the IDs of any segment referencing (containing) a given node ID, as a dictionary
|
||||||
|
# segmentNodes holds up the node IDs for a given segment ID
|
||||||
|
# segmentIDByNodes holds up segment IDs for a given pair of node IDs (useful for looking for duplicated segments)
|
||||||
|
# segmentRefs holds up the IDs of any ways or areas referencing (containing) a given segment ID, as a dictionary with segment IDs as keys, and a boolean as value (the bool is a flag indicating whether the segment is an existing segment, but reversed - will probably screw things up with oneway=yes stuff)
|
||||||
|
# areaRings holds up the rings, as a list of segments, for a given area ID
|
||||||
|
# areaTags holds up the tags for a given area ID
|
||||||
|
# lineSegments and lineTags work pretty much as areaRings and areaTags (only that lineSegments is a list, and areaRings is a list of lists)
|
||||||
|
|
||||||
|
|
||||||
|
# Stuff needed for locating translation methods
|
||||||
|
if translationMethod:
|
||||||
|
try:
|
||||||
|
sys.path.append(os.getcwd() + "/translations")
|
||||||
|
module = __import__(translationMethod)
|
||||||
|
translateAttributes = module.translateAttributes
|
||||||
|
translateAttributes([])
|
||||||
|
except:
|
||||||
|
print "Could not load translation method " + translationMethod + ". Check the translations/ directory for valid values."
|
||||||
|
sys.exit(-1)
|
||||||
|
print "Successfully loaded " + translationMethod + " translation method."
|
||||||
|
else:
|
||||||
|
# If no function has been defined, perform no translation: just copy everything.
|
||||||
|
translateAttributes = lambda(attrs): attrs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
elementIdCounter = -1
|
||||||
|
nodeCount = 0
|
||||||
|
segmentCount = 0
|
||||||
|
lineCount = 0
|
||||||
|
areaCount = 0
|
||||||
|
segmentJoinCount = 0
|
||||||
|
|
||||||
|
|
||||||
|
print
|
||||||
|
print "Parsing features"
|
||||||
|
|
||||||
|
|
||||||
|
# Some aux stuff for parsing the features into the data arrays
|
||||||
|
|
||||||
|
def addNode(x,y,tags = {}):
|
||||||
|
"Given x,y, returns the ID of an existing node there, or creates it and returns the new ID. Node will be updated with the optional tags."
|
||||||
|
global elementIdCounter, nodeCount, nodeCoords, nodeIDsByXT, nodeTags, nodeCoords
|
||||||
|
|
||||||
|
if (x,y) in nodeIDsByXY:
|
||||||
|
# Node already exists, merge tags
|
||||||
|
#print
|
||||||
|
#print "Warning, node already exists"
|
||||||
|
nodeID = nodeIDsByXY[(x,y)]
|
||||||
|
try:
|
||||||
|
nodeTags[nodeID].update(tags)
|
||||||
|
except:
|
||||||
|
nodeTags[nodeID]=tags
|
||||||
|
return nodeID
|
||||||
|
else:
|
||||||
|
# Allocate a new node
|
||||||
|
nodeID = elementIdCounter
|
||||||
|
elementIdCounter = elementIdCounter - 1
|
||||||
|
|
||||||
|
nodeTags[nodeID]=tags
|
||||||
|
nodeIDsByXY[(x,y)] = nodeID
|
||||||
|
nodeCoords[nodeID] = (x,y)
|
||||||
|
nodeCount = nodeCount +1
|
||||||
|
return nodeID
|
||||||
|
|
||||||
|
|
||||||
|
def lineStringToSegments(geometry,references):
|
||||||
|
"Given a LineString geometry, will create the appropiate segments. It will add the optional tags and will not check for duplicate segments. Needs a line or area ID for updating the segment references. Returns a list of segment IDs."
|
||||||
|
global elementIdCounter, segmentCount, segmentNodes, segmentTags, showProgress, nodeRefs, segmentRefs, segmentIDByNodes
|
||||||
|
|
||||||
|
result = []
|
||||||
|
|
||||||
|
(lastx,lasty,z) = geometry.GetPoint(0)
|
||||||
|
lastNodeID = addNode(lastx,lasty)
|
||||||
|
|
||||||
|
for k in range(1,geometry.GetPointCount()):
|
||||||
|
|
||||||
|
(newx,newy,z) = geometry.GetPoint(k)
|
||||||
|
newNodeID = addNode(newx,newy)
|
||||||
|
|
||||||
|
if (lastNodeID, newNodeID) in segmentIDByNodes:
|
||||||
|
if showProgress: sys.stdout.write(u"-")
|
||||||
|
segmentID = segmentIDByNodes[(lastNodeID, newNodeID)]
|
||||||
|
reversed = False
|
||||||
|
#print
|
||||||
|
#print "Duplicated segment"
|
||||||
|
elif (newNodeID, lastNodeID) in segmentIDByNodes:
|
||||||
|
if showProgress: sys.stdout.write(u"_")
|
||||||
|
segmentID = segmentIDByNodes[(newNodeID, lastNodeID)]
|
||||||
|
reversed = True
|
||||||
|
#print
|
||||||
|
#print "Duplicated reverse segment"
|
||||||
|
else:
|
||||||
|
if showProgress: sys.stdout.write('.')
|
||||||
|
segmentID = elementIdCounter
|
||||||
|
|
||||||
|
elementIdCounter = elementIdCounter - 1
|
||||||
|
segmentCount = segmentCount +1
|
||||||
|
segmentNodes[segmentID] = [ lastNodeID, newNodeID ]
|
||||||
|
segmentIDByNodes[(lastNodeID, newNodeID)] = segmentID
|
||||||
|
reversed = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
nodeRefs[lastNodeID].update({segmentID:True})
|
||||||
|
except:
|
||||||
|
nodeRefs[lastNodeID]={segmentID:True}
|
||||||
|
try:
|
||||||
|
nodeRefs[newNodeID].update({segmentID:True})
|
||||||
|
except:
|
||||||
|
nodeRefs[newNodeID]={segmentID:True}
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
segmentRefs[segmentID].update({references:reversed})
|
||||||
|
except:
|
||||||
|
segmentRefs[segmentID]={references:reversed}
|
||||||
|
|
||||||
|
result.append(segmentID)
|
||||||
|
|
||||||
|
# FIXME
|
||||||
|
segmentRefs
|
||||||
|
|
||||||
|
lastNodeID = newNodeID
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Let's dive into the OGR data source and fetch the features
|
||||||
|
|
||||||
|
for i in range(dataSource.GetLayerCount()):
|
||||||
|
layer = dataSource.GetLayer(i)
|
||||||
|
layer.ResetReading()
|
||||||
|
|
||||||
|
spatialRef = None
|
||||||
|
if detectProjection:
|
||||||
|
spatialRef = layer.GetSpatialRef()
|
||||||
|
if spatialRef != None:
|
||||||
|
print "Detected projection metadata:"
|
||||||
|
print spatialRef
|
||||||
|
else:
|
||||||
|
print "No projection metadata, falling back to EPSG:4326"
|
||||||
|
elif useEPSG:
|
||||||
|
spatialRef = osr.SpatialReference()
|
||||||
|
spatialRef.ImportFromEPSG(sourceEPSG)
|
||||||
|
elif useProj4:
|
||||||
|
spatialRef = osr.SpatialReference()
|
||||||
|
spatialRef.ImportFromProj4(sourceProj4)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if spatialRef == None: # No source proj specified yet? Then default to do no reprojection.
|
||||||
|
# Some python magic: skip reprojection altogether by using a dummy lamdba funcion. Otherwise, the lambda will be a call to the OGR reprojection stuff.
|
||||||
|
reproject = lambda(geometry): None
|
||||||
|
else:
|
||||||
|
destSpatialRef = osr.SpatialReference()
|
||||||
|
destSpatialRef.ImportFromEPSG(4326) # Destionation projection will *always* be EPSG:4326, WGS84 lat-lon
|
||||||
|
coordTrans = osr.CoordinateTransformation(spatialRef,destSpatialRef)
|
||||||
|
reproject = lambda(geometry): geometry.Transform(coordTrans)
|
||||||
|
|
||||||
|
|
||||||
|
featureDefinition = layer.GetLayerDefn()
|
||||||
|
|
||||||
|
fieldNames = []
|
||||||
|
fieldCount = featureDefinition.GetFieldCount();
|
||||||
|
|
||||||
|
for j in range(fieldCount):
|
||||||
|
#print featureDefinition.GetFieldDefn(j).GetNameRef()
|
||||||
|
fieldNames.append (featureDefinition.GetFieldDefn(j).GetNameRef())
|
||||||
|
if attributeStats:
|
||||||
|
attributeStatsTable.update({featureDefinition.GetFieldDefn(j).GetNameRef():{} })
|
||||||
|
|
||||||
|
print
|
||||||
|
print fieldNames
|
||||||
|
print "Got layer field definitions"
|
||||||
|
|
||||||
|
#print "Feature definition: " + str(featureDefinition);
|
||||||
|
|
||||||
|
for j in range(layer.GetFeatureCount()):
|
||||||
|
feature = layer.GetNextFeature()
|
||||||
|
geometry = feature.GetGeometryRef()
|
||||||
|
|
||||||
|
fields = {}
|
||||||
|
|
||||||
|
for k in range(fieldCount-1):
|
||||||
|
#fields[ fieldNames[k] ] = feature.GetRawFieldRef(k)
|
||||||
|
fields[ fieldNames[k] ] = feature.GetFieldAsString(k)
|
||||||
|
if attributeStats:
|
||||||
|
try:
|
||||||
|
attributeStatsTable[ fieldNames[k] ][ feature.GetFieldAsString(k) ] = attributeStatsTable[ fieldNames[k] ][ feature.GetFieldAsString(k) ] + 1
|
||||||
|
except:
|
||||||
|
attributeStatsTable[ fieldNames[k] ].update({ feature.GetFieldAsString(k) : 1})
|
||||||
|
|
||||||
|
|
||||||
|
# Translate attributes into tags, as defined per the selected translation method
|
||||||
|
tags = translateAttributes(fields)
|
||||||
|
|
||||||
|
if debugTags:
|
||||||
|
print
|
||||||
|
print tags
|
||||||
|
|
||||||
|
# Do the reprojection (or pass if no reprojection is neccesary, see the lambda function definition)
|
||||||
|
reproject(geometry)
|
||||||
|
|
||||||
|
# Now we got the fields for this feature. Now, let's convert the geometry.
|
||||||
|
# Points will get converted into nodes.
|
||||||
|
# LineStrings will get converted into a set of ways, each having only two nodes
|
||||||
|
# Polygons will be converted into relations
|
||||||
|
# Later, we'll fix the topology and simplify the ways. If a relation can be simplified into a way (i.e. only has one member), it will be. Adjacent segments will be merged if they share tags and direction.
|
||||||
|
|
||||||
|
# We'll split a geometry into subGeometries or "elementary" geometries: points, linestrings, and polygons. This will take care of OGRMultiLineStrings, OGRGeometryCollections and the like
|
||||||
|
|
||||||
|
geometryType = geometry.GetGeometryType()
|
||||||
|
|
||||||
|
subGeometries = []
|
||||||
|
|
||||||
|
if geometryType == wkbPoint or geometryType == wkbLineString or geometryType == wkbPolygon:
|
||||||
|
subGeometries = [geometry]
|
||||||
|
elif geometryType == wkbMultiPoint or geometryType == wkbMultiLineString or geometryType == wkbMultiPolygon or geometryType == wkbGeometryCollection:
|
||||||
|
if showProgress: sys.stdout.write('M')
|
||||||
|
for k in range(geometry.GetGeometryCount()):
|
||||||
|
subGeometries.append(geometry.GetGeometryRef(k))
|
||||||
|
|
||||||
|
elif geometryType == wkbPoint25D or geometryType == wkbLineString25D or geometryType == wkbPolygon25D:
|
||||||
|
if showProgress: sys.stdout.write('z')
|
||||||
|
subGeometries = [geometry]
|
||||||
|
elif geometryType == wkbMultiPoint25D or geometryType == wkbMultiLineString25D or geometryType == wkbMultiPolygon25D or geometryType == wkbGeometryCollection25D:
|
||||||
|
if showProgress: sys.stdout.write('Mz')
|
||||||
|
for k in range(geometry.GetGeometryCount()):
|
||||||
|
subGeometries.append(geometry.GetGeometryRef(k))
|
||||||
|
|
||||||
|
elif geometryType == wkbUnknown:
|
||||||
|
print "Geometry type is wkbUnknown, feature will be ignored\n"
|
||||||
|
elif geometryType == wkbNone:
|
||||||
|
print "Geometry type is wkbNone, feature will be ignored\n"
|
||||||
|
else:
|
||||||
|
print "Unknown or unimplemented geometry type :" + str(geometryType) + ", feature will be ignored\n"
|
||||||
|
|
||||||
|
|
||||||
|
for geometry in subGeometries:
|
||||||
|
if geometry.GetDimension() == 0:
|
||||||
|
# 0-D = point
|
||||||
|
if showProgress: sys.stdout.write(',')
|
||||||
|
x = geometry.GetX()
|
||||||
|
y = geometry.GetY()
|
||||||
|
|
||||||
|
nodeID = addNode(x,y,tags)
|
||||||
|
# TODO: tags
|
||||||
|
|
||||||
|
elif geometry.GetDimension() == 1:
|
||||||
|
# 1-D = linestring
|
||||||
|
if showProgress: sys.stdout.write('|')
|
||||||
|
|
||||||
|
lineID = elementIdCounter
|
||||||
|
elementIdCounter = elementIdCounter - 1
|
||||||
|
lineSegments[lineID] = lineStringToSegments(geometry,lineID)
|
||||||
|
lineTags[lineID] = tags
|
||||||
|
lineCount = lineCount + 1
|
||||||
|
|
||||||
|
elif geometry.GetDimension() == 2:
|
||||||
|
# FIXME
|
||||||
|
# 2-D = area
|
||||||
|
|
||||||
|
if showProgress: sys.stdout.write('O')
|
||||||
|
areaID = elementIdCounter
|
||||||
|
elementIdCounter = elementIdCounter - 1
|
||||||
|
rings = []
|
||||||
|
|
||||||
|
for k in range(0,geometry.GetGeometryCount()):
|
||||||
|
if showProgress: sys.stdout.write('r')
|
||||||
|
rings.append(lineStringToSegments(geometry.GetGeometryRef(k), areaID))
|
||||||
|
|
||||||
|
areaRings[areaID] = rings
|
||||||
|
areaTags[areaID] = tags
|
||||||
|
areaCount = areaCount + 1
|
||||||
|
# TODO: tags
|
||||||
|
# The ring 0 will be the outer hull, any other rings will be inner hulls.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
print
|
||||||
|
print "Nodes: " + str(nodeCount)
|
||||||
|
print "Way segments: " + str(segmentCount)
|
||||||
|
print "Lines: " + str(lineCount)
|
||||||
|
print "Areas: " + str(areaCount)
|
||||||
|
|
||||||
|
print
|
||||||
|
print "Joining segments"
|
||||||
|
|
||||||
|
|
||||||
|
# OK, all features should be parsed in the arrays by now
|
||||||
|
# Let's start to do some topological magic
|
||||||
|
|
||||||
|
# We'll iterate through all the lines and areas, then iterate through all the nodes contained there
|
||||||
|
# We'll then fetch all segments referencing that node. If a pair of segments share the same references (i.e. they are part of the same line or area), they will be joined as one and de-referenced from that node. Joining segments mean than the concept of segment changes at this point, becoming linestrings or ways.
|
||||||
|
# There are some edge cases in which the algorithm may not prove optimal: if a line (or area ring) crosses itself, then the node will have more than two segments referenced to the line (or area), and does NOT check for the optimal one. As a result, lines that cross themselves may be (incorrectly) split into two and merged via a relation. In other words, the order of the points in a line (or ring) may not be kept if the line crosses itself.
|
||||||
|
# The algorithm will not check if the node has been de-referenced: instead, it will check for the first and last node of the segments involved - if the segments have already been joined, the check will fail.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def simplifyNode(nodeID):
|
||||||
|
global nodeRefs, segmentNodes, segmentRefs, showProgress, lineSegments, areaRings, segmentJoinCount
|
||||||
|
#for (nodeID, segments) in nodeRefs.items():
|
||||||
|
segments = nodeRefs[nodeID]
|
||||||
|
|
||||||
|
segmentsJoined = 0
|
||||||
|
#print
|
||||||
|
#print "Node ID: " + str(nodeID)
|
||||||
|
#print "Node references to: " + str(segments)
|
||||||
|
|
||||||
|
# We have to try all pairs of segments somehow
|
||||||
|
for segmentID1 in segments.copy():
|
||||||
|
for segmentID2 in segments.copy(): # We'll be changing the references, so make sure we iterate through the original list
|
||||||
|
if segmentID1 != segmentID2:
|
||||||
|
#print str(segmentID1) + " vs " + str(segmentID2)
|
||||||
|
try:
|
||||||
|
if segmentNodes[segmentID1][-1] == segmentNodes[segmentID2][0] == nodeID and segmentRefs[segmentID1] == segmentRefs[segmentID2] :
|
||||||
|
|
||||||
|
#print "Segment " + str(segmentID1) + ": " + str(segmentNodes[segmentID1])
|
||||||
|
#print "Segment " + str(segmentID2) + ": " + str(segmentNodes[segmentID2])
|
||||||
|
|
||||||
|
#if showProgress: sys.stdout.write('=')
|
||||||
|
segmentNodes[segmentID1].extend( segmentNodes[segmentID2][1:] ) # Voila! Joined!
|
||||||
|
for nodeShifted in segmentNodes[segmentID2][1:]: # Replace node references
|
||||||
|
#print "deleting reference from node " + str(nodeShifted) + " to segment " + str(segmentID2) + "; updating to " + str(segmentID1)
|
||||||
|
del nodeRefs[nodeShifted][segmentID2]
|
||||||
|
nodeRefs[nodeShifted].update({segmentID1:True})
|
||||||
|
|
||||||
|
# TODO: Check for potential clashes between the references? As in "way X has these segments in the wrong direction". The trivial case for this looks like a topology error, anyway.
|
||||||
|
# Anyway, delete all references to the second segment - we're 100% sure that the line or area references the first one 'cause we've checked before joining the segments
|
||||||
|
for segmentRef in segmentRefs[segmentID2]:
|
||||||
|
try:
|
||||||
|
lineSegments[segmentRef].remove(segmentID2)
|
||||||
|
except:
|
||||||
|
for ring in areaRings[segmentRef]:
|
||||||
|
try:
|
||||||
|
ring.remove(segmentID2)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
del segmentRefs[segmentID2]
|
||||||
|
|
||||||
|
del segmentNodes[segmentID2]
|
||||||
|
segmentJoinCount = segmentJoinCount +1
|
||||||
|
segmentsJoined = segmentsJoined + 1
|
||||||
|
except:
|
||||||
|
pass # This is due to the node no longer referencing to a segment because we just de-referenced it in a previous pass of the loop; this will be quite common
|
||||||
|
|
||||||
|
# FIXME: if segmentsJoined > 1, this should mark the node for further testing - It's very likely to be a self-intersection.
|
||||||
|
|
||||||
|
if showProgress: sys.stdout.write(str(segmentsJoined))
|
||||||
|
|
||||||
|
print
|
||||||
|
print "Simplifying line segments"
|
||||||
|
for line in lineSegments.values():
|
||||||
|
#print line
|
||||||
|
for segmentID in line: # No need to check the last segment, it could not be simplyfied
|
||||||
|
#print segmentID
|
||||||
|
#print segmentNodes[segmentID]
|
||||||
|
for nodeID in segmentNodes[segmentID]:
|
||||||
|
simplifyNode(nodeID)
|
||||||
|
#simplifyNode(segmentNodes[segmentID][0]) # last node in segment
|
||||||
|
|
||||||
|
print
|
||||||
|
print "Simplifying area segments"
|
||||||
|
for area in areaRings.values():
|
||||||
|
for ring in area:
|
||||||
|
for segmentID in ring:
|
||||||
|
for nodeID in segmentNodes[segmentID]:
|
||||||
|
simplifyNode(nodeID) # last node in segment
|
||||||
|
|
||||||
|
|
||||||
|
# That *should* do it... but a second pass through all the nodes will really fix things up. I wonder why some nodes are left out of the previous pass
|
||||||
|
print
|
||||||
|
print "Simplifying remaining nodes"
|
||||||
|
for node in nodeRefs.keys():
|
||||||
|
simplifyNode(node)
|
||||||
|
|
||||||
|
|
||||||
|
print
|
||||||
|
print "Nodes: " + str(nodeCount)
|
||||||
|
print "Original way segments: " + str(segmentCount)
|
||||||
|
print "Segment join operations: " + str(segmentJoinCount)
|
||||||
|
print "Lines: " + str(lineCount)
|
||||||
|
print "Areas: " + str(areaCount)
|
||||||
|
|
||||||
|
#print nodeRefs
|
||||||
|
#print segmentNodes
|
||||||
|
#print lineSegments
|
||||||
|
#print areaRings
|
||||||
|
#print segmentRefs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
print
|
||||||
|
print "Generating OSM XML..."
|
||||||
|
print "Generating nodes."
|
||||||
|
|
||||||
|
|
||||||
|
#w = XMLWriter(sys.stdout)
|
||||||
|
w = XMLWriter(open(outputFile,'w'))
|
||||||
|
|
||||||
|
w.start("osm", version='0.6', generator='ogr2osm')
|
||||||
|
|
||||||
|
# First, the nodes
|
||||||
|
for (nodeID,(x,y)) in nodeCoords.items():
|
||||||
|
w.start("node", visible="true", id=str(nodeID), lat=str(y), lon=str(x))
|
||||||
|
for (tagKey,tagValue) in nodeTags[nodeID].items():
|
||||||
|
if tagValue:
|
||||||
|
w.element("tag", k=tagKey, v=tagValue)
|
||||||
|
w.end("node")
|
||||||
|
if showProgress: sys.stdout.write('.')
|
||||||
|
|
||||||
|
|
||||||
|
#print "Generated nodes. On to shared segments."
|
||||||
|
|
||||||
|
# Now, the segments used by more than one line/area, as untagged ways
|
||||||
|
|
||||||
|
|
||||||
|
#for (segmentID, segmentRef) in segmentRefs.items():
|
||||||
|
#if len(segmentRef) > 1:
|
||||||
|
#print "FIXME: output shared segment"
|
||||||
|
#outputtedSegments[segmentID] = True
|
||||||
|
|
||||||
|
|
||||||
|
print
|
||||||
|
print "Generated nodes. On to lines."
|
||||||
|
|
||||||
|
# Next, the lines, either as ways or as relations
|
||||||
|
|
||||||
|
outputtedSegments = {}
|
||||||
|
|
||||||
|
for (lineID, lineSegment) in lineSegments.items():
|
||||||
|
if showProgress: sys.stdout.write(str(len(lineSegment)) + " ")
|
||||||
|
if len(lineSegment) == 1: # The line will be a simple way
|
||||||
|
w.start('way', id=str(lineID), action='modify', visible='true')
|
||||||
|
|
||||||
|
for nodeID in segmentNodes[ lineSegment[0] ]:
|
||||||
|
w.element('nd',ref=str(nodeID))
|
||||||
|
|
||||||
|
for (tagKey,tagValue) in lineTags[lineID].items():
|
||||||
|
if tagValue:
|
||||||
|
w.element("tag", k=tagKey, v=tagValue)
|
||||||
|
|
||||||
|
w.end('way')
|
||||||
|
pass
|
||||||
|
else: # The line will be a relationship
|
||||||
|
#print
|
||||||
|
#print "Line ID " + str(lineID) + " uses more than one segment: " + str(lineSegment)
|
||||||
|
for segmentID in lineSegment:
|
||||||
|
if segmentID not in outputtedSegments:
|
||||||
|
w.start('way', id=str(segmentID), action='modify', visible='true')
|
||||||
|
for nodeID in segmentNodes[ segmentID ]:
|
||||||
|
w.element('nd',ref=str(nodeID))
|
||||||
|
w.end('way')
|
||||||
|
w.start('relation', id=str(lineID), action='modify', visible='true')
|
||||||
|
for segmentID in lineSegment:
|
||||||
|
w.element('member', type='way', ref=str(segmentID), role='')
|
||||||
|
for (tagKey,tagValue) in lineTags[lineID].items():
|
||||||
|
if tagValue:
|
||||||
|
w.element("tag", k=tagKey, v=tagValue)
|
||||||
|
w.end('relation')
|
||||||
|
|
||||||
|
print
|
||||||
|
print "Generated lines. On to areas."
|
||||||
|
|
||||||
|
# And last, the areas, either as ways or as relations
|
||||||
|
|
||||||
|
#print areaRings
|
||||||
|
|
||||||
|
for (areaID, areaRing) in areaRings.items():
|
||||||
|
#sys.stdout.write(str(len(areaRings)))
|
||||||
|
|
||||||
|
if len(areaRing) == 1 and len(areaRing[0]) == 1: # The area will be a simple way
|
||||||
|
w.start('way', id=str(areaID), action='modify', visible='true')
|
||||||
|
|
||||||
|
for nodeID in segmentNodes[ areaRing[0][0] ]:
|
||||||
|
w.element('nd',ref=str(nodeID))
|
||||||
|
|
||||||
|
for (tagKey,tagValue) in areaTags[areaID].items():
|
||||||
|
if tagValue:
|
||||||
|
w.element("tag", k=tagKey, v=tagValue)
|
||||||
|
|
||||||
|
w.end('way')
|
||||||
|
if showProgress: sys.stdout.write('0 ')
|
||||||
|
else:
|
||||||
|
segmentsUsed = 0
|
||||||
|
segmentsUsedInRing = 0
|
||||||
|
#print "FIXME"
|
||||||
|
|
||||||
|
for ring in areaRing:
|
||||||
|
for segmentID in ring:
|
||||||
|
if segmentID not in outputtedSegments:
|
||||||
|
w.start('way', id=str(segmentID), action='modify', visible='true')
|
||||||
|
for nodeID in segmentNodes[ segmentID ]:
|
||||||
|
w.element('nd',ref=str(nodeID))
|
||||||
|
w.end('way')
|
||||||
|
|
||||||
|
|
||||||
|
w.start('relation', id=str(areaID), action='modify', visible='true')
|
||||||
|
w.element("tag", k='type', v='multipolygon')
|
||||||
|
|
||||||
|
role = 'outer'
|
||||||
|
for ring in areaRing:
|
||||||
|
for segmentID in ring:
|
||||||
|
w.element('member', type='way', ref=str(segmentID), role=role)
|
||||||
|
segmentsUsed = segmentsUsed + 1
|
||||||
|
segmentsUsedInRing = segmentsUsedInRing + 1
|
||||||
|
role = 'inner'
|
||||||
|
#if showProgress: sys.stdout.write(str(segmentsUsedInRing)+'r')
|
||||||
|
segmentsUsedInRing = 0
|
||||||
|
|
||||||
|
for (tagKey,tagValue) in areaTags[areaID].items():
|
||||||
|
if tagValue:
|
||||||
|
w.element("tag", k=tagKey, v=tagValue)
|
||||||
|
w.end('relation')
|
||||||
|
if showProgress: sys.stdout.write(str(segmentsUsed) + " ")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if attributeStats:
|
||||||
|
print
|
||||||
|
for (attribute, stats) in attributeStatsTable.items():
|
||||||
|
print "All values for attribute " + attribute + ":"
|
||||||
|
print stats
|
||||||
|
|
||||||
|
|
||||||
|
print
|
||||||
|
print "All done. Enjoy your data!"
|
||||||
|
|
||||||
|
|
||||||
|
w.end("osm")
|
||||||
|
|
||||||
|
|
||||||
76
lib/ogr2osm/translations/carreteras_gv.py
Normal file
76
lib/ogr2osm/translations/carreteras_gv.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
"""
|
||||||
|
Translation rules for Valencia Community road network.
|
||||||
|
|
||||||
|
This is so simple that it'll be used as an example of how to build translation methods.
|
||||||
|
|
||||||
|
The important thing is that a file like this must define a "translateAttributes" function that, taking a directory of attributes (key:value), will return a directory of tags (key:value)
|
||||||
|
|
||||||
|
The function must allow for empty directories.
|
||||||
|
|
||||||
|
The reason for using a full module is that you can define auxiliary functions and data structures here. But this example is so simple it won't have any auxiliary stuff at all.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def translateAttributes(attrs):
|
||||||
|
if not attrs: return
|
||||||
|
|
||||||
|
tags = {}
|
||||||
|
|
||||||
|
# Use the "NOM_ACT" attribute as the name= tag
|
||||||
|
if attrs['NOM_ACT']:
|
||||||
|
tags = {'name':attrs['NOM_ACT']}
|
||||||
|
|
||||||
|
# If the name contains an hyphen, set it to the ref= tag too
|
||||||
|
if attrs['NOM_ACT'].find('-') != -1:
|
||||||
|
tags.update({'ref':attrs['NOM_ACT']})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Depending on the value of the TIPUS_ACT, set the highway= tag
|
||||||
|
if attrs['TIPUS_ACT'] == 'Altres comunitats autonomes':
|
||||||
|
tags.update({'highway':'road'})
|
||||||
|
|
||||||
|
elif attrs['TIPUS_ACT'] == 'Basica':
|
||||||
|
tags.update({'highway':'trunk'})
|
||||||
|
|
||||||
|
elif attrs['TIPUS_ACT'] == 'En construccio':
|
||||||
|
tags.update({'highway':'construction','construction':'road'})
|
||||||
|
|
||||||
|
elif attrs['TIPUS_ACT'] == 'Via de servei':
|
||||||
|
tags.update({'highway':'service'})
|
||||||
|
|
||||||
|
elif attrs['TIPUS_ACT'] == 'Municipal':
|
||||||
|
tags.update({'highway':'primary'})
|
||||||
|
|
||||||
|
elif attrs['TIPUS_ACT'] == 'Autopista/Autovia':
|
||||||
|
tags.update({'highway':'motorway'})
|
||||||
|
|
||||||
|
elif attrs['TIPUS_ACT'] == 'Auxiliar':
|
||||||
|
tags.update({'highway':'motorway_link'})
|
||||||
|
|
||||||
|
elif attrs['TIPUS_ACT'] == 'Local':
|
||||||
|
tags.update({'highway':'tertiary'})
|
||||||
|
|
||||||
|
elif attrs['TIPUS_ACT'] == 'Fora de servei':
|
||||||
|
tags.update({'highway':'road', 'access':'no'})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#print "foo!"
|
||||||
|
return tags
|
||||||
|
#sys.exit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Taken from --attribute-stats:
|
||||||
|
|
||||||
|
All values for attribute TIPUS_ACT:
|
||||||
|
{'Altres comunitats autonomes': 224, 'Basica': 2950, 'En construccio': 360, 'Via de servei': 505, 'Municipal': 3135, 'Autopista/Autovia': 2849, 'Auxiliar': 9887, 'Local': 4868, 'Fora de servei': 35}
|
||||||
|
|
||||||
|
All values for attribute TIT_ACT:
|
||||||
|
{'Diputacio': 3337, 'Municipal': 2152, 'Sense determinar': 6498, 'Ministeri': 5908, 'Conselleria': 6881, 'Fora de servei': 35, 'Altres administracions': 2}
|
||||||
|
"""
|
||||||
|
|
||||||
55
lib/ogr2osm/translations/ithaca_haiti.py
Normal file
55
lib/ogr2osm/translations/ithaca_haiti.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
"""
|
||||||
|
Translation rules for the Ithaca Haiti damage assesment report data.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def translateAttributes(attrs):
|
||||||
|
if not attrs: return
|
||||||
|
|
||||||
|
tags = {}
|
||||||
|
|
||||||
|
tags.update({'FIXME':'Check for duplicated data'})
|
||||||
|
|
||||||
|
tags.update({'source':'Ithaca'})
|
||||||
|
|
||||||
|
tags.update({'source:imagery': attrs['SOURCE'] })
|
||||||
|
|
||||||
|
# Only thing to translate is the "TYPE" attr.
|
||||||
|
|
||||||
|
if attrs['TYPE'] == 'Landslide':
|
||||||
|
tags.update({'earthquake:damage':'landslide'})
|
||||||
|
|
||||||
|
|
||||||
|
if attrs['TYPE'] == 'Damaged infrastructure':
|
||||||
|
tags.update({'earthquake:damage':'damaged_infrastructure'})
|
||||||
|
|
||||||
|
|
||||||
|
if attrs['TYPE'] == 'Spontaneous camp':
|
||||||
|
tags.update({'tourism':'camp_site'})
|
||||||
|
tags.update({'refugee':'yes'})
|
||||||
|
tags.update({'earthquake:damage':'spontaneous_camp'})
|
||||||
|
|
||||||
|
|
||||||
|
if attrs['TYPE'] == 'Collapsed building':
|
||||||
|
tags.update({'earthquake:damage':'collapsed_buiding'})
|
||||||
|
tags.update({'building':'collapsed'})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#print "foo!"
|
||||||
|
return tags
|
||||||
|
#sys.exit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Taken from --attribute-stats:
|
||||||
|
|
||||||
|
All values for attribute TYPE:
|
||||||
|
{'Landslide': 45, 'Damaged infrastructure': 35, 'Spontaneous camp': 87, 'Collapsed building': 1490}
|
||||||
|
|
||||||
|
"""
|
||||||
19
test/fixtures/warps.yml
vendored
Normal file
19
test/fixtures/warps.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
|
||||||
|
|
||||||
|
one:
|
||||||
|
parent_id: 1
|
||||||
|
content_type: MyString
|
||||||
|
filename: MyString
|
||||||
|
thumbnail: MyString
|
||||||
|
size: 1
|
||||||
|
width: 1
|
||||||
|
height: 1
|
||||||
|
|
||||||
|
two:
|
||||||
|
parent_id: 1
|
||||||
|
content_type: MyString
|
||||||
|
filename: MyString
|
||||||
|
thumbnail: MyString
|
||||||
|
size: 1
|
||||||
|
width: 1
|
||||||
|
height: 1
|
||||||
8
test/unit/warp_test.rb
Normal file
8
test/unit/warp_test.rb
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class WarpTest < ActiveSupport::TestCase
|
||||||
|
# Replace this with your real tests.
|
||||||
|
test "the truth" do
|
||||||
|
assert true
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user