kml-geojson
Version:
kml与geojson互转工具类
416 lines (373 loc) • 10.9 kB
JavaScript
// geojson => kml
export function geoJSONToKml(geojson, options) {
options = options || {
documentName: undefined,
documentDescription: undefined,
name: "name",
description: "description",
simplestyle: false,
timestamp: "timestamp"
}
return (
'<?xml version="1.0" encoding="UTF-8"?>' +
tag("kml", tag("Document", documentName(options) + documentDescription(options) + root(geojson, options)), [
["xmlns", "http://www.opengis.net/kml/2.2"]
])
)
}
function feature(options, styleHashesArray) {
return function (attr) {
if (!attr.properties || !geometry.valid(attr.geometry)) {
return ""
}
const geometryString = geometry.any(attr.geometry)
if (!geometryString) {
return ""
}
let styleDefinition = ""
let styleReference = ""
if (options.simplestyle) {
const styleHash = hashStyle(attr.properties)
if (styleHash) {
if (geometry.isPoint(attr.geometry) && hasMarkerStyle(attr.properties)) {
if (styleHashesArray.indexOf(styleHash) === -1) {
styleDefinition = markerStyle(attr.properties, styleHash)
styleHashesArray.push(styleHash)
}
styleReference = tag("styleUrl", "#" + styleHash)
} else if ((geometry.isPolygon(attr.geometry) || geometry.isLine(attr.geometry)) && hasPolygonAndLineStyle(attr.properties)) {
if (styleHashesArray.indexOf(styleHash) === -1) {
styleDefinition = polygonAndLineStyle(attr.properties, styleHash)
styleHashesArray.push(styleHash)
}
styleReference = tag("styleUrl", "#" + styleHash)
}
// Note that style of GeometryCollection / MultiGeometry is not supported
}
}
return (
styleDefinition +
tag(
"Placemark",
name(attr.properties, options) +
description(attr.properties, options) +
extendeddata(attr.properties) +
timestamp(attr.properties, options) +
geometryString +
styleReference
)
)
}
}
function root(attr, options) {
if (!attr.type) {
return ""
}
const styleHashesArray = []
switch (attr.type) {
case "FeatureCollection":
if (!attr.features) {
return ""
}
return attr.features.map(feature(options, styleHashesArray)).join("")
case "Feature":
return feature(options, styleHashesArray)(attr)
default:
return feature(
options,
styleHashesArray
)({
type: "Feature",
geometry: attr,
properties: {}
})
}
}
function documentName(options) {
return options.documentName !== undefined ? tag("name", options.documentName) : ""
}
function documentDescription(options) {
return options.documentDescription !== undefined ? tag("description", options.documentDescription) : ""
}
function name(attr, options) {
return attr[options.name] ? tag("name", encode(attr[options.name])) : ""
}
function description(attr, options) {
return attr[options.description] ? tag("description", encode(attr[options.description])) : ""
}
function timestamp(attr, options) {
return attr[options.timestamp] ? tag("TimeStamp", tag("when", encode(attr[options.timestamp]))) : ""
}
// ## Geometry Types
//
// https://developers.google.com/kml/documentation/kmlreference#geometry
const geometry = {
Point: function (attr) {
return tag("Point", tag("coordinates", attr.coordinates.join(",")))
},
LineString: function (attr) {
return tag("LineString", tag("coordinates", linearring(attr.coordinates)))
},
Polygon: function (attr) {
if (!attr.coordinates.length) {
return ""
}
const outer = attr.coordinates[0]
const inner = attr.coordinates.slice(1)
const outerRing = tag("outerBoundaryIs", tag("LinearRing", tag("coordinates", linearring(outer))))
const innerRings = inner
.map(function (i) {
return tag("innerBoundaryIs", tag("LinearRing", tag("coordinates", linearring(i))))
})
.join("")
return tag("Polygon", outerRing + innerRings)
},
MultiPoint: function (attr) {
if (!attr.coordinates.length) {
return ""
}
return tag(
"MultiGeometry",
attr.coordinates
.map(function (c) {
return geometry.Point({ coordinates: c })
})
.join("")
)
},
MultiPolygon: function (attr) {
if (!attr.coordinates.length) {
return ""
}
return tag(
"MultiGeometry",
attr.coordinates
.map(function (c) {
return geometry.Polygon({ coordinates: c })
})
.join("")
)
},
MultiLineString: function (attr) {
if (!attr.coordinates.length) {
return ""
}
return tag(
"MultiGeometry",
attr.coordinates
.map(function (c) {
return geometry.LineString({ coordinates: c })
})
.join("")
)
},
GeometryCollection: function (attr) {
return tag("MultiGeometry", attr.geometries.map(geometry.any).join(""))
},
valid: function (attr) {
return attr && attr.type && (attr.coordinates || (attr.type === "GeometryCollection" && attr.geometries && attr.geometries.every(geometry.valid)))
},
any: function (attr) {
if (geometry[attr.type]) {
return geometry[attr.type](attr)
} else {
return ""
}
},
isPoint: function (attr) {
return attr.type === "Point" || attr.type === "MultiPoint"
},
isPolygon: function (attr) {
return attr.type === "Polygon" || attr.type === "MultiPolygon"
},
isLine: function (attr) {
return attr.type === "LineString" || attr.type === "MultiLineString"
}
}
function linearring(attr) {
return attr
.map(function (cds) {
return cds.join(",")
})
.join(" ")
}
// ## Data
function extendeddata(attr) {
const arr = []
for (const i in attr) {
const val = attr[i]
if (isObject(val)) {
arr.push(`<Data name ="${i}"><value>${JSON.stringify(val)}</value></Data>`)
} else {
arr.push(`<Data name ="${i}"><value>${val}</value></Data>`)
}
}
return tag("ExtendedData", arr.join(""))
}
function data(attr) {
return tag("Data", tag("value", encode(attr[1])), [["name", encode(attr[0])]])
}
// ## Marker style
function hasMarkerStyle(attr) {
return !!(attr["marker-size"] || attr["marker-symbol"] || attr["marker-color"])
}
function markerStyle(attr, styleHash) {
return tag("Style", tag("IconStyle", tag("Icon", tag("href", iconUrl(attr)))) + iconSize(attr), [["id", styleHash]])
}
function iconUrl(attr) {
return attr["marker-symbol"]
// let size = attr['marker-size'] || 'medium',
// symbol = attr['marker-symbol'] ? '-' + attr['marker-symbol'] : '',
// color = (attr['marker-color'] || '7e7e7e').replace('#', '')
// return 'https://api.tiles.mapbox.com/v3/marker/' + 'pin-' + size.charAt(0) + symbol + '+' + color + '.png'
}
function iconSize(attr) {
return tag("hotSpot", "", [
["xunits", "fraction"],
["yunits", "fraction"],
["x", 0.5],
["y", 0.5]
])
}
// ## Polygon and Line style
function hasPolygonAndLineStyle(attr) {
for (const key in attr) {
if (
{
stroke: true,
"stroke-opacity": true,
"stroke-width": true,
fill: true,
"fill-opacity": true
}[key]
) {
return true
}
}
}
function polygonAndLineStyle(attr, styleHash) {
const lineStyle = tag("LineStyle", [
tag("color", hexToKmlColor(attr.stroke, attr["stroke-opacity"]) || "ff555555") +
tag("width", attr["stroke-width"] === undefined ? 2 : attr["stroke-width"])
])
let polyStyle = ""
if (attr.fill || attr["fill-opacity"]) {
polyStyle = tag("PolyStyle", [tag("color", hexToKmlColor(attr.fill, attr["fill-opacity"]) || "88555555")])
}
return tag("Style", lineStyle + polyStyle, [["id", styleHash]])
}
// ## Style helpers
function hashStyle(attr) {
let hash = ""
if (attr["marker-symbol"]) {
hash = hash + "ms" + attr["marker-symbol"]
}
if (attr["marker-color"]) {
hash = hash + "mc" + attr["marker-color"].replace("#", "")
}
if (attr["marker-size"]) {
hash = hash + "ms" + attr["marker-size"]
}
if (attr.stroke) {
hash = hash + "s" + attr.stroke.replace("#", "")
}
if (attr["stroke-width"]) {
hash = hash + "sw" + attr["stroke-width"].toString().replace(".", "")
}
if (attr["stroke-opacity"]) {
hash = hash + "mo" + attr["stroke-opacity"].toString().replace(".", "")
}
if (attr.fill) {
hash = hash + "f" + attr.fill.replace("#", "")
}
if (attr["fill-opacity"]) {
hash = hash + "fo" + attr["fill-opacity"].toString().replace(".", "")
}
return hash
}
function hexToKmlColor(hexColor, opacity) {
if (typeof hexColor !== "string") {
return ""
}
hexColor = hexColor.replace("#", "").toLowerCase()
if (hexColor.length === 3) {
hexColor = hexColor[0] + hexColor[0] + hexColor[1] + hexColor[1] + hexColor[2] + hexColor[2]
} else if (hexColor.length !== 6) {
return ""
}
const r = hexColor[0] + hexColor[1]
const g = hexColor[2] + hexColor[3]
const b = hexColor[4] + hexColor[5]
let o = "ff"
if (typeof opacity === "number" && opacity >= 0.0 && opacity <= 1.0) {
o = (opacity * 255).toString(16)
if (o.indexOf(".") > -1) {
o = o.substr(0, o.indexOf("."))
}
if (o.length < 2) {
o = "0" + o
}
}
return o + b + g + r
}
/**
* 判断对象是否为Object类型
* @param {*} obj 对象
* @returns {Boolean} 是否为Object类型
*/
export function isObject(obj) {
return Object.prototype.toString.call(obj) === "[object Object]"
}
function encode(attr) {
if (!attr) {
return ""
}
return attr.toString().replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """)
}
function attr(attributes) {
if (!Object.keys(attributes).length) {
return ""
}
return (
" " +
Object.keys(attributes)
.map(function (key) {
return key + '="' + escape(attributes[key]) + '"'
})
.join(" ")
)
}
const escape_map = {
">": ">",
"<": "<",
"'": "'",
'"': """,
"&": "&"
}
function escape(string, ignore) {
if (string === null || string === undefined) {
return
}
ignore = (ignore || "").replace(/[^&"<>\']/g, "")
const pattern = "([&\"<>'])".replace(new RegExp("[" + ignore + "]", "g"), "")
return string.replace(new RegExp(pattern, "g"), function (str, item) {
return escape_map[item]
})
}
function tag(el, attributes, contents) {
if (Array.isArray(attributes) || typeof attributes === "string") {
contents = attributes
attributes = {}
}
if (Array.isArray(contents)) {
contents =
"\n" +
contents
.map(function (content) {
return " " + content
})
.join("\n") +
"\n"
}
return "<" + el + attr(attributes) + ">" + contents + "</" + el + ">"
}