O5M

2018-01-09  本文已影响0人  微雨旧时歌丶

https://wiki.openstreetmap.org/wiki/O5m


目录


<h2 id="1">1.Motivation</h2>
已有两种主流格式:.osm以及.pbf。为什么还要另外一种呢?让我们仔细看看这两种最常见的格式,并尝试确定它们的优缺点。
<h3 id="1.1">1.1 Conventional formats 传统格式</h3>

1.1.1 .osm

这种数据格式是人类可读的,因为它是用XML编写的。通常,文件中的对象按类型(节点、方式、关系)依次排序,然后按id排序。XML格式有一些优点和缺点。

Advantages

Disadvantages

1.1.2 .osm.bz2

Advantages

Disadvantages

1.1.3 .pbf

这种优化的格式被引入来消除一些.osm格式的缺点。它具有一定的灵活性,例如允许地理区域集群,因此您可以从较大的文件中选择区域的数据。(这将意味着打破常规的id顺序。)

通常的可以从不同位置下载的.pbf文件,其对象的顺序与传统的.osm文件相同。

Advantages

Disadvantages

<h3 id="1.2">1.2 Why a new Format?</h3>
新格式试图将两种格式的优点结合起来,主要目标是:

不幸的是,由于加速数据处理,某些目标无法达到。我们将不得不忍受一些缺点:

此外,目前还没有在o5m文件中随机访问的机制,不过如果需要的话,可以很容易地添加这个机制。
<h2 id="2">2. Format description</h2>
新的.o5m格式的结构类似于传统的.osm格式:对象使用严格的顺序存储。首先是所有节点,然后是所有的路线,最后是所有的关系。在这三组中,对象按其id顺序升序排列。

每个数字都是使用Varint格式打包的,您可能从.pbf文件中知道这种格式。字符串包含在零字节中,或者重复时引用。
<h3 id="2.1">2.1 Basics</h3>
要理解格式,最好知道如何存储数字字符串

2.1.1 Numbers

为了存储不同长度的数字,我们放弃了每个字节的位7(最重要的位),并使用这个位作为长度指示符。这个指示符——当设置为1时——告诉我们下一个字节属于相同的数字。这样一个长数字的第一个字节包含最不重要的7位,最后一个字节是最重要的7位。例如:

( ==11000011== 00000010

指示符 64 32 16 8 4 2 1 14次方 …… …… …… 1024 512 256 128
1 1 0 0 0 0 1 1 0 0 0 0 0 0 1 0

因此256+64+2+1 = 323
)

如果一个数字存储为“有符号型”,我们将需要1位符号位。为此,将最小有效字节的最小值作为符号位。0表示正,1表示负。当然,我们不需要数字-0,所以我们可以把负数的范围改为1。例如:

( ==11000011== 00000010

指示符 32 16 8 4 2 1 符号位 13次方 …… …… …… 512 256 128 64
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1

因此 1*64 = + 64
)

要正确地解释一个存储的数字,我们必须知道它是否存储为一个有符号或者无符号的数字。出于这个原因,我们希望用于存储OSM信息的格式必须非常准确地定义。

如你所见,小数字比大数字需要的空间更少。我们的数据格式会尽量避免大数字。为了达到这个目的,我们使用了.pbf格式中的一个技巧:即delta编码

特别是当数字之间的差别很小的时候,我们只存储这些数字之间的差异。例如,假设我们要存储几个节点的id,比如123000,123050,123055。这些被存储为三个有符号整数值:+123000,+50,+5。

所描述的数字格式只支持整数。为了存储小数,我们使用定点表示。纬度和经度被存储为以100纳米为单位的数值,即小数点右移7位。注意,在增量编码精度值时,创建或读取.o5m文件的应用程序使用32位有符号整数值而不是64位。出于这个原因,即使在你期望的情况下,你也不会得到任何超出32位有符号整数范围的delta值 -
例如,如果delta数为358度(179减去 -179)。这些358度的存储值为正值(714.967.296或0x2a9d8900),因为在32位算术中,1.790.000.000 + 714.967.296会溢出,结果是-1.790.000.000,这正是我们想要的值。在64位算法中,我们将存储一个负值(-3.580.000.000或0xffffffff2a9d8900)。很明显,我们只需要这个值的最后32位,这正是存储在32位算术中的值。

2.1.2 Strings

字符串没有打包,它们按原样存储(编码为UTF-8)。一般来说,字符串是成对存储的。为了标记开始、结束和字符串对的两个元素之间的位置,我们使用零字节。如果没有一对,但只有一个字符串,第二个元素将被省略。用户名是用他们的用户ID打包成一个字符串。例如:

Varint编码 :0xfc 0x07 = 1111 1100 0000 0111 = 4+8+ 16+32+64 +128+256+512 = 1020

UTF-8编码:0x4a 0x6f 0x68 0x4e ->John
)

要做到这一点,每个字符串都需要花费大量的空间。幸运的是,OSM中的大多数字符串都成对出现,并不断重复。以"highway"/"residential""building"/"yes"为例。这允许我们在任何时候重用以前遇到的一对字符串时使用引用。

To refer to a string pair which has already been defined in the way shown above, we count the string pair definitions from the present location in the file back to that definition which matches our string pair.
为了引用已经在上面所示的方式定义的字符串对,我们将字符串对定义从文件当前位置返回到与我们的字符串对匹配的定义。使用相同的方法将该计数存储为无符号的数字,该方法在前面的章节中已经描述过。

为了限制解码器程序必须分配的最大内存数量,我们可以引用的不同的字符串对的数量是有限制的,并且它们的长度是有限制的:

当读取一个.o5m编码的文件时,我们使用一个有15000行,250+2个字符的引用表(出于性能考虑:使用256个字符)。我们遇到的每个字符串对都被复制到表中,只有一个例外:字符串对的长度超过250个字符,但不会被复制到表中。

如果表中没有空间容纳新的字符串对,那么最老的字符串对将被覆盖。该表作为FIRO(先进,随机输出)。地址以1开头,表示“最新存储字符串”。地址2表示“第二个最新存储字符串”,等等。

注意,字符串通常存储为字符串对,即使定义为单个字符串。

下面是一个如何存储几个字符串的示例:

它们的编码如下:

<h3 id="2.2">2.2 File</h3>

每一个.o5m文件以字节0xff开始,以字节0xfe结束。每个数据集从一个特定的字节开始:

每个数据集的第二个字节,如果需要,因为值大于127,则下面的字节包含长度信息,编码为无符号数(见上文)。如果解码程序不理解一个特定的数据集,它将跳过它的内容。长度信息不包括长度字节本身,也不包括数据集的开始字节。

注意,在从0xf00xff的范围内没有长度信息,因此程序必须跳过一个字节。

<h3 id="2.3">2.3 Node</h3>

每个节点数据集从0x10开始,然后是数据集长度数据。数据字段:

ox10 数据集长度 数据

例如:

这个例子的OSM XML格式文件如下:

<node id="125799" lat="53.0749606" lon="8.7867843" version="5" changeset="5922698" user="UScha" uid="45445" timestamp="2010-09-30T19:23:30Z"/>
<node id="125799" lat="53.0749606" lon="8.7867843" version="5" changeset="5922698" user="UScha" uid="45445" timestamp="2010-09-30T19:23:30Z"/>

注意,可以通过减少数据集的长度(数据集的第二字节)来缩短数据集。解码程序必须处理这样的剪切数据集,并接受它们不包含键/val对、纬度/经度,甚至不包含作者信息。如果数据集以这样的方式被剪切,只剩下它的正文(id和可能的版本和作者信息),程序应该执行一个delete操作,并使用这个id删除对象(参见下面,section .o5c)。

<h3 id="2.4">2.4 Way</h3>
数据集从0x11开始,然后是数据集长度数据。数据字段:

例如:

OSM XML格式的例子:

<way id="3999478">
  <nd ref="20958823"/>
  <nd ref="20973902"/>
<tag k="highway" v="secondary" />
</way>

<h3 id="2.5">2.5 Relation</h3>
每个关系数据集从0x12开始,然后是数据集长度数据。数据字段:

例如:

OSM XML格式的例子:

<relation id="2952">
  <member type="way" ref="11560506" role="inner"/>
  <member type="way" ref="25873183" role="inner"/>
  <tag k="type" v="multipolygon" />
</relation>

<h3 id="2.6">2.6 File Timestamp</h3>
这是一个可选的数据集。它以0xdc开始,然后是文件的时间戳。该单位从1970年1月1日开始的秒数:

如果该数据集用于文件,那么它必须存储在任何OSM对象之前,即在任何节点、路线或关系之前。

<h3 id="2.7">2.7 Bounding Box</h3>
这个(可选的)数据集从0xdb开始,然后是数据集长度边界框坐标

类似于文件时间戳(见上面),必须在任何OSM对象之前(即在任何节点、路线或关系之前)存储该文件。

<h3 id="2.8">2.8 Reset</h3>
重置字节0xff告诉编码器重新设置计数器。此时,每个增量编码将从0开始,没有使用字符串引用,这将在重置字节之前引用文件内容。

这个机制是它允许.o5m文件通过不同的线程并行处理。通常,至少开始的路线和关系部分将由重置字节开始。

注意,0xff不启动数据集;因此,没有长度信息会跟随0xff

<h3 id="2.9">2.9 Sync</h3>
如果您需要将OSM数据作为流处理,您将需要一些您可以同步的位置。这些同步点在解析32位零的数据流时可以找到它们。每个同步数据集必须有一个重置字节。否则,如果使用增量编码,您将无法解码随后存储的OSM数据集。语法如下:

如果开发一个不使用这个同步机制的解析器程序,您不需要关心同步数据集,因为这些数据集将被识别为“未知数据集”,因此被忽略。

<h3 id="2.10">2.10 Jump</h3>
要获得随机访问,而不仅仅是访问其中一个节的开头(节点的开始,路线的开始,关系的开始),你可以定义额外的跳转点作为跳转数据集。这些跳转点允许您快速地在文件中向前和向后移动。

跳转数据集以0xef开始,然后是数据集长度数据。数据字段:

每个跳转数据集必须接着一个重置字节。
否则,如果使用增量编码,您将无法解码随后存储的OSM数据集。例如:

如果开发一个不使用这个跳转机制的解析器程序,您不需要关心跳转数据集,因为这些数据集将被识别为“未知数据集”,因此被忽略。

<h2 id="3">3.File Size Comparison</h2>
OSM数据文件的大小取决于所使用的格式。来自geofabrik.de的2011- 5 -12德国文件被用作这种比较的基础。压缩算法.gz和.7z是用默认参数(中等压缩强度)完成的。pbf使用内部的zlib压缩。

OSM File Format and Compression Type Size in Bytes Relative Size Reading Time (slow computer)
germany.osm 15,519,707,799 100.0 % 604 s
germany.osm.bz2 1,442,403,577 9.3 %
germany.o5m 1,469,972,938 9.5 % 36 s
germany.o5m.gz 949,544,868 6.1 %
germany.o5m.7z 851,845,615 5.5 %
germany.pbf 948,980,117 6.1 % 90 s

如果您丢弃元数据(时间戳、用户名等),文件大小将减少:

OSM File Format and Compression Type Size in Bytes Relative Size Reading Time (slow computer)
germany.osm (excl. meta data) 7,937,414,195 51.1 %
germany.o5m (excl. meta data) 1,046,676,637 6.7 %
germany.o5m.gz (excl. meta data) 730,187,675 4.7 %
germany.o5m.7z (excl. meta data) 647,320,555 4.2 %
germany.pbf (excl. meta data) 697,041,975 4.5 %

<h2 id="4">4.Further Information</h2>

<h3 id="4.1">4.1 Why that strange Name?</h3>
之所以选择o5m,是因为它看起来有点像osm。数字5代表“比。osm小5倍”。同时,我们知道因子是10,而不是5。但是,o10m的声音会有多傻呢?

<h3 id="4.2">4.2 .5c as OSM Change Format</h3>
你很容易使用.o5m格式来存储OSM更改文件。文件的数据结构没有区别,只有一个例外:文件头包含“o5c2”而不是“o5m2”。对于文件名,建议使用扩展名.o5c代替.o5m。

我们如何处理<create> , <modify>和<delete> 这些在.osc格式中是众所周知的标签呢?好吧,这些标签并没有真正的目的——除非你想用它们来进行貌似可信的检查。创建修改产生相同的操作结果:所引用对象的新版本将被存储;所以我们简单地把对象的版本与最新的更改文件一起存储并保存。删除是一个特殊的操作,我们将不得不定义自己的.o5c对象类型。 但是,要删除一个对象,我们只需要它的id就可以了。 因此我们决定,如果文件中没有任何内容存储在对象中(除了它的id)(也可能是它的版本或作者信息),这意味着这个id引用的对象将被删除。

<h3 id="4.3">4.3 Future Extensions</h3>
如果由于新的OSM api,数据格式需要额外的需求,我们会怎么做?这不应该是个问题。有239个o5m数据集id(范围0x01到0xdf);目前,只有3个在使用:0x10用于节点,0x11用于路线,0x12用于关系。Varint数格式和字符串格式也可以用于任何目的。

<h3 id="4.4">4.4 Is there an Example Implementation?</h3>

文件格式已被完全描述,因此您可以使用任何您喜欢的编程语言从零开始实施.o5m读写器。 不过,看一下.o5m阅读器示例代码(用C语言编写)或osmconvertosmfilter等的源代码可能是有用的。

<h2 id="5">5. Software supporting .o5m</h2>

数据格式非常新,所以这个列表非常短。

<h3 id="5.1"> 5.1 Programs already support .o5m</h3>

<h3 id="5.2"> 5.2 Toolchains using .o5m</h3>

上一篇下一篇

猜你喜欢

热点阅读