Merge pull request #1368 from twpayne/vector-api-kml

[vector-api] ol.format.KML
This commit is contained in:
Tom Payne
2013-12-19 09:07:43 -08:00
23 changed files with 17978 additions and 70 deletions

File diff suppressed because one or more lines are too long

View File

@@ -104,13 +104,13 @@
</Style>
<Folder>
<name>Paths</name>
<visibility>0</visibility>
<visibility>1</visibility>
<description>Examples of paths. Note that the tessellate tag is by default
set to 0. If you want to create tessellated lines, they must be authored
(or edited) directly in KML.</description>
<Placemark>
<name>Tessellated</name>
<visibility>0</visibility>
<visibility>1</visibility>
<description><![CDATA[If the <tessellate> tag has a value of 1, the line will contour to the underlying terrain]]></description>
<LookAt>
<longitude>-112.0822680013139</longitude>
@@ -128,7 +128,7 @@
</Placemark>
<Placemark>
<name>Untessellated</name>
<visibility>0</visibility>
<visibility>1</visibility>
<description><![CDATA[If the <tessellate> tag has a value of 0, the line follow a simple straight-line path from point to point]]></description>
<LookAt>
<longitude>-112.0822680013139</longitude>
@@ -146,7 +146,7 @@
</Placemark>
<Placemark>
<name>Absolute</name>
<visibility>0</visibility>
<visibility>1</visibility>
<description>Transparent purple line</description>
<LookAt>
<longitude>-112.2719329043177</longitude>
@@ -175,7 +175,7 @@
</Placemark>
<Placemark>
<name>Absolute Extruded</name>
<visibility>0</visibility>
<visibility>1</visibility>
<description>Transparent green wall with yellow outlines</description>
<LookAt>
<longitude>-112.2643334742529</longitude>
@@ -205,7 +205,7 @@
</Placemark>
<Placemark>
<name>Relative</name>
<visibility>0</visibility>
<visibility>1</visibility>
<description>Black line (10 pixels wide), height tracks terrain</description>
<LookAt>
<longitude>-112.2580438551384</longitude>
@@ -234,7 +234,7 @@
</Placemark>
<Placemark>
<name>Relative Extruded</name>
<visibility>0</visibility>
<visibility>1</visibility>
<description>Opaque blue walls with red outline, height tracks terrain</description>
<LookAt>
<longitude>-112.2683594333433</longitude>
@@ -265,7 +265,7 @@
<Placemark>
<name>Blue Icon</name>
<description>Just another blue icon.</description>
<styleUrl>data/kml/styles.kml#blueIcons</styleUrl>
<styleUrl>styles.kml#blueIcons</styleUrl>
<Point>
<coordinates>-112.292238941097,36.09520916122063,630</coordinates>
</Point>
@@ -273,7 +273,7 @@
<Placemark>
<name>Sun Icon</name>
<description>Just another sun icon.</description>
<styleUrl>data/kml/styles.kml#sunIconMap</styleUrl>
<styleUrl>styles.kml#sunIconMap</styleUrl>
<Point>
<coordinates>-112.292238941097,36.15520916122063,630</coordinates>
</Point>

View File

@@ -0,0 +1,56 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
<link rel="stylesheet" href="../css/ol.css" type="text/css">
<link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
<link rel="stylesheet" href="../resources/layout.css" type="text/css">
<link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
<title>Drag-and-Drop example</title>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div id="map" class="map"></div>
</div>
</div>
<div class="row-fluid">
<div class="span4">
<h4 id="title">Drag-and-Drop example</h4>
<p id="shortdesc">Example of using the drag-and-drop interaction. Drag and drop KML files on to the map.</p>
<div id="docs">
<p>See the <a href="drag-and-drop.js" target="_blank">drag-and-drop.js source</a> to see how this is done.</p>
</div>
<div id="tags">drag-and-drop, kml</div>
</div>
<div class="span4 offset4">
<div id="info" class="alert alert-success">
&nbsp;
</div>
</div>
</div>
</div>
<script src="jquery.min.js" type="text/javascript"></script>
<script src="loader.js?id=drag-and-drop" type="text/javascript"></script>
<script src="../resources/example-behaviour.js" type="text/javascript"></script>
</body>
</html>

60
examples/drag-and-drop.js Normal file
View File

@@ -0,0 +1,60 @@
goog.require('ol.Map');
goog.require('ol.RendererHint');
goog.require('ol.View2D');
goog.require('ol.format.KML');
goog.require('ol.interaction');
goog.require('ol.interaction.DragAndDrop');
goog.require('ol.layer.Tile');
goog.require('ol.source.BingMaps');
var map = new ol.Map({
interactions: ol.interaction.defaults().extend([
new ol.interaction.DragAndDrop({
formatConstructors: [
ol.format.KML
]
})
]),
layers: [
new ol.layer.Tile({
source: new ol.source.BingMaps({
imagerySet: 'Aerial',
key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3'
})
})
],
renderer: ol.RendererHint.CANVAS,
target: 'map',
view: new ol.View2D({
center: [0, 0],
zoom: 2
})
});
var displayFeatureInfo = function(pixel) {
var features = [];
map.forEachFeatureAtPixel(pixel, function(feature, layer) {
features.push(feature);
});
if (features.length > 0) {
var info = [];
var i, ii;
for (i = 0, ii = features.length; i < ii; ++i) {
info.push(features[i].get('name'));
}
document.getElementById('info').innerHTML = info.join(', ') || '&nbsp';
} else {
document.getElementById('info').innerHTML = '&nbsp;';
}
};
$(map.getViewport()).on('mousemove', function(evt) {
var pixel = map.getEventPixel(evt.originalEvent);
displayFeatureInfo(pixel);
});
map.on('singleclick', function(evt) {
var pixel = evt.getPixel();
displayFeatureInfo(pixel);
});

View File

@@ -0,0 +1,75 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
<link rel="stylesheet" href="../css/ol.css" type="text/css">
<link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
<link rel="stylesheet" href="../resources/layout.css" type="text/css">
<link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
<title>Earthquakes in KML</title>
<style>
#map {
position: relative;
}
#info {
position: absolute;
height: 1px;
width: 1px;
z-index: 100;
}
.tooltip.in {
opacity: 1;
filter: alpha(opacity=100);
}
.tooltip.top .tooltip-arrow {
border-top-color: white;
}
.tooltip-inner {
border: 2px solid white;
}
</style>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div id="map" class="map"><div id="info"></div></div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<h4 id="title">Earthquakes in KML</h4>
<p id="shortdesc">Demonstrates the use of a Shape symbolizer to render earthquake locations.</p>
<div id="docs">
<p>
This example parses a KML file and renders the features as a vector layer. The layer is given a <code>styleFunction</code> that renders earthquake locations with a size relative to their magnitude.
</p>
<p>See the <a href="kml-earthquakes.js" target="_blank">kml-earthquakes.js source</a> to see how this is done.</p>
</div>
<div id="tags">KML, vector, style</div>
</div>
</div>
</div>
<script src="jquery.min.js" type="text/javascript"></script>
<script src="../resources/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<script src="loader.js?id=kml-earthquakes" type="text/javascript"></script>
<script src="../resources/example-behaviour.js" type="text/javascript"></script>
</body>
</html>

View File

@@ -0,0 +1,96 @@
goog.require('ol.Map');
goog.require('ol.RendererHint');
goog.require('ol.View2D');
goog.require('ol.layer.Tile');
goog.require('ol.layer.Vector');
goog.require('ol.source.KML');
goog.require('ol.source.Stamen');
goog.require('ol.style.Circle');
goog.require('ol.style.Fill');
goog.require('ol.style.Stroke');
goog.require('ol.style.Style');
var styleCache = {};
var styleFunction = function(feature, resolution) {
// 2012_Earthquakes_Mag5.kml stores the magnitude of each earthquake in a
// standards-violating <magnitude> tag in each Placemark. We extract it from
// the Placemark's name instead.
var name = feature.get('name');
var magnitude = parseFloat(name.substr(2));
var radius = 5 + 20 * (magnitude - 5);
var style = styleCache[radius];
if (!style) {
style = [new ol.style.Style({
image: new ol.style.Circle({
radius: radius,
fill: new ol.style.Fill({
color: 'rgba(255, 153, 0, 0.4)'
}),
stroke: new ol.style.Stroke({
color: 'rgba(255, 204, 0, 0.2)',
width: 1
})
})
})];
styleCache[radius] = style;
}
return style;
};
var vector = new ol.layer.Vector({
source: new ol.source.KML({
reprojectTo: 'EPSG:3857',
url: 'data/kml/2012_Earthquakes_Mag5.kml'
}),
styleFunction: styleFunction
});
var raster = new ol.layer.Tile({
source: new ol.source.Stamen({
layer: 'toner'
})
});
var map = new ol.Map({
layers: [raster, vector],
renderer: ol.RendererHint.CANVAS,
target: 'map',
view: new ol.View2D({
center: [0, 0],
zoom: 2
})
});
var info = $('#info');
info.tooltip({
animation: false,
trigger: 'manual'
});
var displayFeatureInfo = function(evt) {
var pixel = map.getEventPixel(evt.originalEvent);
info.css({
left: pixel[0] + 'px',
top: (pixel[1] - 15) + 'px'
});
var feature = map.forEachFeatureAtPixel(pixel, function(feature, layer) {
return feature;
});
if (feature) {
info.tooltip('hide')
.attr('data-original-title', feature.get('name'))
.tooltip('fixTitle')
.tooltip('show');
} else {
info.tooltip('hide');
}
};
$(map.getViewport()).on('mousemove', function(evt) {
displayFeatureInfo(evt);
});
map.on('singleclick', function(evt) {
displayFeatureInfo(evt);
});

View File

@@ -0,0 +1,63 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
<link rel="stylesheet" href="../css/ol.css" type="text/css">
<link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
<link rel="stylesheet" href="../resources/layout.css" type="text/css">
<link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
<title>Timezones in KML</title>
<style>
#map {
position: relative;
}
#info {
position: absolute;
height: 1px;
width: 1px;
z-index: 100;
}
</style>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div id="map" class="map"><div id="info"></div></div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<h4 id="title">Timezones in KML</h4>
<p id="shortdesc">Demonstrates rendering timezones from KML.</p>
<div id="docs">
<p>This example parses a KML file and renders the features as a vector layer. The layer is given a <code>ol.style.Style</code> that fills timezones yellow with an opacity calculated based on the current offset to local noon.</p>
<p>See the <a href="kml-timezones.js" target="_blank">kml-timezones.js source</a> to see how this is done.</p>
</div>
<div id="tags">KML, vector, style</div>
</div>
</div>
</div>
<script src="jquery.min.js" type="text/javascript"></script>
<script src="../resources/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<script src="loader.js?id=kml-timezones" type="text/javascript"></script>
<script src="../resources/example-behaviour.js" type="text/javascript"></script>
</body>
</html>

102
examples/kml-timezones.js Normal file
View File

@@ -0,0 +1,102 @@
goog.require('ol.Map');
goog.require('ol.RendererHint');
goog.require('ol.View2D');
goog.require('ol.layer.Tile');
goog.require('ol.layer.Vector');
goog.require('ol.source.KML');
goog.require('ol.source.Stamen');
goog.require('ol.style.Fill');
goog.require('ol.style.Stroke');
goog.require('ol.style.Style');
/*
* Compute the style of the feature. Here we want the opacity of polygons to
* be based on the offset from local noon. For example, a timezone where it is
* currently noon would have an opacity of 0.75. And a timezone where it is
* currently midnight would have an opacity of 0. This doesn't account for
* daylight savings, so don't use it to plan your vacation.
*/
var styleFunction = function(feature, resolution) {
var offset = 0;
var name = feature.get('name'); // e.g. GMT -08:30
var match = name.match(/([\-+]\d{2}):(\d{2})$/);
if (match) {
var hours = parseInt(match[1], 10);
var minutes = parseInt(match[2], 10);
offset = 60 * hours + minutes;
}
var date = new Date();
var local = new Date(date.getTime() +
(date.getTimezoneOffset() + offset) * 60000);
// offset from local noon (in hours)
var delta = Math.abs(12 - local.getHours() + (local.getMinutes() / 60));
if (delta > 12) {
delta = 24 - delta;
}
var opacity = 0.75 * (1 - delta / 12);
return [new ol.style.Style({
fill: new ol.style.Fill({
color: [0xff, 0xff, 0x33, opacity]
}),
stroke: new ol.style.Stroke({
color: '#ffffff'
})
})];
};
var vector = new ol.layer.Vector({
source: new ol.source.KML({
url: 'data/kml/timezones.kml'
}),
styleFunction: styleFunction
});
var raster = new ol.layer.Tile({
source: new ol.source.Stamen({
layer: 'toner'
})
});
var map = new ol.Map({
layers: [raster, vector],
renderer: ol.RendererHint.CANVAS,
target: 'map',
view: new ol.View2D({
center: [0, 0],
zoom: 2
})
});
var info = $('#info');
info.tooltip({
animation: false,
trigger: 'manual'
});
var displayFeatureInfo = function(evt) {
var pixel = map.getEventPixel(evt.originalEvent);
info.css({
left: pixel[0] + 'px',
top: (pixel[1] - 15) + 'px'
});
var feature = map.forEachFeatureAtPixel(pixel, function(feature, layer) {
return feature;
});
if (feature) {
info.tooltip('hide')
.attr('data-original-title', feature.get('name'))
.tooltip('fixTitle')
.tooltip('show');
} else {
info.tooltip('hide');
}
};
$(map.getViewport()).on('mousemove', function(evt) {
displayFeatureInfo(evt);
});
map.on('singleclick', function(evt) {
displayFeatureInfo(evt);
});

56
examples/kml.html Normal file
View File

@@ -0,0 +1,56 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
<link rel="stylesheet" href="../css/ol.css" type="text/css">
<link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
<link rel="stylesheet" href="../resources/layout.css" type="text/css">
<link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
<title>KML example</title>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div id="map" class="map"></div>
</div>
</div>
<div class="row-fluid">
<div class="span4">
<h4 id="title">KML example</h4>
<p id="shortdesc">Example of using the KML source.</p>
<div id="docs">
<p>See the <a href="kml.js" target="_blank">kml.js source</a> to see how this is done.</p>
</div>
<div id="tags">KML</div>
</div>
<div class="span4 offset4">
<div id="info" class="alert alert-success">
&nbsp;
</div>
</div>
</div>
</div>
<script src="jquery.min.js" type="text/javascript"></script>
<script src="loader.js?id=kml" type="text/javascript"></script>
<script src="../resources/example-behaviour.js" type="text/javascript"></script>
</body>
</html>

58
examples/kml.js Normal file
View File

@@ -0,0 +1,58 @@
goog.require('ol.Map');
goog.require('ol.RendererHint');
goog.require('ol.View2D');
goog.require('ol.layer.Tile');
goog.require('ol.layer.Vector');
goog.require('ol.source.BingMaps');
goog.require('ol.source.KML');
var raster = new ol.layer.Tile({
source: new ol.source.BingMaps({
imagerySet: 'Aerial',
key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3'
})
});
var vector = new ol.layer.Vector({
source: new ol.source.KML({
reprojectTo: 'EPSG:3857',
url: 'data/kml/2012-02-10.kml'
})
});
var map = new ol.Map({
layers: [raster, vector],
renderer: ol.RendererHint.CANVAS,
target: 'map',
view: new ol.View2D({
center: [876970.8463461736, 5859807.853963373],
zoom: 10
})
});
var displayFeatureInfo = function(pixel) {
var features = [];
map.forEachFeatureAtPixel(pixel, function(feature, layer) {
features.push(feature);
});
if (features.length > 0) {
var info = [];
var i, ii;
for (i = 0, ii = features.length; i < ii; ++i) {
info.push(features[i].get('name'));
}
document.getElementById('info').innerHTML = info.join(', ') || '&nbsp';
} else {
document.getElementById('info').innerHTML = '&nbsp;';
}
};
$(map.getViewport()).on('mousemove', function(evt) {
var pixel = map.getEventPixel(evt.originalEvent);
displayFeatureInfo(pixel);
});
map.on('singleclick', function(evt) {
var pixel = evt.getPixel();
displayFeatureInfo(pixel);
});

View File

@@ -260,6 +260,12 @@
* Possible values are `barometric`, `gps`, and `none`. Default is `none`.
*/
/**
* @typedef {Object} olx.format.KMLOptions
* @property {boolean|undefined} extractAttributes Extract attributes.
* @property {boolean|undefined} extractStyles Extract styles.
*/
/**
* @typedef {Object} olx.interaction.DoubleClickZoomOptions
* @property {number|undefined} duration Animation duration in milliseconds. Default is `250`.
@@ -268,6 +274,19 @@
* @todo stability experimental
*/
/**
* @typedef {Object} olx.interaction.DragAndDropOptions
* @property {boolean|undefined} fitView Fit view. Default is `true`.
* @property {Array.<function(new: ol.format.Format)>|undefined}
* formatConstructors Format constructors.
* @property {ol.source.Vector|undefined} source Source. If this is defined
* then features will be added to this source.
* @property {ol.layer.Vector|undefined} layer Layer. If this is defined then
* features will be added to this layer's source. If neither `source` nor
* `layer` are defined then the dropped features will be added as a new
* layer.
*/
/**
* @typedef {Object} olx.interaction.DragPanOptions
* @property {ol.Kinetic|undefined} kinetic Kinetic inertia to apply to the pan.
@@ -530,6 +549,19 @@
* @property {Object|undefined} params Additional parameters.
*/
/**
* @typedef {Object} olx.source.KMLOptions
* @property {Array.<ol.Attribution>|undefined} attributions Attributions.
* @property {Document|undefined} doc Document.
* @property {ol.Extent|undefined} extent Extent.
* @property {string|undefined} logo Logo.
* @property {Node|undefined| node Node.
* @property {ol.proj.ProjectionLike} projection Projection.
* @property {ol.proj.ProjectionLike} reprojectTo Re-project to.
* @property {string|undefined} text Text.
* @property {string|undefined} url URL.
*/
/**
* @typedef {Object} olx.source.MapQuestOptions
* @property {ol.TileLoadFunctionType|undefined} tileLoadFunction Optional

View File

@@ -23,6 +23,12 @@ ol.format.Format = function() {
};
/**
* @return {Array.<string>} Extensions.
*/
ol.format.Format.prototype.getExtensions = goog.abstractMethod;
/**
* @return {ol.format.FormatType} Format.
*/

View File

@@ -41,6 +41,13 @@ ol.format.GeoJSON = function(opt_options) {
goog.inherits(ol.format.GeoJSON, ol.format.JSON);
/**
* @const {Array.<string>}
* @private
*/
ol.format.GeoJSON.EXTENSIONS_ = ['.geojson'];
/**
* @param {GeoJSONObject} object Object.
* @private
@@ -278,6 +285,14 @@ ol.format.GeoJSON.GEOMETRY_WRITERS_ = {
};
/**
* @inheritDoc
*/
ol.format.GeoJSON.prototype.getExtensions = function() {
return ol.format.GeoJSON.EXTENSIONS_;
};
/**
* @inheritDoc
*/

View File

@@ -0,0 +1 @@
@exportSymbol ol.format.KML

1409
src/ol/format/kmlformat.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,8 @@
goog.provide('ol.format.XML');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.dom.NodeType');
goog.require('goog.dom.xml');
goog.require('ol.format.Format');
goog.require('ol.format.FormatType');
@@ -17,57 +19,6 @@ ol.format.XML = function() {
goog.inherits(ol.format.XML, ol.format.Format);
/**
* @param {Document|Node|Object|string} source Source.
* @private
* @return {Document} Document.
*/
ol.format.XML.prototype.getDocument_ = function(source) {
if (source instanceof Document) {
return source;
} else if (goog.isString(source)) {
return goog.dom.xml.loadXml(source);
} else {
goog.asserts.fail();
return null;
}
};
/**
* @param {Document|Node|Object|string} source Source.
* @private
* @return {Document|Node} Document.
*/
ol.format.XML.prototype.getDocumentOrNode_ = function(source) {
if (source instanceof Document) {
return source;
} else if (source instanceof Node) {
return source;
} else if (goog.isString(source)) {
return goog.dom.xml.loadXml(source);
} else {
goog.asserts.fail();
return null;
}
};
/**
* @param {Document|Node|Object|string} source Source.
* @private
* @return {Node} Node.
*/
ol.format.XML.prototype.getNode_ = function(source) {
if (source instanceof Node) {
return source;
} else {
goog.asserts.fail();
return null;
}
};
/**
* @inheritDoc
*/
@@ -80,7 +31,31 @@ ol.format.XML.prototype.getType = function() {
* @inheritDoc
*/
ol.format.XML.prototype.readFeature = function(source) {
return this.readFeatureFromNode(this.getNode_(source));
if (source instanceof Document) {
return this.readFeatureFromDocument(source);
} else if (source instanceof Node) {
return this.readFeatureFromNode(source);
} else if (goog.isString(source)) {
var doc = goog.dom.xml.loadXml(source);
return this.readFeatureFromDocument(doc);
} else {
goog.asserts.fail();
return null;
}
};
/**
* @param {Document} doc Document.
* @return {ol.Feature} Feature.
*/
ol.format.XML.prototype.readFeatureFromDocument = function(doc) {
var features = this.readFeaturesFromDocument(doc);
if (features.length > 0) {
return features[0];
} else {
return null;
}
};
@@ -95,11 +70,13 @@ ol.format.XML.prototype.readFeatureFromNode = goog.abstractMethod;
* @inheritDoc
*/
ol.format.XML.prototype.readFeatures = function(source) {
var documentOrNode = this.getDocumentOrNode_(source);
if (documentOrNode instanceof Document) {
return this.readFeaturesFromDocument(documentOrNode);
} else if (documentOrNode instanceof Node) {
return this.readFeaturesFromNode(documentOrNode);
if (source instanceof Document) {
return this.readFeaturesFromDocument(source);
} else if (source instanceof Node) {
return this.readFeaturesFromNode(source);
} else if (goog.isString(source)) {
var doc = goog.dom.xml.loadXml(source);
return this.readFeaturesFromDocument(doc);
} else {
goog.asserts.fail();
return null;
@@ -113,8 +90,15 @@ ol.format.XML.prototype.readFeatures = function(source) {
* @return {Array.<ol.Feature>} Features.
*/
ol.format.XML.prototype.readFeaturesFromDocument = function(doc) {
goog.asserts.assert(doc.childNodes.length == 1);
return this.readFeaturesFromNode(doc.firstChild);
/** @type {Array.<ol.Feature>} */
var features = [];
var n;
for (n = doc.firstChild; !goog.isNull(n); n = n.nextSibling) {
if (n.nodeType == goog.dom.NodeType.ELEMENT) {
goog.array.extend(features, this.readFeaturesFromNode(n));
}
}
return features;
};
@@ -130,10 +114,28 @@ ol.format.XML.prototype.readFeaturesFromNode = goog.abstractMethod;
* @inheritDoc
*/
ol.format.XML.prototype.readGeometry = function(source) {
return this.readGeometryFromNode(this.getNode_(source));
if (source instanceof Document) {
return this.readGeometryFromDocument(source);
} else if (source instanceof Node) {
return this.readGeometryFromNode(source);
} else if (goog.isString(source)) {
var doc = goog.dom.xml.loadXml(source);
return this.readGeometryFromDocument(doc);
} else {
goog.asserts.fail();
return null;
}
};
/**
* @param {Document} doc Document.
* @protected
* @return {ol.geom.Geometry} Geometry.
*/
ol.format.XML.prototype.readGeometryFromDocument = goog.abstractMethod;
/**
* @param {Node} node Node.
* @protected
@@ -146,10 +148,28 @@ ol.format.XML.prototype.readGeometryFromNode = goog.abstractMethod;
* @inheritDoc
*/
ol.format.XML.prototype.readProjection = function(source) {
return this.readProjectionFromNode(this.getNode_(source));
if (source instanceof Document) {
return this.readProjectionFromDocument(source);
} else if (source instanceof Node) {
return this.readProjectionFromNode(source);
} else if (goog.isString(source)) {
var doc = goog.dom.xml.loadXml(source);
return this.readProjectionFromDocument(doc);
} else {
goog.asserts.fail();
return null;
}
};
/**
* @param {Document} doc Document.
* @protected
* @return {ol.proj.Projection} Projection.
*/
ol.format.XML.prototype.readProjectionFromDocument = goog.abstractMethod;
/**
* @param {Node} node Node.
* @protected

View File

@@ -0,0 +1 @@
@exportSymbol ol.interaction.DragAndDrop

View File

@@ -0,0 +1,202 @@
// FIXME should handle all geo-referenced data, not just vector data
goog.provide('ol.interaction.DragAndDrop');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.events.FileDropHandler');
goog.require('goog.events.FileDropHandler.EventType');
goog.require('goog.fs.FileReader');
goog.require('goog.functions');
goog.require('ol.interaction.Interaction');
goog.require('ol.layer.Vector');
goog.require('ol.proj');
goog.require('ol.source.Vector');
/**
* @constructor
* @extends {ol.interaction.Interaction}
* @param {olx.interaction.DragAndDropOptions=} opt_options Options.
*/
ol.interaction.DragAndDrop = function(opt_options) {
var options = goog.isDef(opt_options) ? opt_options : {};
goog.base(this);
/**
* @private
* @type {boolean}
*/
this.fitView_ = goog.isDef(options.fitView) ? options.fitView : true;
/**
* @private
* @type {Array.<function(new: ol.format.Format)>}
*/
this.formatConstructors_ = goog.isDef(options.formatConstructors) ?
options.formatConstructors : [];
/**
* @private
* @type {ol.source.Vector}
*/
this.source_ = goog.isDef(options.source) ? options.source : null;
/**
* @private
* @type {ol.layer.Vector}
*/
this.layer_ = goog.isDef(options.layer) ? options.layer : null;
/**
* @private
* @type {goog.events.FileDropHandler}
*/
this.fileDropHandler_ = null;
/**
* @private
* @type {goog.events.Key|undefined}
*/
this.dropListenKey_ = undefined;
};
goog.inherits(ol.interaction.DragAndDrop, ol.interaction.Interaction);
/**
* @inheritDoc
*/
ol.interaction.DragAndDrop.prototype.disposeInternal = function() {
if (goog.isDef(this.dropListenKey_)) {
goog.events.unlistenByKey(this.dropListenKey_);
}
goog.base(this, 'disposeInternal');
};
/**
* @param {goog.events.BrowserEvent} event Event.
* @private
*/
ol.interaction.DragAndDrop.prototype.handleDrop_ = function(event) {
var files = event.getBrowserEvent().dataTransfer.files;
var i, ii;
for (i = 0, ii = files.length; i < ii; ++i) {
var reader = goog.fs.FileReader.readAsText(files[i]);
reader.addCallback(this.handleResult_, this);
}
};
/**
* @param {string} result Result.
* @private
*/
ol.interaction.DragAndDrop.prototype.handleResult_ = function(result) {
var map = this.getMap();
goog.asserts.assert(!goog.isNull(map));
var view = map.getView();
goog.asserts.assert(goog.isDef(view));
var view2D = view.getView2D();
var targetProjection;
if (!goog.isNull(this.source_)) {
targetProjection = this.source_.getProjection();
} else if (!goog.isNull(this.layer_)) {
targetProjection = this.layer_.getSource().getProjection();
} else {
targetProjection = view2D.getProjection();
}
var formatConstructors = this.formatConstructors_;
var features = [];
var i, ii;
for (i = 0, ii = formatConstructors.length; i < ii; ++i) {
var formatConstructor = formatConstructors[i];
var format = new formatConstructor();
var readFeatures = this.tryReadFeatures_(format, result);
if (!goog.isNull(readFeatures)) {
var featureProjection = format.readProjection(result);
var transform = ol.proj.getTransform(featureProjection, targetProjection);
var j, jj;
for (j = 0, jj = readFeatures.length; j < jj; ++j) {
var feature = readFeatures[j];
var geometry = feature.getGeometry();
if (!goog.isNull(geometry)) {
geometry.transform(transform);
}
features.push(feature);
}
}
}
if (features.length > 0) {
var source;
if (!goog.isNull(this.source_)) {
source = this.source_;
} else if (!goog.isNull(this.layer_)) {
source = this.layer_.getSource();
goog.asserts.assertInstanceof(source, ol.source.Vector);
} else {
source = new ol.source.Vector();
}
for (i = 0, ii = features.length; i < ii; ++i) {
source.addFeature(features[i]);
}
if (goog.isNull(this.layer_)) {
map.getLayers().push(new ol.layer.Vector({
source: source
}));
}
if (this.fitView_) {
view2D.fitExtent(source.getExtent(), map.getSize());
}
}
};
/**
* @inheritDoc
*/
ol.interaction.DragAndDrop.prototype.handleMapBrowserEvent =
goog.functions.TRUE;
/**
* @inheritDoc
*/
ol.interaction.DragAndDrop.prototype.setMap = function(map) {
if (goog.isDef(this.dropListenKey_)) {
goog.events.unlistenByKey(this.dropListenKey_);
this.dropListenKey_ = undefined;
}
if (!goog.isNull(this.fileDropHandler_)) {
goog.dispose(this.fileDropHandler_);
this.fileDropHandler_ = null;
}
goog.asserts.assert(!goog.isDef(this.dropListenKey_));
goog.base(this, 'setMap', map);
if (!goog.isNull(map)) {
this.fileDropHandler_ = new goog.events.FileDropHandler(map.getViewport());
this.dropListenKey_ = goog.events.listen(
this.fileDropHandler_, goog.events.FileDropHandler.EventType.DROP,
this.handleDrop_, false, this);
}
};
/**
* @param {ol.format.Format} format Format.
* @param {string} text Text.
* @private
* @return {Array.<ol.Feature>} Features.
*/
ol.interaction.DragAndDrop.prototype.tryReadFeatures_ = function(format, text) {
try {
return format.readFeatures(text);
} catch (e) {
return null;
}
};

View File

@@ -0,0 +1 @@
@exportSymbol ol.source.KML

View File

@@ -0,0 +1,31 @@
goog.provide('ol.source.KML');
goog.require('ol.format.KML');
goog.require('ol.source.VectorFile');
/**
* @constructor
* @extends {ol.source.VectorFile}
* @param {olx.source.KMLOptions=} opt_options Options.
*/
ol.source.KML = function(opt_options) {
var options = goog.isDef(opt_options) ? opt_options : {};
goog.base(this, {
attributions: options.attributions,
doc: options.doc,
extent: options.extent,
format: new ol.format.KML(),
logo: options.logo,
node: options.node,
projection: options.projection,
reprojectTo: options.reprojectTo,
text: options.text,
url: options.url
});
};
goog.inherits(ol.source.KML, ol.source.VectorFile);

202
src/ol/xml.js Normal file
View File

@@ -0,0 +1,202 @@
goog.provide('ol.xml');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.dom.NodeType');
goog.require('goog.object');
/**
* @typedef {function(Node, Array.<*>)}
*/
ol.xml.Parser;
/**
* @param {Node} node Node.
* @param {boolean} normalizeWhitespace Normalize whitespace.
* @return {string} All text content.
*/
ol.xml.getAllTextContent = function(node, normalizeWhitespace) {
return ol.xml.getAllTextContent_(node, normalizeWhitespace, []).join('');
};
/**
* @param {Node} node Node.
* @param {boolean} normalizeWhitespace Normalize whitespace.
* @param {Array.<String|string>} accumulator Accumulator.
* @private
* @return {Array.<String|string>} Accumulator.
*/
ol.xml.getAllTextContent_ = function(node, normalizeWhitespace, accumulator) {
if (node.nodeType == goog.dom.NodeType.CDATA_SECTION ||
node.nodeType == goog.dom.NodeType.TEXT) {
if (normalizeWhitespace) {
// FIXME understand why goog.dom.getTextContent_ uses String here
accumulator.push(String(node.nodeValue).replace(/(\r\n|\r|\n)/g, ''));
} else {
accumulator.push(node.nodeValue);
}
} else {
var n;
for (n = node.firstChild; !goog.isNull(n); n = n.nextSibling) {
ol.xml.getAllTextContent_(n, normalizeWhitespace, accumulator);
}
}
return accumulator;
};
/**
* @param {function(this: T, Node, Array.<*>): (Array.<*>|undefined)}
* valueReader Value reader.
* @param {T=} opt_obj Scope.
* @return {ol.xml.Parser} Parser.
* @template T
*/
ol.xml.makeArrayExtender = function(valueReader, opt_obj) {
return (
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
*/
function(node, objectStack) {
var value = valueReader.call(opt_obj, node, objectStack);
if (goog.isDef(value)) {
goog.asserts.assert(goog.isArray(value));
var array = /** @type {Array.<*>} */
(objectStack[objectStack.length - 1]);
goog.asserts.assert(goog.isArray(array));
goog.array.extend(array, value);
}
});
};
/**
* @param {function(this: T, Node, Array.<*>): *} valueReader Value reader.
* @param {T=} opt_obj Scope.
* @return {ol.xml.Parser} Parser.
* @template T
*/
ol.xml.makeArrayPusher = function(valueReader, opt_obj) {
return (
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
*/
function(node, objectStack) {
var value = valueReader.call(opt_obj, node, objectStack);
if (goog.isDef(value)) {
var array = objectStack[objectStack.length - 1];
goog.asserts.assert(goog.isArray(array));
array.push(value);
}
});
};
/**
* @param {function(this: T, Node, Array.<*>): *} valueReader Value reader.
* @param {T=} opt_obj Scope.
* @return {ol.xml.Parser} Parser.
* @template T
*/
ol.xml.makeReplacer = function(valueReader, opt_obj) {
return (
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
*/
function(node, objectStack) {
var value = valueReader.call(opt_obj, node, objectStack);
if (goog.isDef(value)) {
objectStack[objectStack.length - 1] = value;
}
});
};
/**
* @param {function(this: T, Node, Array.<*>): *} valueReader Value reader.
* @param {string=} opt_property Property.
* @param {T=} opt_obj Scope.
* @return {ol.xml.Parser} Parser.
* @template T
*/
ol.xml.makeObjectPropertySetter = function(valueReader, opt_property, opt_obj) {
goog.asserts.assert(goog.isDef(valueReader));
return (
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
*/
function(node, objectStack) {
var value = valueReader.call(opt_obj, node, objectStack);
if (goog.isDef(value)) {
var object = /** @type {Object} */
(objectStack[objectStack.length - 1]);
var property = goog.isDef(opt_property) ?
opt_property : node.localName;
goog.asserts.assert(goog.isObject(object));
goog.object.set(object, property, value);
}
});
};
/**
* @param {Array.<string>} namespaceURIs Namespace URIs.
* @param {Object.<string, ol.xml.Parser>} parsers Parsers.
* @return {Object.<string, Object.<string, ol.xml.Parser>>} Parsers NS.
*/
ol.xml.makeParsersNS = function(namespaceURIs, parsers) {
/** @type {Object.<string, Object.<string, ol.xml.Parser>>} */
var parsersNS = {};
var i, ii;
for (i = 0, ii = namespaceURIs.length; i < ii; ++i) {
parsersNS[namespaceURIs[i]] = parsers;
}
return parsersNS;
};
/**
* @param {Object.<string, Object.<string, ol.xml.Parser>>} parsersNS
* Parsers by namespace.
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @param {*=} opt_obj Scope.
*/
ol.xml.parse = function(parsersNS, node, objectStack, opt_obj) {
var n;
for (n = node.firstChild; !goog.isNull(n); n = n.nextSibling) {
if (n.nodeType == goog.dom.NodeType.ELEMENT) {
var parsers = parsersNS[n.namespaceURI];
if (goog.isDef(parsers)) {
var parser = parsers[n.localName];
if (goog.isDef(parser)) {
parser.call(opt_obj, n, objectStack);
}
}
}
}
};
/**
* @param {T} object Object.
* @param {Object.<string, Object.<string, ol.xml.Parser>>} parsersNS
* Parsers by namespace.
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @param {*=} opt_obj Scope.
* @return {T|undefined} Object.
* @template T
*/
ol.xml.pushAndParse = function(object, parsersNS, node, objectStack, opt_obj) {
objectStack.push(object);
ol.xml.parse(parsersNS, node, objectStack, opt_obj);
return objectStack.pop();
};

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff