// KML 序列化:把文档模型转回 KML 文本。
//
// 输出带 XML 声明、kml 根与命名空间、Document;文本做 XML 转义。
// 这是读写闭环的一半,round-trip 测试会用它配合 parse。
///|
/// 追加一段文本的 XML 转义结果到字节数组。
fn push_escaped(out : Array[Byte], text : String) -> Unit {
let data = @utf8.encode(text)
let mut i = 0
while i < data.length() {
let b = data[i]
if b == b'&' {
push_lit(out, "&")
} else if b == b'<' {
push_lit(out, "<")
} else if b == b'>' {
push_lit(out, ">")
} else if b == b'"' {
push_lit(out, """)
} else if b == b'\'' {
push_lit(out, "'")
} else {
out.push(b)
}
i = i + 1
}
}
///|
/// 追加字面量字符串的 UTF-8 字节。
fn push_lit(out : Array[Byte], text : String) -> Unit {
let data = @utf8.encode(text)
let mut i = 0
while i < data.length() {
out.push(data[i])
i = i + 1
}
}
///|
/// 追加换行。
fn push_newline(out : Array[Byte]) -> Unit {
out.push(b'\n')
}
///|
/// 序列化一个坐标点。
fn push_point_coords(out : Array[Byte], p : KmlPoint) -> Unit {
push_double(out, p.lon)
out.push(b',')
push_double(out, p.lat)
match p.alt {
Some(a) => {
out.push(b',')
push_double(out, a)
}
None => ()
}
}
///|
/// 追加双精度浮点:用最短可往返的 to_string。
fn push_double(out : Array[Byte], value : Double) -> Unit {
push_lit(out, value.to_string())
}
///|
/// 序列化一个点列(LineString 或 LinearRing 共用)。
fn push_points(out : Array[Byte], points : Array[KmlPoint]) -> Unit {
let mut i = 0
while i < points.length() {
push_point_coords(out, points[i])
if i + 1 < points.length() {
push_lit(out, " ")
}
i = i + 1
}
}
///|
/// 序列化 Point。
fn serialize_point(out : Array[Byte], p : KmlPoint) -> Unit {
push_lit(out, "")
push_point_coords(out, p)
push_lit(out, "")
}
///|
/// 序列化 LineString。
fn serialize_line_string(out : Array[Byte], ls : KmlLineString) -> Unit {
push_lit(out, "")
push_points(out, ls.points)
push_lit(out, "")
}
///|
/// 序列化 LinearRing。
fn serialize_ring(out : Array[Byte], ring : LinearRing) -> Unit {
push_lit(out, "")
push_points(out, ring.points)
push_lit(out, "")
}
///|
/// 序列化 Polygon。
fn serialize_polygon(out : Array[Byte], poly : KmlPolygon) -> Unit {
push_lit(out, "")
serialize_ring(out, poly.outer)
push_lit(out, "")
let mut i = 0
while i < poly.inner.length() {
push_lit(out, "")
serialize_ring(out, poly.inner[i])
push_lit(out, "")
i = i + 1
}
push_lit(out, "")
}
///|
/// 序列化一个 Geometry。
fn serialize_geometry(out : Array[Byte], geometry : Geometry) -> Unit {
match geometry {
Geometry::Point(p) => serialize_point(out, p)
Geometry::LineString(ls) => serialize_line_string(out, ls)
Geometry::Polygon(poly) => serialize_polygon(out, poly)
Geometry::MultiGeometry(geoms) => {
push_lit(out, "")
let mut i = 0
while i < geoms.length() {
serialize_geometry(out, geoms[i])
i = i + 1
}
push_lit(out, "")
}
Geometry::GxTrack(track) => {
push_lit(out, "")
let mut i = 0
while i < track.whens.length() {
push_lit(out, "")
push_escaped(out, track.whens[i])
push_lit(out, "")
i = i + 1
}
let mut k = 0
while k < track.coords.length() {
let p = track.coords[k]
push_lit(out, "")
push_double(out, p.lon)
push_lit(out, " ")
push_double(out, p.lat)
push_lit(out, " ")
match p.alt {
Some(a) => push_double(out, a)
None => push_lit(out, "0")
}
push_lit(out, "")
k = k + 1
}
push_lit(out, "")
}
}
}
///|
/// 序列化一个 Placemark。
fn serialize_placemark(out : Array[Byte], pm : Placemark) -> Unit {
push_lit(out, "")
if pm.name != "" {
push_lit(out, "")
push_escaped(out, pm.name)
push_lit(out, "")
}
if pm.description != "" {
push_lit(out, "")
push_escaped(out, pm.description)
push_lit(out, "")
}
if pm.style_url != "" {
push_lit(out, "")
push_escaped(out, pm.style_url)
push_lit(out, "")
}
match pm.timestamp {
Some(t) =>
if t.when != "" {
push_lit(out, "")
push_escaped(out, t.when)
push_lit(out, "")
} else {
push_lit(out, "")
if t.begin != "" {
push_lit(out, "")
push_escaped(out, t.begin)
push_lit(out, "")
}
if t.end != "" {
push_lit(out, "")
push_escaped(out, t.end)
push_lit(out, "")
}
push_lit(out, "")
}
None => ()
}
if pm.extended_data.length() > 0 {
push_lit(out, "")
let mut k = 0
while k < pm.extended_data.length() {
let d = pm.extended_data[k]
push_lit(out, "")
push_escaped(out, d.value)
push_lit(out, "")
k = k + 1
}
push_lit(out, "")
}
match pm.geometry {
Some(g) => serialize_geometry(out, g)
None => ()
}
push_lit(out, "")
}
///|
/// 把 KML 文档序列化成文本。
pub fn serialize(doc : KmlDocument) -> String {
let out : Array[Byte] = []
push_lit(out, "")
push_newline(out)
push_lit(out, "")
push_newline(out)
push_lit(out, "")
if doc.name != "" {
push_newline(out)
push_lit(out, "")
push_escaped(out, doc.name)
push_lit(out, "")
}
let mut i = 0
while i < doc.styles.length() {
push_newline(out)
let st = doc.styles[i]
push_lit(out, "")
i = i + 1
}
let mut sm = 0
while sm < doc.style_maps.length() {
push_newline(out)
let map = doc.style_maps[sm]
push_lit(out, "normal")
push_escaped(out, map.normal)
push_lit(out, "highlight")
push_escaped(out, map.highlight)
push_lit(out, "")
sm = sm + 1
}
let mut i = 0
while i < doc.placemarks.length() {
push_newline(out)
serialize_placemark(out, doc.placemarks[i])
i = i + 1
}
push_newline(out)
push_lit(out, "")
push_newline(out)
push_lit(out, "")
@utf8.decode_lossy(Bytes::from_array(out))
}