Merge pull request #1744 from twpayne/remote-vector

ol.source.RemoteVector
This commit is contained in:
Tom Payne
2014-04-03 16:12:16 +02:00
30 changed files with 897 additions and 2442 deletions

File diff suppressed because it is too large Load Diff

51
examples/tile-vector.html Normal file
View File

@@ -0,0 +1,51 @@
<!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>Tile vector 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" style="background: white;"></div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<h4 id="title">Tile vector example</h4>
<p id="shortdesc">Example of <a href="http://openstreetmap.us/~migurski/vector-datasource/">vector tiles from openstreetmap.us</a>.</p>
<div id="docs">
<p>See the <a href="tile-vector.js" target="_blank">tile-vector.js source</a> to see how this is done.</p>
</div>
<div id="tags">tile-vector, openstreetmap</div>
</div>
</div>
</div>
<script src="jquery.min.js" type="text/javascript"></script>
<script src="../resources/example-behaviour.js" type="text/javascript"></script>
<script src="loader.js?id=tile-vector" type="text/javascript"></script>
</body>
</html>

85
examples/tile-vector.js Normal file
View File

@@ -0,0 +1,85 @@
goog.require('ol.Map');
goog.require('ol.View2D');
goog.require('ol.format.TopoJSON');
goog.require('ol.layer.Vector');
goog.require('ol.proj');
goog.require('ol.source.TileVector');
goog.require('ol.style.Fill');
goog.require('ol.style.Stroke');
goog.require('ol.style.Style');
goog.require('ol.tilegrid.XYZ');
var waterLayer = new ol.layer.Vector({
source: new ol.source.TileVector({
format: new ol.format.TopoJSON({
defaultProjection: 'EPSG:4326'
}),
projection: 'EPSG:3857',
tileGrid: new ol.tilegrid.XYZ({
maxZoom: 19
}),
url: 'http://{a-c}.tile.openstreetmap.us/' +
'vectiles-water-areas/{z}/{x}/{y}.topojson'
}),
style: new ol.style.Style({
fill: new ol.style.Fill({
color: '#9db9e8'
})
})
});
var roadStyleCache = {};
var roadLayer = new ol.layer.Vector({
source: new ol.source.TileVector({
format: new ol.format.TopoJSON({
defaultProjection: 'EPSG:4326'
}),
projection: 'EPSG:3857',
tileGrid: new ol.tilegrid.XYZ({
maxZoom: 19
}),
url: 'http://{a-c}.tile.openstreetmap.us/' +
'vectiles-highroad/{z}/{x}/{y}.topojson'
}),
style: function(feature, resolution) {
var kind = feature.get('kind');
var railway = feature.get('railway');
var sort_key = feature.get('sort_key');
var styleKey = kind + '/' + railway + '/' + sort_key;
var styleArray = roadStyleCache[styleKey];
if (!styleArray) {
var color, width;
if (railway) {
color = '#7de';
width = 1;
} else {
color = {
'major_road': '#776',
'minor_road': '#ccb',
'highway': '#f39'
}[kind];
width = kind == 'highway' ? 1.5 : 1;
}
styleArray = [new ol.style.Style({
stroke: new ol.style.Stroke({
color: color,
width: width
}),
zIndex: sort_key
})];
roadStyleCache[styleKey] = styleArray;
}
return styleArray;
}
});
var map = new ol.Map({
layers: [waterLayer, roadLayer],
renderer: 'canvas',
target: document.getElementById('map'),
view: new ol.View2D({
center: ol.proj.transform([-74.0064, 40.7142], 'EPSG:4326', 'EPSG:3857'),
maxZoom: 19,
zoom: 14
})
});

View File

@@ -32,11 +32,11 @@
<div class="span4">
<h4 id="title">OSM XML example</h4>
<p id="shortdesc">Example of using the OSM XML source.</p>
<p id="shortdesc">Example of using the OSM XML source. Vector data is loaded dynamically from a server using a tiling strategy.</p>
<div id="docs">
<p>See the <a href="vector-osm.js" target="_blank">vector-osm.js source</a> to see how this is done.</p>
</div>
<div id="tags">vector, osm, xml</div>
<div id="tags">vector, osm, xml, loading, server</div>
</div>
<div class="span4 offset4">
<div id="info" class="alert alert-success">

View File

@@ -1,13 +1,17 @@
goog.require('ol.Map');
goog.require('ol.View2D');
goog.require('ol.format.OSMXML');
goog.require('ol.layer.Tile');
goog.require('ol.layer.Vector');
goog.require('ol.loadingstrategy');
goog.require('ol.proj');
goog.require('ol.source.BingMaps');
goog.require('ol.source.OSMXML');
goog.require('ol.source.ServerVector');
goog.require('ol.style.Circle');
goog.require('ol.style.Fill');
goog.require('ol.style.Stroke');
goog.require('ol.style.Style');
goog.require('ol.tilegrid.XYZ');
var styles = {
'amenity': {
@@ -83,9 +87,21 @@ var styles = {
}
};
var vectorSource = new ol.source.OSMXML({
projection: 'EPSG:3857',
url: 'data/osm/map.osm'
var vectorSource = new ol.source.ServerVector({
format: new ol.format.OSMXML(),
loader: function(extent, resolution, projection) {
var transform = ol.proj.getTransform(projection, 'EPSG:4326');
var epsg4326Extent = transform(extent, []);
var url = 'http://overpass-api.de/api/xapi?map?bbox=' +
epsg4326Extent.join(',');
$.ajax(url).then(function(response) {
vectorSource.addFeatures(vectorSource.readFeatures(response));
});
},
strategy: ol.loadingstrategy.createTile(new ol.tilegrid.XYZ({
maxZoom: 19
})),
projection: 'EPSG:3857'
});
var vector = new ol.layer.Vector({
@@ -117,6 +133,7 @@ var map = new ol.Map({
target: document.getElementById('map'),
view: new ol.View2D({
center: [739218, 5906096],
maxZoom: 19,
zoom: 17
})
});

View File

@@ -668,6 +668,15 @@
* @todo stability experimental
*/
/**
* @typedef {Object} olx.source.FormatVectorOptions
* @property {Array.<ol.Attribution>|undefined} attributions Attributions.
* @property {ol.Extent|undefined} extent Extent.
* @property {ol.format.Feature} format Format.
* @property {string|undefined} logo Logo.
* @property {ol.proj.ProjectionLike} projection Projection.
*/
/**
* @typedef {Object} olx.source.GeoJSONOptions
* @property {Array.<ol.Attribution>|undefined} attributions Attributions.
@@ -698,6 +707,25 @@
* @property {Array.<string>|undefined} urls URLs.
*/
/**
* @typedef {Object} olx.source.TileVectorOptions
* @property {Array.<ol.Attribution>|undefined} attributions Attributions.
* @property {ol.proj.ProjectionLike} defaultProjection Default projection.
* @property {ol.Extent|undefined} extent Extent.
* @property {string|undefined} logo Logo.
* @property {GeoJSONObject|undefined} object Object.
* @property {ol.proj.ProjectionLike} projection Destination projection. If
* provided, features will be transformed to this projection. If not
* provided, features will not be transformed.
* @property {ol.tilegrid.TileGrid} tileGrid Tile grid.
* @property {ol.TileUrlFunctionType|undefined} tileUrlFunction Optional
* function to get tile URL given a tile coordinate and the projection.
* Required if url or urls are not provided.
* @property {string|undefined} url URL template. Must include `{x}`, `{y}`,
* and `{z}` placeholders.
* @property {Array.<string>|undefined} urls An array of URL templates.
*/
/**
* @typedef {Object} olx.source.TopoJSONOptions
* @property {Array.<ol.Attribution>|undefined} attributions Attributions.
@@ -895,6 +923,17 @@
* @property {string} url Url.
*/
/**
* @typedef {Object} olx.source.ServerVectorOptions
* @property {Array.<ol.Attribution>|undefined} attributions Attributions.
* @property {ol.Extent|undefined} extent Extent.
* @property {ol.format.Feature} format Format.
* @property {function(this: ol.source.ServerVector, ol.Extent, number, ol.proj.Projection)} loader Loading function.
* @property {function(ol.Extent, number): Array.<ol.Extent>|undefined} strategy Loading strategy. Default is `ol.loadingstrategy.bbox`.
* @property {string|undefined} logo Logo.
* @property {ol.proj.ProjectionLike} projection Projection.
*/
/**
* @typedef {Object} olx.source.TileJSONOptions
* @property {null|string|undefined} crossOrigin crossOrigin setting for image
@@ -952,7 +991,7 @@
*/
/**
* @typedef {Object} olx.source.VectorFileOptions
* @typedef {Object} olx.source.StaticVectorOptions
* @property {ArrayBuffer|undefined} arrayBuffer Array buffer.
* @property {Array.<ol.Attribution>|undefined} attributions Attributions.
* @property {Document|undefined} doc Document.

View File

@@ -597,6 +597,17 @@ ol.extent.isEmpty = function(extent) {
};
/**
* @param {ol.Extent} extent Extent.
* @return {boolean} Is infinite.
* @todo stability experimental
*/
ol.extent.isInfinite = function(extent) {
return extent[0] == -Infinity || extent[1] == -Infinity ||
extent[2] == Infinity || extent[3] == Infinity;
};
/**
* @param {ol.Extent} extent Extent.
* @param {ol.Coordinate} coordinate Coordinate.

View File

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

View File

@@ -0,0 +1,3 @@
@exportSymbol ol.loadingstrategy.all
@exportSymbol ol.loadingstrategy.bbox
@exportSymbol ol.loadingstrategy.createTile

52
src/ol/loadingstrategy.js Normal file
View File

@@ -0,0 +1,52 @@
goog.provide('ol.loadingstrategy');
goog.require('ol.TileCoord');
/**
* @param {ol.Extent} extent Extent.
* @param {number} resolution Resolution.
* @return {Array.<ol.Extent>} Extents.
*/
ol.loadingstrategy.all = function(extent, resolution) {
return [[-Infinity, -Infinity, Infinity, Infinity]];
};
/**
* @param {ol.Extent} extent Extent.
* @param {number} resolution Resolution.
* @return {Array.<ol.Extent>} Extents.
*/
ol.loadingstrategy.bbox = function(extent, resolution) {
return [extent];
};
/**
* @param {ol.tilegrid.TileGrid} tileGrid Tile grid.
* @return {function(ol.Extent, number): Array.<ol.Extent>} Loading strategy.
*/
ol.loadingstrategy.createTile = function(tileGrid) {
return (
/**
* @param {ol.Extent} extent Extent.
* @param {number} resolution Resolution.
* @return {Array.<ol.Extent>} Extents.
*/
function(extent, resolution) {
var z = tileGrid.getZForResolution(resolution);
var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
/** @type {Array.<ol.Extent>} */
var extents = [];
var tileCoord = new ol.TileCoord(z, 0, 0);
for (tileCoord.x = tileRange.minX; tileCoord.x <= tileRange.maxX;
++tileCoord.x) {
for (tileCoord.y = tileRange.minY; tileCoord.y <= tileRange.maxY;
++tileCoord.y) {
extents.push(tileGrid.getTileCoordExtent(tileCoord));
}
}
return extents;
});
};

View File

@@ -170,7 +170,9 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame =
}
var frameStateExtent = frameState.extent;
var frameStateResolution = frameState.view2DState.resolution;
var view2DState = frameState.view2DState;
var projection = view2DState.projection;
var resolution = view2DState.resolution;
var pixelRatio = frameState.pixelRatio;
var vectorLayerRevision = vectorLayer.getRevision();
var vectorLayerRenderOrder = vectorLayer.getRenderOrder();
@@ -179,7 +181,7 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame =
}
if (!this.dirty_ &&
this.renderedResolution_ == frameStateResolution &&
this.renderedResolution_ == resolution &&
this.renderedRevision_ == vectorLayerRevision &&
this.renderedRenderOrder_ == vectorLayerRenderOrder &&
ol.extent.containsExtent(this.renderedExtent_, frameStateExtent)) {
@@ -204,9 +206,10 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame =
if (!goog.isDef(styleFunction)) {
styleFunction = ol.feature.defaultStyleFunction;
}
var tolerance = frameStateResolution / (2 * pixelRatio);
var replayGroup = new ol.render.canvas.ReplayGroup(tolerance, extent,
frameStateResolution);
var tolerance = resolution / (2 * pixelRatio);
var replayGroup =
new ol.render.canvas.ReplayGroup(tolerance, extent, resolution);
vectorSource.loadFeatures(extent, resolution, projection);
var renderFeature =
/**
* @param {ol.Feature} feature Feature.
@@ -215,19 +218,28 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame =
function(feature) {
goog.asserts.assert(goog.isDef(styleFunction));
var dirty = this.renderFeature(
feature, frameStateResolution, pixelRatio, styleFunction, replayGroup);
feature, resolution, pixelRatio, styleFunction, replayGroup);
this.dirty_ = this.dirty_ || dirty;
};
if (!goog.isNull(vectorLayerRenderOrder)) {
var features = vectorSource.getFeaturesInExtent(extent);
/** @type {Array.<ol.Feature>} */
var features = [];
vectorSource.forEachFeatureInExtentAtResolution(extent, resolution,
/**
* @param {ol.Feature} feature Feature.
*/
function(feature) {
features.push(feature);
}, this);
goog.array.sort(features, vectorLayerRenderOrder);
goog.array.forEach(features, renderFeature, this);
} else {
vectorSource.forEachFeatureInExtent(extent, renderFeature, this);
vectorSource.forEachFeatureInExtentAtResolution(
extent, resolution, renderFeature, this);
}
replayGroup.finish();
this.renderedResolution_ = frameStateResolution;
this.renderedResolution_ = resolution;
this.renderedRevision_ = vectorLayerRevision;
this.renderedRenderOrder_ = vectorLayerRenderOrder;
this.replayGroup_ = replayGroup;

View File

@@ -91,6 +91,7 @@ ol.source.BingMaps.prototype.handleImageryMetadataResponse =
this.tileGrid = tileGrid;
var culture = this.culture_;
var sourceProjection = this.getProjection();
this.tileUrlFunction = ol.TileUrlFunction.withTileCoordTransform(
tileGrid.createTileCoordTransform(),
ol.TileUrlFunction.createFromTileUrlFunctions(
@@ -102,7 +103,6 @@ ol.source.BingMaps.prototype.handleImageryMetadataResponse =
.replace('{culture}', culture);
return (
/**
* @this {ol.source.BingMaps}
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @param {number} pixelRatio Pixel ratio.
* @param {ol.proj.Projection} projection Projection.
@@ -110,7 +110,7 @@ ol.source.BingMaps.prototype.handleImageryMetadataResponse =
*/
function(tileCoord, pixelRatio, projection) {
goog.asserts.assert(ol.proj.equivalent(
projection, this.getProjection()));
projection, sourceProjection));
if (goog.isNull(tileCoord)) {
return undefined;
} else {

View File

@@ -0,0 +1,135 @@
// FIXME consider delaying feature reading so projection can be provided by
// consumer (e.g. the view)
goog.provide('ol.source.FormatVector');
goog.require('goog.asserts');
goog.require('goog.dispose');
goog.require('goog.events');
goog.require('goog.net.EventType');
goog.require('goog.net.XhrIo');
goog.require('goog.net.XhrIo.ResponseType');
goog.require('goog.userAgent');
goog.require('ol.BrowserFeature');
goog.require('ol.format.FormatType');
goog.require('ol.proj');
goog.require('ol.source.State');
goog.require('ol.source.Vector');
goog.require('ol.xml');
/**
* @constructor
* @extends {ol.source.Vector}
* @param {olx.source.FormatVectorOptions} options Options.
* @todo stability experimental
*/
ol.source.FormatVector = function(options) {
goog.base(this, {
attributions: options.attributions,
extent: options.extent,
logo: options.logo,
projection: options.projection
});
/**
* @protected
* @type {ol.format.Feature}
*/
this.format = options.format;
};
goog.inherits(ol.source.FormatVector, ol.source.Vector);
/**
* @param {goog.Uri|string} url URL.
* @param {function(this: T, Array.<ol.Feature>)} callback Callback.
* @param {T} thisArg Value to use as `this` when executing `callback`.
* @template T
*/
ol.source.FormatVector.prototype.loadFeaturesFromURL =
function(url, callback, thisArg) {
var xhrIo = new goog.net.XhrIo();
var type = this.format.getType();
var responseType;
// FIXME maybe use ResponseType.DOCUMENT?
if (type == ol.format.FormatType.BINARY &&
ol.BrowserFeature.HAS_ARRAY_BUFFER) {
responseType = goog.net.XhrIo.ResponseType.ARRAY_BUFFER;
} else {
responseType = goog.net.XhrIo.ResponseType.TEXT;
}
xhrIo.setResponseType(responseType);
goog.events.listen(xhrIo, goog.net.EventType.COMPLETE,
/**
* @param {Event} event Event.
* @private
* @this {ol.source.FormatVector}
*/
function(event) {
var xhrIo = event.target;
goog.asserts.assertInstanceof(xhrIo, goog.net.XhrIo);
if (xhrIo.isSuccess()) {
var type = this.format.getType();
/** @type {ArrayBuffer|Document|Node|Object|string|undefined} */
var source;
if (type == ol.format.FormatType.BINARY &&
ol.BrowserFeature.HAS_ARRAY_BUFFER) {
source = xhrIo.getResponse();
goog.asserts.assertInstanceof(source, ArrayBuffer);
} else if (type == ol.format.FormatType.JSON) {
source = xhrIo.getResponseText();
} else if (type == ol.format.FormatType.TEXT) {
source = xhrIo.getResponseText();
} else if (type == ol.format.FormatType.XML) {
if (!goog.userAgent.IE) {
source = xhrIo.getResponseXml();
}
if (!goog.isDefAndNotNull(source)) {
source = ol.xml.load(xhrIo.getResponseText());
}
} else {
goog.asserts.fail();
}
if (goog.isDefAndNotNull(source)) {
callback.call(thisArg, this.readFeatures(source));
} else {
this.setState(ol.source.State.ERROR);
goog.asserts.fail();
}
} else {
this.setState(ol.source.State.ERROR);
}
goog.dispose(xhrIo);
}, false, this);
xhrIo.send(url);
};
/**
* @param {ArrayBuffer|Document|Node|Object|string} source Source.
* @return {Array.<ol.Feature>} Features.
*/
ol.source.FormatVector.prototype.readFeatures = function(source) {
var format = this.format;
var features = format.readFeatures(source);
var featureProjection = format.readProjection(source);
var projection = this.getProjection();
if (!goog.isNull(projection)) {
if (!ol.proj.equivalent(featureProjection, projection)) {
var transform = ol.proj.getTransform(featureProjection, projection);
var i, ii;
for (i = 0, ii = features.length; i < ii; ++i) {
var feature = features[i];
var geometry = feature.getGeometry();
if (!goog.isNull(geometry)) {
geometry.transform(transform);
}
}
}
}
return features;
};

View File

@@ -1,13 +1,13 @@
goog.provide('ol.source.GeoJSON');
goog.require('ol.format.GeoJSON');
goog.require('ol.source.VectorFile');
goog.require('ol.source.StaticVector');
/**
* @constructor
* @extends {ol.source.VectorFile}
* @extends {ol.source.StaticVector}
* @fires {@link ol.source.VectorEvent} ol.source.VectorEvent
* @param {olx.source.GeoJSONOptions=} opt_options Options.
* @todo stability experimental
@@ -31,4 +31,4 @@ ol.source.GeoJSON = function(opt_options) {
});
};
goog.inherits(ol.source.GeoJSON, ol.source.VectorFile);
goog.inherits(ol.source.GeoJSON, ol.source.StaticVector);

View File

@@ -1,13 +1,13 @@
goog.provide('ol.source.GPX');
goog.require('ol.format.GPX');
goog.require('ol.source.VectorFile');
goog.require('ol.source.StaticVector');
/**
* @constructor
* @extends {ol.source.VectorFile}
* @extends {ol.source.StaticVector}
* @fires {@link ol.source.VectorEvent} ol.source.VectorEvent
* @param {olx.source.GPXOptions=} opt_options Options.
* @todo stability experimental
@@ -30,4 +30,4 @@ ol.source.GPX = function(opt_options) {
});
};
goog.inherits(ol.source.GPX, ol.source.VectorFile);
goog.inherits(ol.source.GPX, ol.source.StaticVector);

View File

@@ -1,13 +1,13 @@
goog.provide('ol.source.IGC');
goog.require('ol.format.IGC');
goog.require('ol.source.VectorFile');
goog.require('ol.source.StaticVector');
/**
* @constructor
* @extends {ol.source.VectorFile}
* @extends {ol.source.StaticVector}
* @fires {@link ol.source.VectorEvent} ol.source.VectorEvent
* @param {olx.source.IGCOptions=} opt_options Options.
* @todo stability experimental
@@ -27,4 +27,4 @@ ol.source.IGC = function(opt_options) {
});
};
goog.inherits(ol.source.IGC, ol.source.VectorFile);
goog.inherits(ol.source.IGC, ol.source.StaticVector);

View File

@@ -106,7 +106,7 @@ ol.source.ImageVector.prototype.canvasFunctionInternal_ =
resolution);
var loading = false;
this.source_.forEachFeatureInExtent(extent,
this.source_.forEachFeatureInExtentAtResolution(extent, resolution,
/**
* @param {ol.Feature} feature Feature.
*/

View File

@@ -1,13 +1,13 @@
goog.provide('ol.source.KML');
goog.require('ol.format.KML');
goog.require('ol.source.VectorFile');
goog.require('ol.source.StaticVector');
/**
* @constructor
* @extends {ol.source.VectorFile}
* @extends {ol.source.StaticVector}
* @fires {@link ol.source.VectorEvent} ol.source.VectorEvent
* @param {olx.source.KMLOptions=} opt_options Options.
* @todo stability experimental
@@ -32,4 +32,4 @@ ol.source.KML = function(opt_options) {
});
};
goog.inherits(ol.source.KML, ol.source.VectorFile);
goog.inherits(ol.source.KML, ol.source.StaticVector);

View File

@@ -1,13 +1,13 @@
goog.provide('ol.source.OSMXML');
goog.require('ol.format.OSMXML');
goog.require('ol.source.VectorFile');
goog.require('ol.source.StaticVector');
/**
* @constructor
* @extends {ol.source.VectorFile}
* @extends {ol.source.StaticVector}
* @fires {@link ol.source.VectorEvent} ol.source.VectorEvent
* @param {olx.source.OSMXMLOptions=} opt_options Options.
*/
@@ -29,4 +29,4 @@ ol.source.OSMXML = function(opt_options) {
});
};
goog.inherits(ol.source.OSMXML, ol.source.VectorFile);
goog.inherits(ol.source.OSMXML, ol.source.StaticVector);

View File

@@ -0,0 +1,2 @@
@exportSymbol ol.source.ServerVector
@exportProperty ol.source.ServerVector.prototype.readFeatures

View File

@@ -0,0 +1,99 @@
// FIXME cache expiration
goog.provide('ol.source.ServerVector');
goog.require('ol.extent');
goog.require('ol.loadingstrategy');
goog.require('ol.source.FormatVector');
goog.require('ol.structs.RBush');
/**
* @constructor
* @extends {ol.source.FormatVector}
* @param {olx.source.ServerVectorOptions} options Options.
*/
ol.source.ServerVector = function(options) {
goog.base(this, {
attributions: options.attributions,
extent: options.extent,
format: options.format,
logo: options.logo,
projection: options.projection
});
/**
* @private
* @type {ol.structs.RBush.<{extent: ol.Extent}>}
*/
this.loadedExtents_ = new ol.structs.RBush();
/**
* @private
* @type {function(this: ol.source.ServerVector, ol.Extent, number,
* ol.proj.Projection): string}
*/
this.loader_ = options.loader;
/**
* @private
* @type {function(ol.Extent, number): Array.<ol.Extent>}
*/
this.strategy_ = goog.isDef(options.strategy) ?
options.strategy : ol.loadingstrategy.bbox;
/**
* @private
* @type {Object.<number|string, boolean>}
*/
this.loadedFeatures_ = {};
};
goog.inherits(ol.source.ServerVector, ol.source.FormatVector);
/**
* @inheritDoc
*/
ol.source.ServerVector.prototype.addFeaturesInternal = function(features) {
/** @type {Array.<ol.Feature>} */
var notLoadedFeatures = [];
var i, ii;
for (i = 0, ii = features.length; i < ii; ++i) {
var feature = features[i];
var featureId = feature.getId();
if (!(featureId in this.loadedFeatures_)) {
notLoadedFeatures.push(feature);
this.loadedFeatures_[featureId] = true;
}
}
goog.base(this, 'addFeaturesInternal', notLoadedFeatures);
};
/**
* @inheritDoc
*/
ol.source.ServerVector.prototype.loadFeatures =
function(extent, resolution, projection) {
var loadedExtents = this.loadedExtents_;
var extentsToLoad = this.strategy_(extent, resolution);
var i, ii;
for (i = 0, ii = extentsToLoad.length; i < ii; ++i) {
var extentToLoad = extentsToLoad[i];
var alreadyLoaded = loadedExtents.forEachInExtent(extentToLoad,
/**
* @param {{extent: ol.Extent}} object Object.
* @return {boolean} Contains.
*/
function(object) {
return ol.extent.containsExtent(object.extent, extentToLoad);
});
if (!alreadyLoaded) {
this.loader_.call(this, extentToLoad, resolution, projection);
loadedExtents.insert(extentToLoad, {extent: extentToLoad.slice()});
}
}
};

View File

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

View File

@@ -0,0 +1,76 @@
goog.provide('ol.source.StaticVector');
goog.require('ol.source.FormatVector');
goog.require('ol.source.State');
/**
* @constructor
* @extends {ol.source.FormatVector}
* @fires {@link ol.source.VectorEvent} ol.source.VectorEvent
* @param {olx.source.StaticVectorOptions} options Options.
* @todo stability experimental
*/
ol.source.StaticVector = function(options) {
goog.base(this, {
attributions: options.attributions,
extent: options.extent,
format: options.format,
logo: options.logo,
projection: options.projection
});
if (goog.isDef(options.arrayBuffer)) {
this.addFeaturesInternal(this.readFeatures(options.arrayBuffer));
}
if (goog.isDef(options.doc)) {
this.addFeaturesInternal(this.readFeatures(options.doc));
}
if (goog.isDef(options.node)) {
this.addFeaturesInternal(this.readFeatures(options.node));
}
if (goog.isDef(options.object)) {
this.addFeaturesInternal(this.readFeatures(options.object));
}
if (goog.isDef(options.text)) {
this.addFeaturesInternal(this.readFeatures(options.text));
}
if (goog.isDef(options.url) || goog.isDef(options.urls)) {
this.setState(ol.source.State.LOADING);
if (goog.isDef(options.url)) {
this.loadFeaturesFromURL(options.url,
/**
* @param {Array.<ol.Feature>} features Features.
* @this {ol.source.StaticVector}
*/
function(features) {
this.addFeaturesInternal(features);
this.setState(ol.source.State.READY);
}, this);
}
if (goog.isDef(options.urls)) {
var urls = options.urls;
var i, ii;
for (i = 0, ii = urls.length; i < ii; ++i) {
this.loadFeaturesFromURL(urls[i],
/**
* @param {Array.<ol.Feature>} features Features.
* @this {ol.source.StaticVector}
*/
function(features) {
this.addFeaturesInternal(features);
this.setState(ol.source.State.READY);
}, this);
}
}
}
};
goog.inherits(ol.source.StaticVector, ol.source.FormatVector);

View File

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

View File

@@ -0,0 +1,245 @@
goog.provide('ol.source.TileVector');
goog.require('goog.array');
goog.require('goog.object');
goog.require('ol.TileCoord');
goog.require('ol.TileUrlFunction');
goog.require('ol.source.FormatVector');
goog.require('ol.source.State');
goog.require('ol.tilegrid.TileGrid');
/**
* @constructor
* @extends {ol.source.FormatVector}
* @param {olx.source.TileVectorOptions} options Options.
*/
ol.source.TileVector = function(options) {
goog.base(this, {
attributions: options.attributions,
extent: options.extent,
format: options.format,
logo: options.logo,
projection: options.projection
});
var tileGrid = options.tileGrid;
/**
* @private
* @type {ol.tilegrid.TileGrid}
*/
this.tileGrid_ = options.tileGrid;
/**
* @private
* @type {ol.TileUrlFunctionType}
*/
this.tileUrlFunction_ = ol.TileUrlFunction.nullTileUrlFunction;
/**
* @private
* @type {ol.TileCoordTransformType}
*/
this.tileCoordTransform_ = tileGrid.createTileCoordTransform({
extent: options.extent
});
/**
* @private
* @type {Object.<string, Array.<ol.Feature>>}
*/
this.tiles_ = {};
if (goog.isDef(options.tileUrlFunction)) {
this.setTileUrlFunction(options.tileUrlFunction);
} else if (goog.isDef(options.urls)) {
this.setUrls(options.urls);
} else if (goog.isDef(options.url)) {
this.setUrl(options.url);
}
};
goog.inherits(ol.source.TileVector, ol.source.FormatVector);
/**
* @inheritDoc
*/
ol.source.TileVector.prototype.addFeature = goog.abstractMethod;
/**
* @inheritDoc
*/
ol.source.TileVector.prototype.addFeatures = goog.abstractMethod;
/**
* @inheritDoc
*/
ol.source.TileVector.prototype.clear = function() {
goog.object.clear(this.tiles_);
};
/**
* @inheritDoc
*/
ol.source.TileVector.prototype.forEachFeature = goog.abstractMethod;
/**
* @inheritDoc
*/
ol.source.TileVector.prototype.forEachFeatureInExtent = goog.abstractMethod;
/**
* @inheritDoc
*/
ol.source.TileVector.prototype.forEachFeatureInExtentAtResolution =
function(extent, resolution, f, opt_this) {
var tileGrid = this.tileGrid_;
var tiles = this.tiles_;
var z = tileGrid.getZForResolution(resolution);
var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
var x, y;
for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
var tileKey = this.getTileKeyZXY_(z, x, y);
var features = tiles[tileKey];
if (goog.isDef(features)) {
var i, ii;
for (i = 0, ii = features.length; i < ii; ++i) {
var result = f.call(opt_this, features[i]);
if (result) {
return result;
}
}
}
}
}
return undefined;
};
/**
* @inheritDoc
*/
ol.source.TileVector.prototype.getClosestFeatureToCoordinate =
goog.abstractMethod;
/**
* @inheritDoc
*/
ol.source.TileVector.prototype.getExtent = goog.abstractMethod;
/**
* @inheritDoc
*/
ol.source.TileVector.prototype.getFeatures = function() {
var tiles = this.tiles_;
var features = [];
var tileKey;
for (tileKey in tiles) {
goog.array.extend(features, tiles[tileKey]);
}
return features;
};
/**
* @inheritDoc
*/
ol.source.TileVector.prototype.getFeaturesInExtent = goog.abstractMethod;
/**
* @param {number} z Z.
* @param {number} x X.
* @param {number} y Y.
* @private
* @return {string} Tile key.
*/
ol.source.TileVector.prototype.getTileKeyZXY_ = function(z, x, y) {
return z + '/' + x + '/' + y;
};
/**
* @inheritDoc
*/
ol.source.TileVector.prototype.loadFeatures =
function(extent, resolution, projection) {
var tileCoordTransform = this.tileCoordTransform_;
var tileGrid = this.tileGrid_;
var tileUrlFunction = this.tileUrlFunction_;
var tiles = this.tiles_;
var z = tileGrid.getZForResolution(resolution);
var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
var tileCoord = new ol.TileCoord(z, 0, 0);
var x, y;
for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
var tileKey = this.getTileKeyZXY_(z, x, y);
if (!(tileKey in tiles)) {
tileCoord.z = z;
tileCoord.x = x;
tileCoord.y = y;
tileCoordTransform(tileCoord, projection, tileCoord);
var url = tileUrlFunction(tileCoord, 1, projection);
if (goog.isDef(url)) {
tiles[tileKey] = [];
this.loadFeaturesFromURL(url, goog.partial(
/**
* @param {string} tileKey Tile key.
* @param {Array.<ol.Feature>} features Features.
* @this {ol.source.TileVector}
*/
function(tileKey, features) {
tiles[tileKey] = features;
this.setState(ol.source.State.READY);
}, tileKey), this);
}
}
}
}
};
/**
* @inheritDoc
*/
ol.source.TileVector.prototype.removeFeature = goog.abstractMethod;
/**
* @param {ol.TileUrlFunctionType} tileUrlFunction Tile URL function.
*/
ol.source.TileVector.prototype.setTileUrlFunction = function(tileUrlFunction) {
this.tileUrlFunction_ = tileUrlFunction;
this.dispatchChangeEvent();
};
/**
* @param {string} url URL.
* @todo stability experimental
*/
ol.source.TileVector.prototype.setUrl = function(url) {
this.setTileUrlFunction(ol.TileUrlFunction.createFromTemplates(
ol.TileUrlFunction.expandUrl(url)));
};
/**
* @param {Array.<string>} urls URLs.
*/
ol.source.TileVector.prototype.setUrls = function(urls) {
this.setTileUrlFunction(ol.TileUrlFunction.createFromTemplates(urls));
};

View File

@@ -1,13 +1,13 @@
goog.provide('ol.source.TopoJSON');
goog.require('ol.format.TopoJSON');
goog.require('ol.source.VectorFile');
goog.require('ol.source.StaticVector');
/**
* @constructor
* @extends {ol.source.VectorFile}
* @extends {ol.source.StaticVector}
* @fires {@link ol.source.VectorEvent} ol.source.VectorEvent
* @param {olx.source.TopoJSONOptions=} opt_options Options.
* @todo stability experimental
@@ -30,4 +30,4 @@ ol.source.TopoJSON = function(opt_options) {
});
};
goog.inherits(ol.source.TopoJSON, ol.source.VectorFile);
goog.inherits(ol.source.TopoJSON, ol.source.StaticVector);

View File

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

View File

@@ -1,168 +0,0 @@
// FIXME consider delaying feature reading so projection can be provided by
// consumer (e.g. the view)
goog.provide('ol.source.VectorFile');
goog.require('goog.asserts');
goog.require('goog.dispose');
goog.require('goog.events');
goog.require('goog.net.EventType');
goog.require('goog.net.XhrIo');
goog.require('goog.net.XhrIo.ResponseType');
goog.require('goog.userAgent');
goog.require('ol.BrowserFeature');
goog.require('ol.format.FormatType');
goog.require('ol.proj');
goog.require('ol.source.State');
goog.require('ol.source.Vector');
goog.require('ol.xml');
/**
* @constructor
* @extends {ol.source.Vector}
* @fires {@link ol.source.VectorEvent} ol.source.VectorEvent
* @param {olx.source.VectorFileOptions=} opt_options Options.
* @todo stability experimental
*/
ol.source.VectorFile = function(opt_options) {
var options = goog.isDef(opt_options) ? opt_options : {};
goog.base(this, {
attributions: options.attributions,
extent: options.extent,
logo: options.logo,
projection: options.projection
});
/**
* @type {ol.format.Feature}
* @protected
*/
this.format = options.format;
if (goog.isDef(options.doc)) {
this.readFeatures_(options.doc);
}
if (goog.isDef(options.node)) {
this.readFeatures_(options.node);
}
if (goog.isDef(options.object)) {
this.readFeatures_(options.object);
}
if (goog.isDef(options.text)) {
this.readFeatures_(options.text);
}
if (goog.isDef(options.arrayBuffer)) {
this.readFeatures_(options.arrayBuffer);
}
if (goog.isDef(options.url) || goog.isDef(options.urls)) {
this.setState(ol.source.State.LOADING);
var type = this.format.getType();
var responseType;
if (type == ol.format.FormatType.BINARY &&
ol.BrowserFeature.HAS_ARRAY_BUFFER) {
responseType = goog.net.XhrIo.ResponseType.ARRAY_BUFFER;
} else {
responseType = goog.net.XhrIo.ResponseType.TEXT;
}
var xhrIo;
if (goog.isDef(options.url)) {
xhrIo = new goog.net.XhrIo();
xhrIo.setResponseType(responseType);
goog.events.listen(xhrIo, goog.net.EventType.COMPLETE,
goog.bind(this.handleXhrIo_, this));
xhrIo.send(options.url);
}
if (goog.isDef(options.urls)) {
var urls = options.urls;
var i, ii;
for (i = 0, ii = urls.length; i < ii; ++i) {
xhrIo = new goog.net.XhrIo();
xhrIo.setResponseType(responseType);
goog.events.listen(xhrIo, goog.net.EventType.COMPLETE,
goog.bind(this.handleXhrIo_, this));
xhrIo.send(urls[i]);
}
}
}
};
goog.inherits(ol.source.VectorFile, ol.source.Vector);
/**
* @param {Event} event Event.
* @private
*/
ol.source.VectorFile.prototype.handleXhrIo_ = function(event) {
var xhrIo = event.target;
goog.asserts.assertInstanceof(xhrIo, goog.net.XhrIo);
if (xhrIo.isSuccess()) {
var type = this.format.getType();
/** @type {ArrayBuffer|Document|Node|Object|string|undefined} */
var source;
if (type == ol.format.FormatType.BINARY &&
ol.BrowserFeature.HAS_ARRAY_BUFFER) {
source = xhrIo.getResponse();
goog.asserts.assertInstanceof(source, ArrayBuffer);
} else if (type == ol.format.FormatType.JSON) {
source = xhrIo.getResponseText();
} else if (type == ol.format.FormatType.TEXT) {
source = xhrIo.getResponseText();
} else if (type == ol.format.FormatType.XML) {
if (!goog.userAgent.IE) {
source = xhrIo.getResponseXml();
}
if (!goog.isDefAndNotNull(source)) {
source = ol.xml.load(xhrIo.getResponseText());
}
} else {
goog.asserts.fail();
}
goog.dispose(xhrIo);
if (goog.isDefAndNotNull(source)) {
this.readFeatures_(source);
} else {
this.setState(ol.source.State.ERROR);
goog.asserts.fail();
}
} else {
this.setState(ol.source.State.ERROR);
}
};
/**
* @param {ArrayBuffer|Document|Node|Object|string} source Source.
* @private
*/
ol.source.VectorFile.prototype.readFeatures_ = function(source) {
var format = this.format;
var features = format.readFeatures(source);
var featureProjection = format.readProjection(source);
var projection = this.getProjection();
if (!goog.isNull(projection)) {
if (!ol.proj.equivalent(featureProjection, projection)) {
var transform = ol.proj.getTransform(featureProjection, projection);
var i, ii;
for (i = 0, ii = features.length; i < ii; ++i) {
var feature = features[i];
var geometry = feature.getGeometry();
if (!goog.isNull(geometry)) {
geometry.transform(transform);
}
}
}
}
this.addFeaturesInternal(features);
this.setState(ol.source.State.READY);
};

View File

@@ -13,6 +13,7 @@ goog.require('goog.events.Event');
goog.require('goog.events.EventType');
goog.require('goog.object');
goog.require('ol.ObjectEventType');
goog.require('ol.proj');
goog.require('ol.source.Source');
goog.require('ol.structs.RBush');
@@ -207,6 +208,21 @@ ol.source.Vector.prototype.forEachFeatureInExtent =
};
/**
* @param {ol.Extent} extent Extent.
* @param {number} resolution Resolution.
* @param {function(this: T, ol.Feature): S} f Callback.
* @param {T=} opt_this The object to use as `this` in `f`.
* @return {S|undefined}
* @template T,S
* @todo stability experimental
*/
ol.source.Vector.prototype.forEachFeatureInExtentAtResolution =
function(extent, resolution, f, opt_this) {
return this.forEachFeatureInExtent(extent, f, opt_this);
};
/**
* @return {Array.<ol.Feature>} Features.
* @todo stability experimental
@@ -337,6 +353,14 @@ ol.source.Vector.prototype.isEmpty = function() {
};
/**
* @param {ol.Extent} extent Extent.
* @param {number} resolution Resolution.
* @param {ol.proj.Projection} projection Projection.
*/
ol.source.Vector.prototype.loadFeatures = goog.nullFunction;
/**
* @param {ol.Feature} feature Feature.
* @todo stability experimental

View File

@@ -10,11 +10,10 @@ goog.require('ol.TileCoord');
* A function that takes an {@link ol.TileCoord} for the tile coordinate,
* a `{number}` representing the pixel ratio and an {@link ol.proj.Projection}
* for the projection as arguments and returns a `{string}` or
* undefined representing the tile URL. The this keyword inside the function
* references the {@link ol.source.TileImage}.
* undefined representing the tile URL.
*
* @typedef {function(this: ol.source.TileImage, ol.TileCoord,
* number, ol.proj.Projection): (string|undefined)}
* @typedef {function(ol.TileCoord, number,
* ol.proj.Projection): (string|undefined)}
* @todo stability experimental
*/
ol.TileUrlFunctionType;
@@ -34,7 +33,6 @@ ol.TileCoordTransformType;
ol.TileUrlFunction.createFromTemplate = function(template) {
return (
/**
* @this {ol.source.TileImage}
* @param {ol.TileCoord} tileCoord Tile Coordinate.
* @param {number} pixelRatio Pixel ratio.
* @param {ol.proj.Projection} projection Projection.
@@ -72,7 +70,6 @@ ol.TileUrlFunction.createFromTileUrlFunctions = function(tileUrlFunctions) {
}
return (
/**
* @this {ol.source.TileImage}
* @param {ol.TileCoord} tileCoord Tile Coordinate.
* @param {number} pixelRatio Pixel ratio.
* @param {ol.proj.Projection} projection Projection.
@@ -84,15 +81,13 @@ ol.TileUrlFunction.createFromTileUrlFunctions = function(tileUrlFunctions) {
} else {
var index =
goog.math.modulo(tileCoord.hash(), tileUrlFunctions.length);
return tileUrlFunctions[index].call(
this, tileCoord, pixelRatio, projection);
return tileUrlFunctions[index](tileCoord, pixelRatio, projection);
}
});
};
/**
* @this {ol.source.TileImage}
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @param {number} pixelRatio Pixel ratio.
* @param {ol.proj.Projection} projection Projection.
@@ -114,7 +109,6 @@ ol.TileUrlFunction.withTileCoordTransform =
var tmpTileCoord = new ol.TileCoord(0, 0, 0);
return (
/**
* @this {ol.source.TileImage}
* @param {ol.TileCoord} tileCoord Tile Coordinate.
* @param {number} pixelRatio Pixel ratio.
* @param {ol.proj.Projection} projection Projection.
@@ -124,9 +118,8 @@ ol.TileUrlFunction.withTileCoordTransform =
if (goog.isNull(tileCoord)) {
return undefined;
} else {
return tileUrlFunction.call(
this,
transformFn.call(this, tileCoord, projection, tmpTileCoord),
return tileUrlFunction(
transformFn(tileCoord, projection, tmpTileCoord),
pixelRatio,
projection);
}