@maphubs/tokml
Version:
convert geojson to kml
490 lines (438 loc) • 12.6 kB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.tokml = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
var esc = require('./lib/xml-escape')
var strxml = require('./lib/strxml'),
tag = strxml.tag
module.exports = function tokml(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',
{ xmlns: 'http://www.opengis.net/kml/2.2' },
tag(
'Document',
documentName(options) +
documentDescription(options) +
root(geojson, options)
)
)
)
}
function feature(options, styleHashesArray) {
return function (_) {
if (!_.properties || !geometry.valid(_.geometry)) return ''
var geometryString = geometry.any(_.geometry)
if (!geometryString) return ''
var styleDefinition = '',
styleReference = ''
if (options.simplestyle) {
var styleHash = hashStyle(_.properties)
if (styleHash) {
if (geometry.isPoint(_.geometry) && hasMarkerStyle(_.properties)) {
if (styleHashesArray.indexOf(styleHash) === -1) {
styleDefinition = markerStyle(_.properties, styleHash)
styleHashesArray.push(styleHash)
}
styleReference = tag('styleUrl', '#' + styleHash)
removeMarkerStyle(_.properties)
} else if (
(geometry.isPolygon(_.geometry) || geometry.isLine(_.geometry)) &&
hasPolygonAndLineStyle(_.properties)
) {
if (styleHashesArray.indexOf(styleHash) === -1) {
styleDefinition = polygonAndLineStyle(_.properties, styleHash)
styleHashesArray.push(styleHash)
}
styleReference = tag('styleUrl', '#' + styleHash)
removePolygonAndLineStyle(_.properties)
}
// Note that style of GeometryCollection / MultiGeometry is not supported
}
}
var attributes = {}
if (_.id) attributes.id = _.id.toString();
return (
styleDefinition +
tag(
'Placemark',
attributes,
name(_.properties, options) +
description(_.properties, options) +
extendeddata(_.properties) +
timestamp(_.properties, options) +
geometryString +
styleReference
)
)
}
}
function root(_, options) {
if (!_.type) return ''
var styleHashesArray = []
switch (_.type) {
case 'FeatureCollection':
if (!_.features) return ''
return _.features.map(feature(options, styleHashesArray)).join('')
case 'Feature':
return feature(options, styleHashesArray)(_)
default:
return feature(
options,
styleHashesArray
)({
type: 'Feature',
geometry: _,
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(_, options) {
return _[options.name] ? tag('name', esc(_[options.name])) : ''
}
function description(_, options) {
return _[options.description]
? tag('description', esc(_[options.description]))
: ''
}
function timestamp(_, options) {
return _[options.timestamp]
? tag('TimeStamp', tag('when', esc(_[options.timestamp])))
: ''
}
// ## Geometry Types
//
// https://developers.google.com/kml/documentation/kmlreference#geometry
var geometry = {
Point: function (_) {
return tag('Point', tag('coordinates', _.coordinates.join(',')))
},
LineString: function (_) {
return tag('LineString', tag('coordinates', linearring(_.coordinates)))
},
Polygon: function (_) {
if (!_.coordinates.length) return ''
var outer = _.coordinates[0],
inner = _.coordinates.slice(1),
outerRing = tag(
'outerBoundaryIs',
tag('LinearRing', tag('coordinates', linearring(outer)))
),
innerRings = inner
.map(function (i) {
return tag(
'innerBoundaryIs',
tag('LinearRing', tag('coordinates', linearring(i)))
)
})
.join('')
return tag('Polygon', outerRing + innerRings)
},
MultiPoint: function (_) {
if (!_.coordinates.length) return ''
return tag(
'MultiGeometry',
_.coordinates
.map(function (c) {
return geometry.Point({ coordinates: c })
})
.join('')
)
},
MultiPolygon: function (_) {
if (!_.coordinates.length) return ''
return tag(
'MultiGeometry',
_.coordinates
.map(function (c) {
return geometry.Polygon({ coordinates: c })
})
.join('')
)
},
MultiLineString: function (_) {
if (!_.coordinates.length) return ''
return tag(
'MultiGeometry',
_.coordinates
.map(function (c) {
return geometry.LineString({ coordinates: c })
})
.join('')
)
},
GeometryCollection: function (_) {
return tag('MultiGeometry', _.geometries.map(geometry.any).join(''))
},
valid: function (_) {
return (
_ &&
_.type &&
(_.coordinates ||
(_.type === 'GeometryCollection' &&
_.geometries &&
_.geometries.every(geometry.valid)))
)
},
any: function (_) {
if (geometry[_.type]) {
return geometry[_.type](_)
} else {
return ''
}
},
isPoint: function (_) {
return _.type === 'Point' || _.type === 'MultiPoint'
},
isPolygon: function (_) {
return _.type === 'Polygon' || _.type === 'MultiPolygon'
},
isLine: function (_) {
return _.type === 'LineString' || _.type === 'MultiLineString'
}
}
function linearring(_) {
return _.map(function (cds) {
return cds.join(',')
}).join(' ')
}
// ## Data
function extendeddata(_) {
return tag('ExtendedData', {}, pairs(_).map(data).join(''))
}
function data(_) {
return tag(
'Data',
{ name: _[0] },
tag('value', {}, esc(_[1] ? _[1].toString() : ''))
)
}
// ## Marker style
function hasMarkerStyle(_) {
return !!(_['marker-size'] || _['marker-symbol'] || _['marker-color'])
}
function removeMarkerStyle(_) {
delete _['marker-size']
delete _['marker-symbol']
delete _['marker-color']
delete _['marker-shape']
}
function markerStyle(_, styleHash) {
return tag(
'Style',
{ id: styleHash },
tag('IconStyle', tag('Icon', tag('href', iconUrl(_)))) + iconSize(_)
)
}
function iconUrl(_) {
var size = _['marker-size'] || 'medium',
symbol = _['marker-symbol'] ? '-' + _['marker-symbol'] : '',
color = (_['marker-color'] || '7e7e7e').replace('#', '')
return (
'https://api.tiles.mapbox.com/v3/marker/' +
'pin-' +
size.charAt(0) +
symbol +
'+' +
color +
'.png'
)
}
function iconSize(_) {
return tag(
'hotSpot',
{
xunits: 'fraction',
yunits: 'fraction',
x: '0.5',
y: '0.5'
},
''
)
}
// ## Polygon and Line style
function hasPolygonAndLineStyle(_) {
for (var key in _) {
if (
{
stroke: true,
'stroke-opacity': true,
'stroke-width': true,
fill: true,
'fill-opacity': true
}[key]
)
return true
}
}
function removePolygonAndLineStyle(_) {
delete _['stroke']
delete _['stroke-opacity']
delete _['stroke-width']
delete _['fill']
delete _['fill-opacity']
}
function polygonAndLineStyle(_, styleHash) {
var lineStyle = tag(
'LineStyle',
tag(
'color',
hexToKmlColor(_['stroke'], _['stroke-opacity']) || 'ff555555'
) +
tag('width', {}, _['stroke-width'] === undefined ? 2 : _['stroke-width'])
)
var polyStyle = ''
if (_['fill'] || _['fill-opacity']) {
polyStyle = tag(
'PolyStyle',
tag(
'color',
{},
hexToKmlColor(_['fill'], _['fill-opacity']) || '88555555'
)
)
}
return tag('Style', { id: styleHash }, lineStyle + polyStyle)
}
// ## Style helpers
function hashStyle(_) {
var hash = ''
if (_['marker-symbol']) hash = hash + 'ms' + _['marker-symbol']
if (_['marker-color']) hash = hash + 'mc' + _['marker-color'].replace('#', '')
if (_['marker-size']) hash = hash + 'ms' + _['marker-size']
if (_['stroke']) hash = hash + 's' + _['stroke'].replace('#', '')
if (_['stroke-width'])
hash = hash + 'sw' + _['stroke-width'].toString().replace('.', '')
if (_['stroke-opacity'])
hash = hash + 'mo' + _['stroke-opacity'].toString().replace('.', '')
if (_['fill']) hash = hash + 'f' + _['fill'].replace('#', '')
if (_['fill-opacity'])
hash = hash + 'fo' + _['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 ''
}
var r = hexColor[0] + hexColor[1]
var g = hexColor[2] + hexColor[3]
var b = hexColor[4] + hexColor[5]
var 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
}
// ## General helpers
function pairs(_) {
var o = []
for (var i in _) {
if (_[i]) {
o.push([i, _[i]])
} else {
o.push([i, ''])
}
}
return o
}
},{"./lib/strxml":2,"./lib/xml-escape":3}],2:[function(require,module,exports){
/* istanbul ignore file */
// strxml from https://github.com/mapbox/strxml
var esc = require('./xml-escape')
module.exports.attr = attr
module.exports.tagClose = tagClose
module.exports.tag = tag
/**
* @param {array} _ an array of attributes
* @returns {string}
*/
function attr(attributes) {
if (!Object.keys(attributes).length) return ''
return (
' ' +
Object.keys(attributes)
.map(function (key) {
return key + '="' + esc(attributes[key]) + '"'
})
.join(' ')
)
}
/**
* @param {string} el element name
* @param {array} attributes array of pairs
* @returns {string}
*/
function tagClose(el, attributes) {
return '<' + el + attr(attributes) + '/>'
}
/**
* @param {string} el element name
* @param {string} contents innerXML
* @param {array} attributes array of pairs
* @returns {string}
*/
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 + '>'
}
},{"./xml-escape":3}],3:[function(require,module,exports){
/* istanbul ignore file */
// originally from https://github.com/miketheprogrammer/xml-escape
var escape = (module.exports = function escape(string, ignore) {
var pattern
if (string === null || string === undefined) return
ignore = (ignore || '').replace(/[^&"<>\']/g, '')
pattern = '([&"<>\'])'.replace(new RegExp('[' + ignore + ']', 'g'), '')
return string.replace(new RegExp(pattern, 'g'), function (str, item) {
return escape.map[item]
})
})
var map = (escape.map = {
'>': '>',
'<': '<',
"'": ''',
'"': '"',
'&': '&'
})
},{}]},{},[1])(1)
});