Converting control to an interaction

To dispatch events, the interaction base class now inherits from
goog.events.EventTarget.
This commit is contained in:
ahocevar
2013-08-26 17:03:14 +02:00
parent c6e61e2d23
commit 100b85a7b0
10 changed files with 281 additions and 438 deletions

View File

@@ -9,52 +9,6 @@
<link rel="stylesheet" href="../resources/layout.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"> <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
<title>Select features example</title> <title>Select features example</title>
<style type="text/css">
/* TODO: remove this after css/button refactoring */
.ol-select {
position: absolute;
background: rgba(255,255,255,0.4);
border-radius: 4px;
left: 8px;
padding: 2px;
top: 65px;
}
@media print {
.ol-select {
display: none;
}
}
.ol-select a {
display: block;
margin: 1px;
padding: 0;
color: white;
font-size: 16px;
font-family: 'Lucida Grande',Verdana,Geneva,Lucida,Arial,Helvetica,sans-serif;
font-weight: bold;
text-decoration: none;
text-align: center;
height: 22px;
width: 22px;
background-color: rgba(0, 60, 136, 0.2);
border-radius: 2px;
}
.ol-touch .ol-select a {
font-size: 20px;
height: 30px;
width: 30px;
line-height: 26px;
}
.ol-select.active a {
background-color: rgba(0, 60, 136, 0.6);
}
.ol-select a:hover {
background-color: rgba(0, 60, 136, 0.7);
}
.ol-select a:after {
content: "S";
}
</style>
</head> </head>
<body> <body>
@@ -84,7 +38,7 @@
<div class="span12"> <div class="span12">
<h4 id="title">Select features example</h4> <h4 id="title">Select features example</h4>
<p id="shortdesc">Example of using the Select control. Select features by clicking polygons. Hold the Shift-key to add to the selection. Click the 'S' button to toggle the control's active state.</p> <p id="shortdesc">Example of using the Select interaction. Select features by clicking polygons. Hold the Shift-key to add to the selection.</p>
<div id="docs"> <div id="docs">
<p>See the <a href="select-features.js" target="_blank">select-features.js source</a> to see how this is done.</p> <p>See the <a href="select-features.js" target="_blank">select-features.js source</a> to see how this is done.</p>
</div> </div>

View File

@@ -1,8 +1,8 @@
goog.require('ol.Map'); goog.require('ol.Map');
goog.require('ol.RendererHint'); goog.require('ol.RendererHint');
goog.require('ol.View2D'); goog.require('ol.View2D');
goog.require('ol.control.Select'); goog.require('ol.interaction.Select');
goog.require('ol.control.defaults'); goog.require('ol.interaction.defaults');
goog.require('ol.layer.TileLayer'); goog.require('ol.layer.TileLayer');
goog.require('ol.layer.Vector'); goog.require('ol.layer.Vector');
goog.require('ol.parser.ogc.GML_v3'); goog.require('ol.parser.ogc.GML_v3');
@@ -46,12 +46,12 @@ var vector = new ol.layer.Vector({
}) })
}); });
var selectControl = new ol.control.Select({ var selectInteraction = new ol.interaction.Select({
layerFilter: function(layer) { return layer === vector; } layerFilter: function(layer) { return layer === vector; }
}); });
var map = new ol.Map({ var map = new ol.Map({
controls: ol.control.defaults().extend([selectControl]), interactions: ol.interaction.defaults().extend([selectInteraction]),
layers: [raster, vector], layers: [raster, vector],
renderer: ol.RendererHint.CANVAS, renderer: ol.RendererHint.CANVAS,
target: 'map', target: 'map',
@@ -60,5 +60,3 @@ var map = new ol.Map({
zoom: 4 zoom: 4
}) })
}); });
selectControl.activate();

View File

@@ -201,16 +201,6 @@
* Default is '' (empty string). * Default is '' (empty string).
*/ */
/**
* @typedef {Object} ol.control.SelectOptions
* @property {string|undefined} className CSS class name. Default is 'ol-select'.
* @property {Element|undefined} element Element.
* @property {undefined|function(ol.layer.Layer):boolean} layerFilter Filter
* function to restrict selection to a subset of layers.
* @property {ol.Map|undefined} map Map.
* @property {Element|undefined} target Target.
*/
/** /**
* @typedef {Object} ol.control.ScaleLineOptions * @typedef {Object} ol.control.ScaleLineOptions
* @property {string|undefined} className CSS Class name. Default is 'ol-scale-line'. * @property {string|undefined} className CSS Class name. Default is 'ol-scale-line'.
@@ -305,6 +295,12 @@
* @property {number|undefined} delta Delta. * @property {number|undefined} delta Delta.
*/ */
/**
* @typedef {Object} ol.interaction.SelectOptions
* @property {undefined|function(ol.layer.Layer):boolean} layerFilter Filter
* function to restrict selection to a subset of layers.
*/
/** /**
* @typedef {Object} ol.interaction.TouchPanOptions * @typedef {Object} ol.interaction.TouchPanOptions
* @property {ol.Kinetic|undefined} kinetic Kinetic. * @property {ol.Kinetic|undefined} kinetic Kinetic.

View File

@@ -1,3 +0,0 @@
@exportClass ol.control.Select ol.control.SelectOptions
@exportProperty ol.control.Select.prototype.activate
@exportProperty ol.control.Select.prototype.deactivate

View File

@@ -1,235 +0,0 @@
goog.provide('ol.control.Select');
goog.require('goog.array');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.dom.classes');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('ol.MapBrowserEvent.EventType');
goog.require('ol.control.Control');
goog.require('ol.css');
goog.require('ol.interaction.condition');
goog.require('ol.layer.Vector');
goog.require('ol.layer.VectorLayerRenderIntent');
goog.require('ol.source.Vector');
/**
* @typedef {{layer: ol.layer.Layer,
* selected: (Array.<ol.Feature>|undefined),
* type: goog.events.EventType,
* unselected: (Array.<ol.Feature>|undefined)}}
*/
ol.control.SelectEventObject;
/**
* @constructor
* @extends {ol.control.Control}
* @param {ol.control.SelectOptions=} opt_options Options.
*/
ol.control.Select = function(opt_options) {
var options = goog.isDef(opt_options) ? opt_options : {};
/**
* @type {boolean}
* @private
*/
this.active_ = false;
/**
* Mapping between original features and cloned features on selection layers.
* @type {Object.<*,Object.<string,ol.Feature>>}
* @private
*/
this.featureMap_ = {};
/**
* Mapping between original layers and selection layers.
* @type {Object.<*,ol.layer.Vector>}
* @protected
*/
this.selectionLayers = {};
/**
* @type {null|function(ol.layer.Layer):boolean}
* @private
*/
this.layerFilter_ = goog.isDef(options.layerFilter) ?
options.layerFilter : null;
// TODO: css/button refactoring
var className = goog.isDef(options.className) ? options.className :
'ol-select';
var element = goog.dom.createDom(goog.dom.TagName.DIV, {
'class': className + ' ' + ol.css.CLASS_UNSELECTABLE
});
var button = goog.dom.createDom(goog.dom.TagName.A, {
'href': '#Select'
});
goog.dom.appendChild(element, button);
goog.events.listen(element, [
goog.events.EventType.TOUCHEND,
goog.events.EventType.CLICK
], this.toggleActive_, false, this);
goog.base(this, {
element: element,
map: options.map,
target: options.target
});
};
goog.inherits(ol.control.Select, ol.control.Control);
/**
* @param {goog.events.BrowserEvent} browserEvent Browser event.
* @private
*/
ol.control.Select.prototype.toggleActive_ = function(browserEvent) {
// prevent #Select anchor from getting appended to the url
browserEvent.preventDefault();
if (this.active_) {
this.deactivate();
} else {
this.activate();
}
};
/**
* Activate the control.
*/
ol.control.Select.prototype.activate = function() {
if (!this.active_) {
this.active_ = true;
goog.dom.classes.add(this.element, 'active');
var map = this.getMap();
for (var i in this.selectionLayers) {
map.addLayer(this.selectionLayers[i]);
}
// TODO: Implement box selection
this.listenerKeys.push(
goog.events.listen(map, ol.MapBrowserEvent.EventType.CLICK,
this.handleClick, true, this));
}
};
/**
* Dectivate the control.
*/
ol.control.Select.prototype.deactivate = function() {
if (this.active_) {
if (!goog.array.isEmpty(this.listenerKeys)) {
goog.array.forEach(this.listenerKeys, goog.events.unlistenByKey);
this.listenerKeys.length = 0;
}
var map = this.getMap();
for (var i in this.selectionLayers) {
map.removeLayer(this.selectionLayers[i]);
}
goog.dom.classes.remove(this.element, 'active');
this.active_ = false;
}
};
/**
* @param {ol.MapBrowserEvent} evt Event.
*/
ol.control.Select.prototype.handleClick = function(evt) {
var map = this.getMap();
var layers = map.getLayerGroup().getLayersArray();
if (!goog.isNull(this.layerFilter_)) {
layers = goog.array.filter(layers, this.layerFilter_);
}
var clear = !ol.interaction.condition.shiftKeyOnly(evt.browserEvent);
function select(featuresByLayer) {
this.select(featuresByLayer, layers, clear);
}
map.getFeatures({
layers: layers,
pixel: evt.getPixel(),
success: goog.bind(select, this)
});
};
/**
* @param {Array.<Array.<ol.Feature>>} featuresByLayer Features by layer.
* @param {Array.<ol.layer.Layer>} layers The queried layers.
* @param {boolean} clear Whether the current layer content should be cleared.
*/
ol.control.Select.prototype.select = function(featuresByLayer, layers, clear) {
for (var i = 0, ii = featuresByLayer.length; i < ii; ++i) {
var layer = layers[i];
var layerId = goog.getUid(layer);
var selectionLayer = this.selectionLayers[layerId];
if (!goog.isDef(selectionLayer)) {
selectionLayer = new ol.layer.Vector({
source: new ol.source.Vector({parser: null}),
style: layer.getStyle()
});
selectionLayer.setTemporary(true);
this.getMap().addLayer(selectionLayer);
this.selectionLayers[layerId] = selectionLayer;
this.featureMap_[layerId] = {};
}
var features = featuresByLayer[i];
var numFeatures = features.length;
var selectedFeatures = [];
var featuresToAdd = [];
var unselectedFeatures = [];
var featuresToRemove = [];
var featureMap = this.featureMap_[layerId];
for (var j = 0; j < numFeatures; ++j) {
var feature = features[j];
var featureId = goog.getUid(feature);
var clone = featureMap[featureId];
if (clone) {
// TODO: make toggle configurable
unselectedFeatures.push(feature);
featuresToRemove.push(clone);
delete featureMap[featureId];
}
if (clear) {
for (var f in featureMap) {
unselectedFeatures.push(layer.getFeatureWithUid(f));
featuresToRemove.push(featureMap[f]);
}
featureMap = {};
this.featureMap_[layerId] = featureMap;
}
if (!clone) {
clone = feature.clone();
featureMap[featureId] = clone;
clone.renderIntent = ol.layer.VectorLayerRenderIntent.SELECTED;
selectedFeatures.push(feature);
featuresToAdd.push(clone);
}
}
if (goog.isFunction(layer.setRenderIntent)) {
layer.setRenderIntent(ol.layer.VectorLayerRenderIntent.HIDDEN,
selectedFeatures);
layer.setRenderIntent(ol.layer.VectorLayerRenderIntent.DEFAULT,
unselectedFeatures);
}
selectionLayer.removeFeatures(featuresToRemove);
selectionLayer.addFeatures(featuresToAdd);
this.dispatchEvent(/** @type {ol.control.SelectEventObject} */ ({
layer: layer,
selected: selectedFeatures,
type: goog.events.EventType.CHANGE,
unselected: unselectedFeatures
}));
}
};

View File

@@ -2,6 +2,7 @@
goog.provide('ol.interaction.Interaction'); goog.provide('ol.interaction.Interaction');
goog.require('goog.events.EventTarget');
goog.require('ol.MapBrowserEvent'); goog.require('ol.MapBrowserEvent');
goog.require('ol.animation.pan'); goog.require('ol.animation.pan');
goog.require('ol.animation.rotate'); goog.require('ol.animation.rotate');
@@ -12,9 +13,12 @@ goog.require('ol.easing');
/** /**
* @constructor * @constructor
* @extends {goog.events.EventTarget}
*/ */
ol.interaction.Interaction = function() { ol.interaction.Interaction = function() {
goog.base(this);
}; };
goog.inherits(ol.interaction.Interaction, goog.events.EventTarget);
/** /**

View File

@@ -0,0 +1,2 @@
@exportClass ol.interaction.Select ol.interaction.SelectOptions
@exportProperty ol.interaction.Select.prototype.dispose

View File

@@ -0,0 +1,180 @@
goog.provide('ol.interaction.Select');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('ol.MapBrowserEvent.EventType');
goog.require('ol.interaction.Interaction');
goog.require('ol.interaction.condition');
goog.require('ol.layer.Vector');
goog.require('ol.layer.VectorLayerRenderIntent');
goog.require('ol.source.Vector');
/**
* @typedef {{layer: ol.layer.Layer,
* map: ol.Map,
* selected: (Array.<ol.Feature>|undefined),
* type: goog.events.EventType,
* unselected: (Array.<ol.Feature>|undefined)}}
*/
ol.interaction.SelectEventObject;
/**
* @constructor
* @extends {ol.interaction.Interaction}
* @param {ol.interaction.SelectOptions=} opt_options Options.
*/
ol.interaction.Select = function(opt_options) {
var options = goog.isDef(opt_options) ? opt_options : {};
/**
* Mapping between original features and cloned features on selection layers.
* @type {Object.<*,Object.<*,ol.Feature>>}
* @private
*/
this.featureMap_ = {};
/**
* Mapping between original layers and selection layers, by map.
* @type {Object.<*,{map:ol.Map,layers:Object.<*,ol.layer.Vector>}>}
* @protected
*/
this.selectionLayers = {};
/**
* @type {null|function(ol.layer.Layer):boolean}
* @private
*/
this.layerFilter_ = goog.isDef(options.layerFilter) ?
options.layerFilter : null;
goog.base(this);
};
goog.inherits(ol.interaction.Select, ol.interaction.Interaction);
/**
* @inheritDoc
*/
ol.interaction.Select.prototype.disposeInternal = function() {
for (var m in this.selectionLayers) {
var selectionLayers = this.selectionLayers[m].layers;
var map = this.selectionLayers[m].map;
for (var l in selectionLayers) {
map.removeLayer(selectionLayers[l]);
}
}
goog.base(this, 'disposeInternal');
};
/**
* @inheritDoc.
*/
ol.interaction.Select.prototype.handleMapBrowserEvent = function(evt) {
if (evt.type === ol.MapBrowserEvent.EventType.CLICK) {
var map = evt.map;
var layers = map.getLayerGroup().getLayersArray();
if (!goog.isNull(this.layerFilter_)) {
layers = goog.array.filter(layers, this.layerFilter_);
}
var clear = !ol.interaction.condition.shiftKeyOnly(evt.browserEvent);
var select = function(featuresByLayer) {
this.select(map, featuresByLayer, layers, clear);
};
map.getFeatures({
layers: layers,
pixel: evt.getPixel(),
success: goog.bind(select, this)
});
}
// TODO: Implement box selection
return true;
};
/**
* @param {ol.Map} map The map where the selction event originated.
* @param {Array.<Array.<ol.Feature>>} featuresByLayer Features by layer.
* @param {Array.<ol.layer.Layer>} layers The queried layers.
* @param {boolean} clear Whether the current layer content should be cleared.
*/
ol.interaction.Select.prototype.select =
function(map, featuresByLayer, layers, clear) {
var mapId = goog.getUid(map);
for (var i = 0, ii = featuresByLayer.length; i < ii; ++i) {
var layer = layers[i];
var layerId = goog.getUid(layer);
if (!(mapId in this.selectionLayers)) {
this.selectionLayers[mapId] = {map: map, layers: {}};
}
var selectionLayer = this.selectionLayers[mapId].layers[layerId];
if (!goog.isDef(selectionLayer)) {
goog.asserts.assertFunction(layer.getStyle,
'At least one of the layers has no "getStyle()" function.');
selectionLayer = new ol.layer.Vector({
source: new ol.source.Vector({parser: null}),
style: layer.getStyle()
});
selectionLayer.setTemporary(true);
map.addLayer(selectionLayer);
this.selectionLayers[mapId].layers[layerId] = selectionLayer;
this.featureMap_[layerId] = {};
}
var features = featuresByLayer[i];
var numFeatures = features.length;
var selectedFeatures = [];
var featuresToAdd = [];
var unselectedFeatures = [];
var featuresToRemove = [];
var featureMap = this.featureMap_[layerId];
var oldFeatureMap = featureMap;
if (clear) {
for (var f in featureMap) {
unselectedFeatures.push(layer.getFeatureWithUid(f));
featuresToRemove.push(featureMap[f]);
}
featureMap = {};
this.featureMap_[layerId] = featureMap;
}
for (var j = 0; j < numFeatures; ++j) {
var feature = features[j];
var featureId = goog.getUid(feature);
var clone = featureMap[featureId];
if (clone) {
// TODO: make toggle configurable
unselectedFeatures.push(feature);
featuresToRemove.push(clone);
delete featureMap[featureId];
} else if (!(featureId in oldFeatureMap)) {
clone = feature.clone();
featureMap[featureId] = clone;
clone.renderIntent = ol.layer.VectorLayerRenderIntent.SELECTED;
selectedFeatures.push(feature);
featuresToAdd.push(clone);
}
}
if (goog.isFunction(layer.setRenderIntent)) {
layer.setRenderIntent(ol.layer.VectorLayerRenderIntent.HIDDEN,
selectedFeatures);
layer.setRenderIntent(ol.layer.VectorLayerRenderIntent.DEFAULT,
unselectedFeatures);
}
selectionLayer.removeFeatures(featuresToRemove);
selectionLayer.addFeatures(featuresToAdd);
this.dispatchEvent(/** @type {ol.interaction.SelectEventObject} */ ({
layer: layer,
map: map,
selected: selectedFeatures,
type: goog.events.EventType.CHANGE,
unselected: unselectedFeatures
}));
}
};

View File

@@ -1,137 +0,0 @@
goog.provide('ol.test.control.Select');
describe('ol.control.Select', function() {
var map, target, select, vector, features;
beforeEach(function() {
target = document.createElement('div');
target.style.width = '256px';
target.style.height = '256px';
document.body.appendChild(target);
map = new ol.Map({
target: target
});
features = ol.parser.GeoJSON.read(JSON.stringify({
'type': 'FeatureCollection',
'features': [{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [-1, 1]
}
}, {
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [1, -1]
}
}]
}));
vector = new ol.layer.Vector({source: new ol.source.Vector({})});
vector.addFeatures(features);
select = new ol.control.Select({
layerFilter: function(layer) { return layer === vector; },
map: map
});
});
afterEach(function() {
goog.dispose(select);
goog.dispose(map);
document.body.removeChild(target);
select = null;
map = null;
target = null;
});
describe('DOM creation', function() {
it('creates the expected DOM elements', function() {
var selectButtons = goog.dom.getElementsByClass('ol-select', target),
selectButton = selectButtons[0],
hasUnselectableCls;
expect(selectButtons.length).to.be(1);
hasUnselectableCls = goog.dom.classes.has(selectButton,
'ol-unselectable');
expect(hasUnselectableCls).to.be(true);
});
it('has an .active class only when activated', function() {
var selectButton = goog.dom.getElementsByClass('ol-select', target)[0];
select.activate();
expect(goog.dom.classes.has(selectButton, 'active')).to.be(true);
select.deactivate();
expect(goog.dom.classes.has(selectButton, 'active')).to.be(false);
});
});
describe('#activate and #deactivate', function() {
it('adds a temp layer to the map only when active and in use', function() {
expect(map.getLayers().getLength()).to.be(0);
select.activate();
expect(map.getLayers().getLength()).to.be(0);
select.select([features[0]], [vector]);
expect(map.getLayers().getLength()).to.be(1);
expect(map.getLayers().getAt(0).getTemporary()).to.be(true);
select.deactivate();
expect(map.getLayers().getLength()).to.be(0);
});
it('has a private property so it knows if it is active', function() {
expect(select.active_).to.be(false);
select.activate();
expect(select.active_).to.be(true);
select.deactivate();
expect(select.active_).to.be(false);
});
it('toggles active state on click', function() {
var selectButton = goog.dom.getElementsByClass('ol-select', target)[0];
var event = new goog.events.BrowserEvent({type: 'click'});
goog.events.fireListeners(selectButton, event.type, false, event);
expect(select.active_).to.be(true);
goog.events.fireListeners(selectButton, event.type, false, event);
expect(select.active_).to.be(false);
});
});
describe('#select', function() {
it('toggles selection of features', function() {
select.select([features], [vector]);
var layer = select.selectionLayers[goog.getUid(vector)];
expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(2);
select.select([features], [vector]);
expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(0);
});
it('can append features to an existing selection', function() {
select.select([[features[0]]], [vector]);
select.select([[features[1]]], [vector]);
var layer = select.selectionLayers[goog.getUid(vector)];
expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(2);
});
it('can clear a selection before selecting new features', function() {
select.select([[features[0]]], [vector], true);
select.select([[features[1]]], [vector], true);
var layer = select.selectionLayers[goog.getUid(vector)];
expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(1);
});
});
});
goog.require('goog.dispose');
goog.require('goog.dom');
goog.require('goog.dom.classes');
goog.require('goog.events');
goog.require('goog.events.BrowserEvent');
goog.require('goog.object');
goog.require('ol.Map');
goog.require('ol.control.Select');
goog.require('ol.layer.Vector');
goog.require('ol.parser.GeoJSON');
goog.require('ol.source.Vector');

View File

@@ -0,0 +1,84 @@
goog.provide('ol.test.interaction.Select');
describe('ol.interaction.Select', function() {
var map, target, select, vector, features;
beforeEach(function() {
target = document.createElement('div');
target.style.width = '256px';
target.style.height = '256px';
document.body.appendChild(target);
map = new ol.Map({
target: target
});
features = ol.parser.GeoJSON.read(JSON.stringify({
'type': 'FeatureCollection',
'features': [{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [-1, 1]
}
}, {
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [1, -1]
}
}]
}));
vector = new ol.layer.Vector({source: new ol.source.Vector({})});
vector.addFeatures(features);
select = new ol.interaction.Select({
layerFilter: function(layer) { return layer === vector; }
});
map.getInteractions().push(select);
});
afterEach(function() {
goog.dispose(select);
goog.dispose(map);
document.body.removeChild(target);
select = null;
map = null;
target = null;
});
describe('#select', function() {
it('toggles selection of features', function() {
select.select(map, [features], [vector]);
var layer = select.selectionLayers[goog.getUid(map)]
.layers[goog.getUid(vector)];
expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(2);
select.select(map, [features], [vector]);
expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(0);
});
it('can append features to an existing selection', function() {
select.select(map, [[features[0]]], [vector]);
select.select(map, [[features[1]]], [vector]);
var layer = select.selectionLayers[goog.getUid(map)]
.layers[goog.getUid(vector)];
expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(2);
});
it('can clear a selection before selecting new features', function() {
select.select(map, [[features[0]]], [vector], true);
select.select(map, [[features[1]]], [vector], true);
var layer = select.selectionLayers[goog.getUid(map)]
.layers[goog.getUid(vector)];
expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(1);
});
});
});
goog.require('goog.dispose');
goog.require('goog.object');
goog.require('ol.Map');
goog.require('ol.interaction.Select');
goog.require('ol.layer.Vector');
goog.require('ol.parser.GeoJSON');
goog.require('ol.source.Vector');