Automated class transform

npx lebab --replace src --transform class
This commit is contained in:
Tim Schaub
2018-07-16 16:18:16 -06:00
parent 60e85e7d89
commit 7b4a73f3b9
145 changed files with 32887 additions and 33714 deletions

View File

@@ -65,236 +65,225 @@ inherits(CollectionEvent, Event);
* @template T
* @api
*/
const Collection = function(opt_array, opt_options) {
class Collection {
constructor(opt_array, opt_options) {
BaseObject.call(this);
BaseObject.call(this);
const options = opt_options || {};
const options = opt_options || {};
/**
* @private
* @type {boolean}
*/
this.unique_ = !!options.unique;
/**
* @private
* @type {!Array.<T>}
*/
this.array_ = opt_array ? opt_array : [];
if (this.unique_) {
for (let i = 0, ii = this.array_.length; i < ii; ++i) {
this.assertUnique_(this.array_[i], i);
}
}
this.updateLength_();
}
/**
* @private
* @type {boolean}
* Remove all elements from the collection.
* @api
*/
this.unique_ = !!options.unique;
/**
* @private
* @type {!Array.<T>}
*/
this.array_ = opt_array ? opt_array : [];
if (this.unique_) {
for (let i = 0, ii = this.array_.length; i < ii; ++i) {
this.assertUnique_(this.array_[i], i);
clear() {
while (this.getLength() > 0) {
this.pop();
}
}
this.updateLength_();
/**
* Add elements to the collection. This pushes each item in the provided array
* to the end of the collection.
* @param {!Array.<T>} arr Array.
* @return {module:ol/Collection.<T>} This collection.
* @api
*/
extend(arr) {
for (let i = 0, ii = arr.length; i < ii; ++i) {
this.push(arr[i]);
}
return this;
}
};
/**
* Iterate over each element, calling the provided callback.
* @param {function(T, number, Array.<T>): *} f The function to call
* for every element. This function takes 3 arguments (the element, the
* index and the array). The return value is ignored.
* @api
*/
forEach(f) {
const array = this.array_;
for (let i = 0, ii = array.length; i < ii; ++i) {
f(array[i], i, array);
}
}
/**
* Get a reference to the underlying Array object. Warning: if the array
* is mutated, no events will be dispatched by the collection, and the
* collection's "length" property won't be in sync with the actual length
* of the array.
* @return {!Array.<T>} Array.
* @api
*/
getArray() {
return this.array_;
}
/**
* Get the element at the provided index.
* @param {number} index Index.
* @return {T} Element.
* @api
*/
item(index) {
return this.array_[index];
}
/**
* Get the length of this collection.
* @return {number} The length of the array.
* @observable
* @api
*/
getLength() {
return /** @type {number} */ (this.get(Property.LENGTH));
}
/**
* Insert an element at the provided index.
* @param {number} index Index.
* @param {T} elem Element.
* @api
*/
insertAt(index, elem) {
if (this.unique_) {
this.assertUnique_(elem);
}
this.array_.splice(index, 0, elem);
this.updateLength_();
this.dispatchEvent(
new CollectionEvent(CollectionEventType.ADD, elem));
}
/**
* Remove the last element of the collection and return it.
* Return `undefined` if the collection is empty.
* @return {T|undefined} Element.
* @api
*/
pop() {
return this.removeAt(this.getLength() - 1);
}
/**
* Insert the provided element at the end of the collection.
* @param {T} elem Element.
* @return {number} New length of the collection.
* @api
*/
push(elem) {
if (this.unique_) {
this.assertUnique_(elem);
}
const n = this.getLength();
this.insertAt(n, elem);
return this.getLength();
}
/**
* Remove the first occurrence of an element from the collection.
* @param {T} elem Element.
* @return {T|undefined} The removed element or undefined if none found.
* @api
*/
remove(elem) {
const arr = this.array_;
for (let i = 0, ii = arr.length; i < ii; ++i) {
if (arr[i] === elem) {
return this.removeAt(i);
}
}
return undefined;
}
/**
* Remove the element at the provided index and return it.
* Return `undefined` if the collection does not contain this index.
* @param {number} index Index.
* @return {T|undefined} Value.
* @api
*/
removeAt(index) {
const prev = this.array_[index];
this.array_.splice(index, 1);
this.updateLength_();
this.dispatchEvent(new CollectionEvent(CollectionEventType.REMOVE, prev));
return prev;
}
/**
* Set the element at the provided index.
* @param {number} index Index.
* @param {T} elem Element.
* @api
*/
setAt(index, elem) {
const n = this.getLength();
if (index < n) {
if (this.unique_) {
this.assertUnique_(elem, index);
}
const prev = this.array_[index];
this.array_[index] = elem;
this.dispatchEvent(
new CollectionEvent(CollectionEventType.REMOVE, prev));
this.dispatchEvent(
new CollectionEvent(CollectionEventType.ADD, elem));
} else {
for (let j = n; j < index; ++j) {
this.insertAt(j, undefined);
}
this.insertAt(index, elem);
}
}
/**
* @private
*/
updateLength_() {
this.set(Property.LENGTH, this.array_.length);
}
/**
* @private
* @param {T} elem Element.
* @param {number=} opt_except Optional index to ignore.
*/
assertUnique_(elem, opt_except) {
for (let i = 0, ii = this.array_.length; i < ii; ++i) {
if (this.array_[i] === elem && i !== opt_except) {
throw new AssertionError(58);
}
}
}
}
inherits(Collection, BaseObject);
/**
* Remove all elements from the collection.
* @api
*/
Collection.prototype.clear = function() {
while (this.getLength() > 0) {
this.pop();
}
};
/**
* Add elements to the collection. This pushes each item in the provided array
* to the end of the collection.
* @param {!Array.<T>} arr Array.
* @return {module:ol/Collection.<T>} This collection.
* @api
*/
Collection.prototype.extend = function(arr) {
for (let i = 0, ii = arr.length; i < ii; ++i) {
this.push(arr[i]);
}
return this;
};
/**
* Iterate over each element, calling the provided callback.
* @param {function(T, number, Array.<T>): *} f The function to call
* for every element. This function takes 3 arguments (the element, the
* index and the array). The return value is ignored.
* @api
*/
Collection.prototype.forEach = function(f) {
const array = this.array_;
for (let i = 0, ii = array.length; i < ii; ++i) {
f(array[i], i, array);
}
};
/**
* Get a reference to the underlying Array object. Warning: if the array
* is mutated, no events will be dispatched by the collection, and the
* collection's "length" property won't be in sync with the actual length
* of the array.
* @return {!Array.<T>} Array.
* @api
*/
Collection.prototype.getArray = function() {
return this.array_;
};
/**
* Get the element at the provided index.
* @param {number} index Index.
* @return {T} Element.
* @api
*/
Collection.prototype.item = function(index) {
return this.array_[index];
};
/**
* Get the length of this collection.
* @return {number} The length of the array.
* @observable
* @api
*/
Collection.prototype.getLength = function() {
return /** @type {number} */ (this.get(Property.LENGTH));
};
/**
* Insert an element at the provided index.
* @param {number} index Index.
* @param {T} elem Element.
* @api
*/
Collection.prototype.insertAt = function(index, elem) {
if (this.unique_) {
this.assertUnique_(elem);
}
this.array_.splice(index, 0, elem);
this.updateLength_();
this.dispatchEvent(
new CollectionEvent(CollectionEventType.ADD, elem));
};
/**
* Remove the last element of the collection and return it.
* Return `undefined` if the collection is empty.
* @return {T|undefined} Element.
* @api
*/
Collection.prototype.pop = function() {
return this.removeAt(this.getLength() - 1);
};
/**
* Insert the provided element at the end of the collection.
* @param {T} elem Element.
* @return {number} New length of the collection.
* @api
*/
Collection.prototype.push = function(elem) {
if (this.unique_) {
this.assertUnique_(elem);
}
const n = this.getLength();
this.insertAt(n, elem);
return this.getLength();
};
/**
* Remove the first occurrence of an element from the collection.
* @param {T} elem Element.
* @return {T|undefined} The removed element or undefined if none found.
* @api
*/
Collection.prototype.remove = function(elem) {
const arr = this.array_;
for (let i = 0, ii = arr.length; i < ii; ++i) {
if (arr[i] === elem) {
return this.removeAt(i);
}
}
return undefined;
};
/**
* Remove the element at the provided index and return it.
* Return `undefined` if the collection does not contain this index.
* @param {number} index Index.
* @return {T|undefined} Value.
* @api
*/
Collection.prototype.removeAt = function(index) {
const prev = this.array_[index];
this.array_.splice(index, 1);
this.updateLength_();
this.dispatchEvent(new CollectionEvent(CollectionEventType.REMOVE, prev));
return prev;
};
/**
* Set the element at the provided index.
* @param {number} index Index.
* @param {T} elem Element.
* @api
*/
Collection.prototype.setAt = function(index, elem) {
const n = this.getLength();
if (index < n) {
if (this.unique_) {
this.assertUnique_(elem, index);
}
const prev = this.array_[index];
this.array_[index] = elem;
this.dispatchEvent(
new CollectionEvent(CollectionEventType.REMOVE, prev));
this.dispatchEvent(
new CollectionEvent(CollectionEventType.ADD, elem));
} else {
for (let j = n; j < index; ++j) {
this.insertAt(j, undefined);
}
this.insertAt(index, elem);
}
};
/**
* @private
*/
Collection.prototype.updateLength_ = function() {
this.set(Property.LENGTH, this.array_.length);
};
/**
* @private
* @param {T} elem Element.
* @param {number=} opt_except Optional index to ignore.
*/
Collection.prototype.assertUnique_ = function(elem, opt_except) {
for (let i = 0, ii = this.array_.length; i < ii; ++i) {
if (this.array_[i] === elem && i !== opt_except) {
throw new AssertionError(58);
}
}
};
export default Collection;

View File

@@ -7,7 +7,17 @@ import {UNDEFINED} from './functions.js';
* Objects that need to clean up after themselves.
* @constructor
*/
const Disposable = function() {};
class Disposable {
/**
* Clean up.
*/
dispose() {
if (!this.disposed_) {
this.disposed_ = true;
this.disposeInternal();
}
}
}
/**
* The object has already been disposed.
@@ -16,16 +26,6 @@ const Disposable = function() {};
*/
Disposable.prototype.disposed_ = false;
/**
* Clean up.
*/
Disposable.prototype.dispose = function() {
if (!this.disposed_) {
this.disposed_ = true;
this.disposeInternal();
}
};
/**
* Extension point for disposable objects.
* @protected

View File

@@ -59,229 +59,219 @@ import Style from './style/Style.js';
* associated with a `geometry` key.
* @api
*/
const Feature = function(opt_geometryOrProperties) {
class Feature {
constructor(opt_geometryOrProperties) {
BaseObject.call(this);
BaseObject.call(this);
/**
* @private
* @type {number|string|undefined}
*/
this.id_ = undefined;
/**
* @private
* @type {number|string|undefined}
*/
this.id_ = undefined;
/**
* @type {string}
* @private
*/
this.geometryName_ = 'geometry';
/**
* @type {string}
* @private
*/
this.geometryName_ = 'geometry';
/**
* User provided style.
* @private
* @type {module:ol/style/Style|Array.<module:ol/style/Style>|module:ol/style/Style~StyleFunction}
*/
this.style_ = null;
/**
* User provided style.
* @private
* @type {module:ol/style/Style|Array.<module:ol/style/Style>|module:ol/style/Style~StyleFunction}
*/
this.style_ = null;
/**
* @private
* @type {module:ol/style/Style~StyleFunction|undefined}
*/
this.styleFunction_ = undefined;
/**
* @private
* @type {module:ol/style/Style~StyleFunction|undefined}
*/
this.styleFunction_ = undefined;
/**
* @private
* @type {?module:ol/events~EventsKey}
*/
this.geometryChangeKey_ = null;
/**
* @private
* @type {?module:ol/events~EventsKey}
*/
this.geometryChangeKey_ = null;
listen(
this, getChangeEventType(this.geometryName_),
this.handleGeometryChanged_, this);
listen(
this, getChangeEventType(this.geometryName_),
this.handleGeometryChanged_, this);
if (opt_geometryOrProperties !== undefined) {
if (opt_geometryOrProperties instanceof Geometry ||
!opt_geometryOrProperties) {
const geometry = opt_geometryOrProperties;
this.setGeometry(geometry);
} else {
/** @type {Object.<string, *>} */
const properties = opt_geometryOrProperties;
this.setProperties(properties);
if (opt_geometryOrProperties !== undefined) {
if (opt_geometryOrProperties instanceof Geometry ||
!opt_geometryOrProperties) {
const geometry = opt_geometryOrProperties;
this.setGeometry(geometry);
} else {
/** @type {Object.<string, *>} */
const properties = opt_geometryOrProperties;
this.setProperties(properties);
}
}
}
};
/**
* Clone this feature. If the original feature has a geometry it
* is also cloned. The feature id is not set in the clone.
* @return {module:ol/Feature} The clone.
* @api
*/
clone() {
const clone = new Feature(this.getProperties());
clone.setGeometryName(this.getGeometryName());
const geometry = this.getGeometry();
if (geometry) {
clone.setGeometry(geometry.clone());
}
const style = this.getStyle();
if (style) {
clone.setStyle(style);
}
return clone;
}
/**
* Get the feature's default geometry. A feature may have any number of named
* geometries. The "default" geometry (the one that is rendered by default) is
* set when calling {@link module:ol/Feature~Feature#setGeometry}.
* @return {module:ol/geom/Geometry|undefined} The default geometry for the feature.
* @api
* @observable
*/
getGeometry() {
return (
/** @type {module:ol/geom/Geometry|undefined} */ (this.get(this.geometryName_))
);
}
/**
* Get the feature identifier. This is a stable identifier for the feature and
* is either set when reading data from a remote source or set explicitly by
* calling {@link module:ol/Feature~Feature#setId}.
* @return {number|string|undefined} Id.
* @api
*/
getId() {
return this.id_;
}
/**
* Get the name of the feature's default geometry. By default, the default
* geometry is named `geometry`.
* @return {string} Get the property name associated with the default geometry
* for this feature.
* @api
*/
getGeometryName() {
return this.geometryName_;
}
/**
* Get the feature's style. Will return what was provided to the
* {@link module:ol/Feature~Feature#setStyle} method.
* @return {module:ol/style/Style|Array.<module:ol/style/Style>|module:ol/style/Style~StyleFunction} The feature style.
* @api
*/
getStyle() {
return this.style_;
}
/**
* Get the feature's style function.
* @return {module:ol/style/Style~StyleFunction|undefined} Return a function
* representing the current style of this feature.
* @api
*/
getStyleFunction() {
return this.styleFunction_;
}
/**
* @private
*/
handleGeometryChange_() {
this.changed();
}
/**
* @private
*/
handleGeometryChanged_() {
if (this.geometryChangeKey_) {
unlistenByKey(this.geometryChangeKey_);
this.geometryChangeKey_ = null;
}
const geometry = this.getGeometry();
if (geometry) {
this.geometryChangeKey_ = listen(geometry,
EventType.CHANGE, this.handleGeometryChange_, this);
}
this.changed();
}
/**
* Set the default geometry for the feature. This will update the property
* with the name returned by {@link module:ol/Feature~Feature#getGeometryName}.
* @param {module:ol/geom/Geometry|undefined} geometry The new geometry.
* @api
* @observable
*/
setGeometry(geometry) {
this.set(this.geometryName_, geometry);
}
/**
* Set the style for the feature. This can be a single style object, an array
* of styles, or a function that takes a resolution and returns an array of
* styles. If it is `null` the feature has no style (a `null` style).
* @param {module:ol/style/Style|Array.<module:ol/style/Style>|module:ol/style/Style~StyleFunction} style Style for this feature.
* @api
* @fires module:ol/events/Event~Event#event:change
*/
setStyle(style) {
this.style_ = style;
this.styleFunction_ = !style ? undefined : createStyleFunction(style);
this.changed();
}
/**
* Set the feature id. The feature id is considered stable and may be used when
* requesting features or comparing identifiers returned from a remote source.
* The feature id can be used with the
* {@link module:ol/source/Vector~VectorSource#getFeatureById} method.
* @param {number|string|undefined} id The feature id.
* @api
* @fires module:ol/events/Event~Event#event:change
*/
setId(id) {
this.id_ = id;
this.changed();
}
/**
* Set the property name to be used when getting the feature's default geometry.
* When calling {@link module:ol/Feature~Feature#getGeometry}, the value of the property with
* this name will be returned.
* @param {string} name The property name of the default geometry.
* @api
*/
setGeometryName(name) {
unlisten(
this, getChangeEventType(this.geometryName_),
this.handleGeometryChanged_, this);
this.geometryName_ = name;
listen(
this, getChangeEventType(this.geometryName_),
this.handleGeometryChanged_, this);
this.handleGeometryChanged_();
}
}
inherits(Feature, BaseObject);
/**
* Clone this feature. If the original feature has a geometry it
* is also cloned. The feature id is not set in the clone.
* @return {module:ol/Feature} The clone.
* @api
*/
Feature.prototype.clone = function() {
const clone = new Feature(this.getProperties());
clone.setGeometryName(this.getGeometryName());
const geometry = this.getGeometry();
if (geometry) {
clone.setGeometry(geometry.clone());
}
const style = this.getStyle();
if (style) {
clone.setStyle(style);
}
return clone;
};
/**
* Get the feature's default geometry. A feature may have any number of named
* geometries. The "default" geometry (the one that is rendered by default) is
* set when calling {@link module:ol/Feature~Feature#setGeometry}.
* @return {module:ol/geom/Geometry|undefined} The default geometry for the feature.
* @api
* @observable
*/
Feature.prototype.getGeometry = function() {
return (
/** @type {module:ol/geom/Geometry|undefined} */ (this.get(this.geometryName_))
);
};
/**
* Get the feature identifier. This is a stable identifier for the feature and
* is either set when reading data from a remote source or set explicitly by
* calling {@link module:ol/Feature~Feature#setId}.
* @return {number|string|undefined} Id.
* @api
*/
Feature.prototype.getId = function() {
return this.id_;
};
/**
* Get the name of the feature's default geometry. By default, the default
* geometry is named `geometry`.
* @return {string} Get the property name associated with the default geometry
* for this feature.
* @api
*/
Feature.prototype.getGeometryName = function() {
return this.geometryName_;
};
/**
* Get the feature's style. Will return what was provided to the
* {@link module:ol/Feature~Feature#setStyle} method.
* @return {module:ol/style/Style|Array.<module:ol/style/Style>|module:ol/style/Style~StyleFunction} The feature style.
* @api
*/
Feature.prototype.getStyle = function() {
return this.style_;
};
/**
* Get the feature's style function.
* @return {module:ol/style/Style~StyleFunction|undefined} Return a function
* representing the current style of this feature.
* @api
*/
Feature.prototype.getStyleFunction = function() {
return this.styleFunction_;
};
/**
* @private
*/
Feature.prototype.handleGeometryChange_ = function() {
this.changed();
};
/**
* @private
*/
Feature.prototype.handleGeometryChanged_ = function() {
if (this.geometryChangeKey_) {
unlistenByKey(this.geometryChangeKey_);
this.geometryChangeKey_ = null;
}
const geometry = this.getGeometry();
if (geometry) {
this.geometryChangeKey_ = listen(geometry,
EventType.CHANGE, this.handleGeometryChange_, this);
}
this.changed();
};
/**
* Set the default geometry for the feature. This will update the property
* with the name returned by {@link module:ol/Feature~Feature#getGeometryName}.
* @param {module:ol/geom/Geometry|undefined} geometry The new geometry.
* @api
* @observable
*/
Feature.prototype.setGeometry = function(geometry) {
this.set(this.geometryName_, geometry);
};
/**
* Set the style for the feature. This can be a single style object, an array
* of styles, or a function that takes a resolution and returns an array of
* styles. If it is `null` the feature has no style (a `null` style).
* @param {module:ol/style/Style|Array.<module:ol/style/Style>|module:ol/style/Style~StyleFunction} style Style for this feature.
* @api
* @fires module:ol/events/Event~Event#event:change
*/
Feature.prototype.setStyle = function(style) {
this.style_ = style;
this.styleFunction_ = !style ? undefined : createStyleFunction(style);
this.changed();
};
/**
* Set the feature id. The feature id is considered stable and may be used when
* requesting features or comparing identifiers returned from a remote source.
* The feature id can be used with the
* {@link module:ol/source/Vector~VectorSource#getFeatureById} method.
* @param {number|string|undefined} id The feature id.
* @api
* @fires module:ol/events/Event~Event#event:change
*/
Feature.prototype.setId = function(id) {
this.id_ = id;
this.changed();
};
/**
* Set the property name to be used when getting the feature's default geometry.
* When calling {@link module:ol/Feature~Feature#getGeometry}, the value of the property with
* this name will be returned.
* @param {string} name The property name of the default geometry.
* @api
*/
Feature.prototype.setGeometryName = function(name) {
unlisten(
this, getChangeEventType(this.geometryName_),
this.handleGeometryChanged_, this);
this.geometryName_ = name;
listen(
this, getChangeEventType(this.geometryName_),
this.handleGeometryChanged_, this);
this.handleGeometryChanged_();
};
/**
* Convert the provided object into a feature style function. Functions passed
* through unchanged. Arrays of module:ol/style/Style or single style objects wrapped

View File

@@ -49,302 +49,289 @@ import {get as getProjection, getTransformFromProjections, identityTransform} fr
* @param {module:ol/Geolocation~Options=} opt_options Options.
* @api
*/
const Geolocation = function(opt_options) {
class Geolocation {
constructor(opt_options) {
BaseObject.call(this);
BaseObject.call(this);
const options = opt_options || {};
const options = opt_options || {};
/**
* The unprojected (EPSG:4326) device position.
* @private
* @type {module:ol/coordinate~Coordinate}
*/
this.position_ = null;
/**
* The unprojected (EPSG:4326) device position.
* @private
* @type {module:ol/coordinate~Coordinate}
*/
this.position_ = null;
/**
* @private
* @type {module:ol/proj~TransformFunction}
*/
this.transform_ = identityTransform;
/**
* @private
* @type {module:ol/proj~TransformFunction}
*/
this.transform_ = identityTransform;
/**
* @private
* @type {number|undefined}
*/
this.watchId_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.watchId_ = undefined;
listen(
this, getChangeEventType(GeolocationProperty.PROJECTION),
this.handleProjectionChanged_, this);
listen(
this, getChangeEventType(GeolocationProperty.TRACKING),
this.handleTrackingChanged_, this);
listen(
this, getChangeEventType(GeolocationProperty.PROJECTION),
this.handleProjectionChanged_, this);
listen(
this, getChangeEventType(GeolocationProperty.TRACKING),
this.handleTrackingChanged_, this);
if (options.projection !== undefined) {
this.setProjection(options.projection);
}
if (options.trackingOptions !== undefined) {
this.setTrackingOptions(options.trackingOptions);
}
this.setTracking(options.tracking !== undefined ? options.tracking : false);
if (options.projection !== undefined) {
this.setProjection(options.projection);
}
if (options.trackingOptions !== undefined) {
this.setTrackingOptions(options.trackingOptions);
}
this.setTracking(options.tracking !== undefined ? options.tracking : false);
/**
* @inheritDoc
*/
disposeInternal() {
this.setTracking(false);
BaseObject.prototype.disposeInternal.call(this);
}
};
/**
* @private
*/
handleProjectionChanged_() {
const projection = this.getProjection();
if (projection) {
this.transform_ = getTransformFromProjections(
getProjection('EPSG:4326'), projection);
if (this.position_) {
this.set(GeolocationProperty.POSITION, this.transform_(this.position_));
}
}
}
/**
* @private
*/
handleTrackingChanged_() {
if (GEOLOCATION) {
const tracking = this.getTracking();
if (tracking && this.watchId_ === undefined) {
this.watchId_ = navigator.geolocation.watchPosition(
this.positionChange_.bind(this),
this.positionError_.bind(this),
this.getTrackingOptions());
} else if (!tracking && this.watchId_ !== undefined) {
navigator.geolocation.clearWatch(this.watchId_);
this.watchId_ = undefined;
}
}
}
/**
* @private
* @param {GeolocationPosition} position position event.
*/
positionChange_(position) {
const coords = position.coords;
this.set(GeolocationProperty.ACCURACY, coords.accuracy);
this.set(GeolocationProperty.ALTITUDE,
coords.altitude === null ? undefined : coords.altitude);
this.set(GeolocationProperty.ALTITUDE_ACCURACY,
coords.altitudeAccuracy === null ?
undefined : coords.altitudeAccuracy);
this.set(GeolocationProperty.HEADING, coords.heading === null ?
undefined : toRadians(coords.heading));
if (!this.position_) {
this.position_ = [coords.longitude, coords.latitude];
} else {
this.position_[0] = coords.longitude;
this.position_[1] = coords.latitude;
}
const projectedPosition = this.transform_(this.position_);
this.set(GeolocationProperty.POSITION, projectedPosition);
this.set(GeolocationProperty.SPEED,
coords.speed === null ? undefined : coords.speed);
const geometry = circularPolygon(this.position_, coords.accuracy);
geometry.applyTransform(this.transform_);
this.set(GeolocationProperty.ACCURACY_GEOMETRY, geometry);
this.changed();
}
/**
* Triggered when the Geolocation returns an error.
* @event error
* @api
*/
/**
* @private
* @param {GeolocationPositionError} error error object.
*/
positionError_(error) {
error.type = EventType.ERROR;
this.setTracking(false);
this.dispatchEvent(/** @type {{type: string, target: undefined}} */ (error));
}
/**
* Get the accuracy of the position in meters.
* @return {number|undefined} The accuracy of the position measurement in
* meters.
* @observable
* @api
*/
getAccuracy() {
return /** @type {number|undefined} */ (this.get(GeolocationProperty.ACCURACY));
}
/**
* Get a geometry of the position accuracy.
* @return {?module:ol/geom/Polygon} A geometry of the position accuracy.
* @observable
* @api
*/
getAccuracyGeometry() {
return (
/** @type {?module:ol/geom/Polygon} */ (this.get(GeolocationProperty.ACCURACY_GEOMETRY) || null)
);
}
/**
* Get the altitude associated with the position.
* @return {number|undefined} The altitude of the position in meters above mean
* sea level.
* @observable
* @api
*/
getAltitude() {
return /** @type {number|undefined} */ (this.get(GeolocationProperty.ALTITUDE));
}
/**
* Get the altitude accuracy of the position.
* @return {number|undefined} The accuracy of the altitude measurement in
* meters.
* @observable
* @api
*/
getAltitudeAccuracy() {
return /** @type {number|undefined} */ (this.get(GeolocationProperty.ALTITUDE_ACCURACY));
}
/**
* Get the heading as radians clockwise from North.
* Note: depending on the browser, the heading is only defined if the `enableHighAccuracy`
* is set to `true` in the tracking options.
* @return {number|undefined} The heading of the device in radians from north.
* @observable
* @api
*/
getHeading() {
return /** @type {number|undefined} */ (this.get(GeolocationProperty.HEADING));
}
/**
* Get the position of the device.
* @return {module:ol/coordinate~Coordinate|undefined} The current position of the device reported
* in the current projection.
* @observable
* @api
*/
getPosition() {
return (
/** @type {module:ol/coordinate~Coordinate|undefined} */ (this.get(GeolocationProperty.POSITION))
);
}
/**
* Get the projection associated with the position.
* @return {module:ol/proj/Projection|undefined} The projection the position is
* reported in.
* @observable
* @api
*/
getProjection() {
return (
/** @type {module:ol/proj/Projection|undefined} */ (this.get(GeolocationProperty.PROJECTION))
);
}
/**
* Get the speed in meters per second.
* @return {number|undefined} The instantaneous speed of the device in meters
* per second.
* @observable
* @api
*/
getSpeed() {
return /** @type {number|undefined} */ (this.get(GeolocationProperty.SPEED));
}
/**
* Determine if the device location is being tracked.
* @return {boolean} The device location is being tracked.
* @observable
* @api
*/
getTracking() {
return /** @type {boolean} */ (this.get(GeolocationProperty.TRACKING));
}
/**
* Get the tracking options.
* @see http://www.w3.org/TR/geolocation-API/#position-options
* @return {GeolocationPositionOptions|undefined} PositionOptions as defined by
* the [HTML5 Geolocation spec
* ](http://www.w3.org/TR/geolocation-API/#position_options_interface).
* @observable
* @api
*/
getTrackingOptions() {
return /** @type {GeolocationPositionOptions|undefined} */ (this.get(GeolocationProperty.TRACKING_OPTIONS));
}
/**
* Set the projection to use for transforming the coordinates.
* @param {module:ol/proj~ProjectionLike} projection The projection the position is
* reported in.
* @observable
* @api
*/
setProjection(projection) {
this.set(GeolocationProperty.PROJECTION, getProjection(projection));
}
/**
* Enable or disable tracking.
* @param {boolean} tracking Enable tracking.
* @observable
* @api
*/
setTracking(tracking) {
this.set(GeolocationProperty.TRACKING, tracking);
}
/**
* Set the tracking options.
* @see http://www.w3.org/TR/geolocation-API/#position-options
* @param {GeolocationPositionOptions} options PositionOptions as defined by the
* [HTML5 Geolocation spec
* ](http://www.w3.org/TR/geolocation-API/#position_options_interface).
* @observable
* @api
*/
setTrackingOptions(options) {
this.set(GeolocationProperty.TRACKING_OPTIONS, options);
}
}
inherits(Geolocation, BaseObject);
/**
* @inheritDoc
*/
Geolocation.prototype.disposeInternal = function() {
this.setTracking(false);
BaseObject.prototype.disposeInternal.call(this);
};
/**
* @private
*/
Geolocation.prototype.handleProjectionChanged_ = function() {
const projection = this.getProjection();
if (projection) {
this.transform_ = getTransformFromProjections(
getProjection('EPSG:4326'), projection);
if (this.position_) {
this.set(GeolocationProperty.POSITION, this.transform_(this.position_));
}
}
};
/**
* @private
*/
Geolocation.prototype.handleTrackingChanged_ = function() {
if (GEOLOCATION) {
const tracking = this.getTracking();
if (tracking && this.watchId_ === undefined) {
this.watchId_ = navigator.geolocation.watchPosition(
this.positionChange_.bind(this),
this.positionError_.bind(this),
this.getTrackingOptions());
} else if (!tracking && this.watchId_ !== undefined) {
navigator.geolocation.clearWatch(this.watchId_);
this.watchId_ = undefined;
}
}
};
/**
* @private
* @param {GeolocationPosition} position position event.
*/
Geolocation.prototype.positionChange_ = function(position) {
const coords = position.coords;
this.set(GeolocationProperty.ACCURACY, coords.accuracy);
this.set(GeolocationProperty.ALTITUDE,
coords.altitude === null ? undefined : coords.altitude);
this.set(GeolocationProperty.ALTITUDE_ACCURACY,
coords.altitudeAccuracy === null ?
undefined : coords.altitudeAccuracy);
this.set(GeolocationProperty.HEADING, coords.heading === null ?
undefined : toRadians(coords.heading));
if (!this.position_) {
this.position_ = [coords.longitude, coords.latitude];
} else {
this.position_[0] = coords.longitude;
this.position_[1] = coords.latitude;
}
const projectedPosition = this.transform_(this.position_);
this.set(GeolocationProperty.POSITION, projectedPosition);
this.set(GeolocationProperty.SPEED,
coords.speed === null ? undefined : coords.speed);
const geometry = circularPolygon(this.position_, coords.accuracy);
geometry.applyTransform(this.transform_);
this.set(GeolocationProperty.ACCURACY_GEOMETRY, geometry);
this.changed();
};
/**
* Triggered when the Geolocation returns an error.
* @event error
* @api
*/
/**
* @private
* @param {GeolocationPositionError} error error object.
*/
Geolocation.prototype.positionError_ = function(error) {
error.type = EventType.ERROR;
this.setTracking(false);
this.dispatchEvent(/** @type {{type: string, target: undefined}} */ (error));
};
/**
* Get the accuracy of the position in meters.
* @return {number|undefined} The accuracy of the position measurement in
* meters.
* @observable
* @api
*/
Geolocation.prototype.getAccuracy = function() {
return /** @type {number|undefined} */ (this.get(GeolocationProperty.ACCURACY));
};
/**
* Get a geometry of the position accuracy.
* @return {?module:ol/geom/Polygon} A geometry of the position accuracy.
* @observable
* @api
*/
Geolocation.prototype.getAccuracyGeometry = function() {
return (
/** @type {?module:ol/geom/Polygon} */ (this.get(GeolocationProperty.ACCURACY_GEOMETRY) || null)
);
};
/**
* Get the altitude associated with the position.
* @return {number|undefined} The altitude of the position in meters above mean
* sea level.
* @observable
* @api
*/
Geolocation.prototype.getAltitude = function() {
return /** @type {number|undefined} */ (this.get(GeolocationProperty.ALTITUDE));
};
/**
* Get the altitude accuracy of the position.
* @return {number|undefined} The accuracy of the altitude measurement in
* meters.
* @observable
* @api
*/
Geolocation.prototype.getAltitudeAccuracy = function() {
return /** @type {number|undefined} */ (this.get(GeolocationProperty.ALTITUDE_ACCURACY));
};
/**
* Get the heading as radians clockwise from North.
* Note: depending on the browser, the heading is only defined if the `enableHighAccuracy`
* is set to `true` in the tracking options.
* @return {number|undefined} The heading of the device in radians from north.
* @observable
* @api
*/
Geolocation.prototype.getHeading = function() {
return /** @type {number|undefined} */ (this.get(GeolocationProperty.HEADING));
};
/**
* Get the position of the device.
* @return {module:ol/coordinate~Coordinate|undefined} The current position of the device reported
* in the current projection.
* @observable
* @api
*/
Geolocation.prototype.getPosition = function() {
return (
/** @type {module:ol/coordinate~Coordinate|undefined} */ (this.get(GeolocationProperty.POSITION))
);
};
/**
* Get the projection associated with the position.
* @return {module:ol/proj/Projection|undefined} The projection the position is
* reported in.
* @observable
* @api
*/
Geolocation.prototype.getProjection = function() {
return (
/** @type {module:ol/proj/Projection|undefined} */ (this.get(GeolocationProperty.PROJECTION))
);
};
/**
* Get the speed in meters per second.
* @return {number|undefined} The instantaneous speed of the device in meters
* per second.
* @observable
* @api
*/
Geolocation.prototype.getSpeed = function() {
return /** @type {number|undefined} */ (this.get(GeolocationProperty.SPEED));
};
/**
* Determine if the device location is being tracked.
* @return {boolean} The device location is being tracked.
* @observable
* @api
*/
Geolocation.prototype.getTracking = function() {
return /** @type {boolean} */ (this.get(GeolocationProperty.TRACKING));
};
/**
* Get the tracking options.
* @see http://www.w3.org/TR/geolocation-API/#position-options
* @return {GeolocationPositionOptions|undefined} PositionOptions as defined by
* the [HTML5 Geolocation spec
* ](http://www.w3.org/TR/geolocation-API/#position_options_interface).
* @observable
* @api
*/
Geolocation.prototype.getTrackingOptions = function() {
return /** @type {GeolocationPositionOptions|undefined} */ (this.get(GeolocationProperty.TRACKING_OPTIONS));
};
/**
* Set the projection to use for transforming the coordinates.
* @param {module:ol/proj~ProjectionLike} projection The projection the position is
* reported in.
* @observable
* @api
*/
Geolocation.prototype.setProjection = function(projection) {
this.set(GeolocationProperty.PROJECTION, getProjection(projection));
};
/**
* Enable or disable tracking.
* @param {boolean} tracking Enable tracking.
* @observable
* @api
*/
Geolocation.prototype.setTracking = function(tracking) {
this.set(GeolocationProperty.TRACKING, tracking);
};
/**
* Set the tracking options.
* @see http://www.w3.org/TR/geolocation-API/#position-options
* @param {GeolocationPositionOptions} options PositionOptions as defined by the
* [HTML5 Geolocation spec
* ](http://www.w3.org/TR/geolocation-API/#position_options_interface).
* @observable
* @api
*/
Geolocation.prototype.setTrackingOptions = function(options) {
this.set(GeolocationProperty.TRACKING_OPTIONS, options);
};
export default Geolocation;

File diff suppressed because it is too large Load Diff

View File

@@ -38,122 +38,119 @@ import {getHeight} from './extent.js';
* @param {?string} crossOrigin Cross origin.
* @param {module:ol/Image~LoadFunction} imageLoadFunction Image load function.
*/
const ImageWrapper = function(extent, resolution, pixelRatio, src, crossOrigin, imageLoadFunction) {
class ImageWrapper {
constructor(extent, resolution, pixelRatio, src, crossOrigin, imageLoadFunction) {
ImageBase.call(this, extent, resolution, pixelRatio, ImageState.IDLE);
ImageBase.call(this, extent, resolution, pixelRatio, ImageState.IDLE);
/**
* @private
* @type {string}
*/
this.src_ = src;
/**
* @private
* @type {string}
*/
this.src_ = src;
/**
* @private
* @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement}
*/
this.image_ = new Image();
if (crossOrigin !== null) {
this.image_.crossOrigin = crossOrigin;
}
/**
* @private
* @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement}
*/
this.image_ = new Image();
if (crossOrigin !== null) {
this.image_.crossOrigin = crossOrigin;
}
/**
* @private
* @type {Array.<module:ol/events~EventsKey>}
*/
this.imageListenerKeys_ = null;
/**
* @private
* @type {Array.<module:ol/events~EventsKey>}
*/
this.imageListenerKeys_ = null;
/**
* @protected
* @type {module:ol/ImageState}
*/
this.state = ImageState.IDLE;
/**
* @protected
* @type {module:ol/ImageState}
*/
this.state = ImageState.IDLE;
/**
* @private
* @type {module:ol/Image~LoadFunction}
*/
this.imageLoadFunction_ = imageLoadFunction;
/**
* @private
* @type {module:ol/Image~LoadFunction}
*/
this.imageLoadFunction_ = imageLoadFunction;
};
}
/**
* @inheritDoc
* @api
*/
getImage() {
return this.image_;
}
/**
* Tracks loading or read errors.
*
* @private
*/
handleImageError_() {
this.state = ImageState.ERROR;
this.unlistenImage_();
this.changed();
}
/**
* Tracks successful image load.
*
* @private
*/
handleImageLoad_() {
if (this.resolution === undefined) {
this.resolution = getHeight(this.extent) / this.image_.height;
}
this.state = ImageState.LOADED;
this.unlistenImage_();
this.changed();
}
/**
* Load the image or retry if loading previously failed.
* Loading is taken care of by the tile queue, and calling this method is
* only needed for preloading or for reloading in case of an error.
* @override
* @api
*/
load() {
if (this.state == ImageState.IDLE || this.state == ImageState.ERROR) {
this.state = ImageState.LOADING;
this.changed();
this.imageListenerKeys_ = [
listenOnce(this.image_, EventType.ERROR,
this.handleImageError_, this),
listenOnce(this.image_, EventType.LOAD,
this.handleImageLoad_, this)
];
this.imageLoadFunction_(this, this.src_);
}
}
/**
* @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} image Image.
*/
setImage(image) {
this.image_ = image;
}
/**
* Discards event handlers which listen for load completion or errors.
*
* @private
*/
unlistenImage_() {
this.imageListenerKeys_.forEach(unlistenByKey);
this.imageListenerKeys_ = null;
}
}
inherits(ImageWrapper, ImageBase);
/**
* @inheritDoc
* @api
*/
ImageWrapper.prototype.getImage = function() {
return this.image_;
};
/**
* Tracks loading or read errors.
*
* @private
*/
ImageWrapper.prototype.handleImageError_ = function() {
this.state = ImageState.ERROR;
this.unlistenImage_();
this.changed();
};
/**
* Tracks successful image load.
*
* @private
*/
ImageWrapper.prototype.handleImageLoad_ = function() {
if (this.resolution === undefined) {
this.resolution = getHeight(this.extent) / this.image_.height;
}
this.state = ImageState.LOADED;
this.unlistenImage_();
this.changed();
};
/**
* Load the image or retry if loading previously failed.
* Loading is taken care of by the tile queue, and calling this method is
* only needed for preloading or for reloading in case of an error.
* @override
* @api
*/
ImageWrapper.prototype.load = function() {
if (this.state == ImageState.IDLE || this.state == ImageState.ERROR) {
this.state = ImageState.LOADING;
this.changed();
this.imageListenerKeys_ = [
listenOnce(this.image_, EventType.ERROR,
this.handleImageError_, this),
listenOnce(this.image_, EventType.LOAD,
this.handleImageLoad_, this)
];
this.imageLoadFunction_(this, this.src_);
}
};
/**
* @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} image Image.
*/
ImageWrapper.prototype.setImage = function(image) {
this.image_ = image;
};
/**
* Discards event handlers which listen for load completion or errors.
*
* @private
*/
ImageWrapper.prototype.unlistenImage_ = function() {
this.imageListenerKeys_.forEach(unlistenByKey);
this.imageListenerKeys_ = null;
};
export default ImageWrapper;

View File

@@ -14,90 +14,86 @@ import EventType from './events/EventType.js';
* @param {number} pixelRatio Pixel ratio.
* @param {module:ol/ImageState} state State.
*/
const ImageBase = function(extent, resolution, pixelRatio, state) {
class ImageBase {
constructor(extent, resolution, pixelRatio, state) {
EventTarget.call(this);
EventTarget.call(this);
/**
* @protected
* @type {module:ol/extent~Extent}
*/
this.extent = extent;
/**
* @protected
* @type {module:ol/extent~Extent}
*/
this.extent = extent;
/**
* @private
* @type {number}
*/
this.pixelRatio_ = pixelRatio;
/**
* @private
* @type {number}
*/
this.pixelRatio_ = pixelRatio;
/**
* @protected
* @type {number|undefined}
*/
this.resolution = resolution;
/**
* @protected
* @type {number|undefined}
*/
this.resolution = resolution;
/**
* @protected
* @type {module:ol/ImageState}
*/
this.state = state;
/**
* @protected
* @type {module:ol/ImageState}
*/
this.state = state;
};
}
/**
* @protected
*/
changed() {
this.dispatchEvent(EventType.CHANGE);
}
/**
* @return {module:ol/extent~Extent} Extent.
*/
getExtent() {
return this.extent;
}
/**
* @abstract
* @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image.
*/
getImage() {}
/**
* @return {number} PixelRatio.
*/
getPixelRatio() {
return this.pixelRatio_;
}
/**
* @return {number} Resolution.
*/
getResolution() {
return /** @type {number} */ (this.resolution);
}
/**
* @return {module:ol/ImageState} State.
*/
getState() {
return this.state;
}
/**
* Load not yet loaded URI.
* @abstract
*/
load() {}
}
inherits(ImageBase, EventTarget);
/**
* @protected
*/
ImageBase.prototype.changed = function() {
this.dispatchEvent(EventType.CHANGE);
};
/**
* @return {module:ol/extent~Extent} Extent.
*/
ImageBase.prototype.getExtent = function() {
return this.extent;
};
/**
* @abstract
* @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image.
*/
ImageBase.prototype.getImage = function() {};
/**
* @return {number} PixelRatio.
*/
ImageBase.prototype.getPixelRatio = function() {
return this.pixelRatio_;
};
/**
* @return {number} Resolution.
*/
ImageBase.prototype.getResolution = function() {
return /** @type {number} */ (this.resolution);
};
/**
* @return {module:ol/ImageState} State.
*/
ImageBase.prototype.getState = function() {
return this.state;
};
/**
* Load not yet loaded URI.
* @abstract
*/
ImageBase.prototype.load = function() {};
export default ImageBase;

View File

@@ -26,77 +26,77 @@ import ImageState from './ImageState.js';
* @param {module:ol/ImageCanvas~Loader=} opt_loader Optional loader function to
* support asynchronous canvas drawing.
*/
const ImageCanvas = function(extent, resolution, pixelRatio, canvas, opt_loader) {
class ImageCanvas {
constructor(extent, resolution, pixelRatio, canvas, opt_loader) {
/**
* Optional canvas loader function.
* @type {?module:ol/ImageCanvas~Loader}
* @private
*/
this.loader_ = opt_loader !== undefined ? opt_loader : null;
/**
* Optional canvas loader function.
* @type {?module:ol/ImageCanvas~Loader}
* @private
*/
this.loader_ = opt_loader !== undefined ? opt_loader : null;
const state = opt_loader !== undefined ? ImageState.IDLE : ImageState.LOADED;
const state = opt_loader !== undefined ? ImageState.IDLE : ImageState.LOADED;
ImageBase.call(this, extent, resolution, pixelRatio, state);
ImageBase.call(this, extent, resolution, pixelRatio, state);
/**
* @private
* @type {HTMLCanvasElement}
*/
this.canvas_ = canvas;
/**
* @private
* @type {HTMLCanvasElement}
*/
this.canvas_ = canvas;
/**
* @private
* @type {Error}
*/
this.error_ = null;
/**
* @private
* @type {Error}
*/
this.error_ = null;
};
}
/**
* Get any error associated with asynchronous rendering.
* @return {Error} Any error that occurred during rendering.
*/
getError() {
return this.error_;
}
/**
* Handle async drawing complete.
* @param {Error} err Any error during drawing.
* @private
*/
handleLoad_(err) {
if (err) {
this.error_ = err;
this.state = ImageState.ERROR;
} else {
this.state = ImageState.LOADED;
}
this.changed();
}
/**
* @inheritDoc
*/
load() {
if (this.state == ImageState.IDLE) {
this.state = ImageState.LOADING;
this.changed();
this.loader_(this.handleLoad_.bind(this));
}
}
/**
* @return {HTMLCanvasElement} Canvas element.
*/
getImage() {
return this.canvas_;
}
}
inherits(ImageCanvas, ImageBase);
/**
* Get any error associated with asynchronous rendering.
* @return {Error} Any error that occurred during rendering.
*/
ImageCanvas.prototype.getError = function() {
return this.error_;
};
/**
* Handle async drawing complete.
* @param {Error} err Any error during drawing.
* @private
*/
ImageCanvas.prototype.handleLoad_ = function(err) {
if (err) {
this.error_ = err;
this.state = ImageState.ERROR;
} else {
this.state = ImageState.LOADED;
}
this.changed();
};
/**
* @inheritDoc
*/
ImageCanvas.prototype.load = function() {
if (this.state == ImageState.IDLE) {
this.state = ImageState.LOADING;
this.changed();
this.loader_(this.handleLoad_.bind(this));
}
};
/**
* @return {HTMLCanvasElement} Canvas element.
*/
ImageCanvas.prototype.getImage = function() {
return this.canvas_;
};
export default ImageCanvas;

View File

@@ -24,149 +24,144 @@ import EventType from './events/EventType.js';
* @param {module:ol/Tile~LoadFunction} tileLoadFunction Tile load function.
* @param {module:ol/Tile~Options=} opt_options Tile options.
*/
const ImageTile = function(tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) {
class ImageTile {
constructor(tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) {
Tile.call(this, tileCoord, state, opt_options);
Tile.call(this, tileCoord, state, opt_options);
/**
* @private
* @type {?string}
*/
this.crossOrigin_ = crossOrigin;
/**
* Image URI
*
* @private
* @type {string}
*/
this.src_ = src;
/**
* @private
* @type {HTMLImageElement|HTMLCanvasElement}
*/
this.image_ = new Image();
if (crossOrigin !== null) {
this.image_.crossOrigin = crossOrigin;
}
/**
* @private
* @type {Array.<module:ol/events~EventsKey>}
*/
this.imageListenerKeys_ = null;
/**
* @private
* @type {module:ol/Tile~LoadFunction}
*/
this.tileLoadFunction_ = tileLoadFunction;
}
/**
* @private
* @type {?string}
* @inheritDoc
*/
this.crossOrigin_ = crossOrigin;
disposeInternal() {
if (this.state == TileState.LOADING) {
this.unlistenImage_();
this.image_ = getBlankImage();
}
if (this.interimTile) {
this.interimTile.dispose();
}
this.state = TileState.ABORT;
this.changed();
Tile.prototype.disposeInternal.call(this);
}
/**
* Image URI
* Get the HTML image element for this tile (may be a Canvas, Image, or Video).
* @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image.
* @api
*/
getImage() {
return this.image_;
}
/**
* @inheritDoc
*/
getKey() {
return this.src_;
}
/**
* Tracks loading or read errors.
*
* @private
* @type {string}
*/
this.src_ = src;
/**
* @private
* @type {HTMLImageElement|HTMLCanvasElement}
*/
this.image_ = new Image();
if (crossOrigin !== null) {
this.image_.crossOrigin = crossOrigin;
}
/**
* @private
* @type {Array.<module:ol/events~EventsKey>}
*/
this.imageListenerKeys_ = null;
/**
* @private
* @type {module:ol/Tile~LoadFunction}
*/
this.tileLoadFunction_ = tileLoadFunction;
};
inherits(ImageTile, Tile);
/**
* @inheritDoc
*/
ImageTile.prototype.disposeInternal = function() {
if (this.state == TileState.LOADING) {
handleImageError_() {
this.state = TileState.ERROR;
this.unlistenImage_();
this.image_ = getBlankImage();
this.changed();
}
if (this.interimTile) {
this.interimTile.dispose();
/**
* Tracks successful image load.
*
* @private
*/
handleImageLoad_() {
if (this.image_.naturalWidth && this.image_.naturalHeight) {
this.state = TileState.LOADED;
} else {
this.state = TileState.EMPTY;
}
this.unlistenImage_();
this.changed();
}
this.state = TileState.ABORT;
this.changed();
Tile.prototype.disposeInternal.call(this);
};
/**
* Get the HTML image element for this tile (may be a Canvas, Image, or Video).
* @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image.
* @api
*/
ImageTile.prototype.getImage = function() {
return this.image_;
};
/**
* @inheritDoc
*/
ImageTile.prototype.getKey = function() {
return this.src_;
};
/**
* Tracks loading or read errors.
*
* @private
*/
ImageTile.prototype.handleImageError_ = function() {
this.state = TileState.ERROR;
this.unlistenImage_();
this.image_ = getBlankImage();
this.changed();
};
/**
* Tracks successful image load.
*
* @private
*/
ImageTile.prototype.handleImageLoad_ = function() {
if (this.image_.naturalWidth && this.image_.naturalHeight) {
this.state = TileState.LOADED;
} else {
this.state = TileState.EMPTY;
}
this.unlistenImage_();
this.changed();
};
/**
* @inheritDoc
* @api
*/
ImageTile.prototype.load = function() {
if (this.state == TileState.ERROR) {
this.state = TileState.IDLE;
this.image_ = new Image();
if (this.crossOrigin_ !== null) {
this.image_.crossOrigin = this.crossOrigin_;
/**
* @inheritDoc
* @api
*/
load() {
if (this.state == TileState.ERROR) {
this.state = TileState.IDLE;
this.image_ = new Image();
if (this.crossOrigin_ !== null) {
this.image_.crossOrigin = this.crossOrigin_;
}
}
if (this.state == TileState.IDLE) {
this.state = TileState.LOADING;
this.changed();
this.imageListenerKeys_ = [
listenOnce(this.image_, EventType.ERROR,
this.handleImageError_, this),
listenOnce(this.image_, EventType.LOAD,
this.handleImageLoad_, this)
];
this.tileLoadFunction_(this, this.src_);
}
}
if (this.state == TileState.IDLE) {
this.state = TileState.LOADING;
this.changed();
this.imageListenerKeys_ = [
listenOnce(this.image_, EventType.ERROR,
this.handleImageError_, this),
listenOnce(this.image_, EventType.LOAD,
this.handleImageLoad_, this)
];
this.tileLoadFunction_(this, this.src_);
/**
* Discards event handlers which listen for load completion or errors.
*
* @private
*/
unlistenImage_() {
this.imageListenerKeys_.forEach(unlistenByKey);
this.imageListenerKeys_ = null;
}
};
}
/**
* Discards event handlers which listen for load completion or errors.
*
* @private
*/
ImageTile.prototype.unlistenImage_ = function() {
this.imageListenerKeys_.forEach(unlistenByKey);
this.imageListenerKeys_ = null;
};
inherits(ImageTile, Tile);
/**

View File

@@ -14,116 +14,114 @@
* @struct
* @api
*/
const Kinetic = function(decay, minVelocity, delay) {
class Kinetic {
constructor(decay, minVelocity, delay) {
/**
* @private
* @type {number}
*/
this.decay_ = decay;
/**
* @private
* @type {number}
*/
this.decay_ = decay;
/**
* @private
* @type {number}
*/
this.minVelocity_ = minVelocity;
/**
* @private
* @type {number}
*/
this.minVelocity_ = minVelocity;
/**
* @private
* @type {number}
*/
this.delay_ = delay;
/**
* @private
* @type {number}
*/
this.delay_ = delay;
/**
* @private
* @type {Array.<number>}
*/
this.points_ = [];
/**
* @private
* @type {Array.<number>}
*/
this.points_ = [];
/**
* @private
* @type {number}
*/
this.angle_ = 0;
/**
* @private
* @type {number}
*/
this.angle_ = 0;
/**
* @private
* @type {number}
*/
this.initialVelocity_ = 0;
};
/**
* @private
* @type {number}
*/
this.initialVelocity_ = 0;
}
/**
* FIXME empty description for jsdoc
*/
begin() {
this.points_.length = 0;
this.angle_ = 0;
this.initialVelocity_ = 0;
}
/**
* FIXME empty description for jsdoc
*/
Kinetic.prototype.begin = function() {
this.points_.length = 0;
this.angle_ = 0;
this.initialVelocity_ = 0;
};
/**
* @param {number} x X.
* @param {number} y Y.
*/
update(x, y) {
this.points_.push(x, y, Date.now());
}
/**
* @return {boolean} Whether we should do kinetic animation.
*/
end() {
if (this.points_.length < 6) {
// at least 2 points are required (i.e. there must be at least 6 elements
// in the array)
return false;
}
const delay = Date.now() - this.delay_;
const lastIndex = this.points_.length - 3;
if (this.points_[lastIndex + 2] < delay) {
// the last tracked point is too old, which means that the user stopped
// panning before releasing the map
return false;
}
/**
* @param {number} x X.
* @param {number} y Y.
*/
Kinetic.prototype.update = function(x, y) {
this.points_.push(x, y, Date.now());
};
// get the first point which still falls into the delay time
let firstIndex = lastIndex - 3;
while (firstIndex > 0 && this.points_[firstIndex + 2] > delay) {
firstIndex -= 3;
}
const duration = this.points_[lastIndex + 2] - this.points_[firstIndex + 2];
// we don't want a duration of 0 (divide by zero)
// we also make sure the user panned for a duration of at least one frame
// (1/60s) to compute sane displacement values
if (duration < 1000 / 60) {
return false;
}
/**
* @return {boolean} Whether we should do kinetic animation.
*/
Kinetic.prototype.end = function() {
if (this.points_.length < 6) {
// at least 2 points are required (i.e. there must be at least 6 elements
// in the array)
return false;
}
const delay = Date.now() - this.delay_;
const lastIndex = this.points_.length - 3;
if (this.points_[lastIndex + 2] < delay) {
// the last tracked point is too old, which means that the user stopped
// panning before releasing the map
return false;
}
const dx = this.points_[lastIndex] - this.points_[firstIndex];
const dy = this.points_[lastIndex + 1] - this.points_[firstIndex + 1];
this.angle_ = Math.atan2(dy, dx);
this.initialVelocity_ = Math.sqrt(dx * dx + dy * dy) / duration;
return this.initialVelocity_ > this.minVelocity_;
}
// get the first point which still falls into the delay time
let firstIndex = lastIndex - 3;
while (firstIndex > 0 && this.points_[firstIndex + 2] > delay) {
firstIndex -= 3;
}
/**
* @return {number} Total distance travelled (pixels).
*/
getDistance() {
return (this.minVelocity_ - this.initialVelocity_) / this.decay_;
}
const duration = this.points_[lastIndex + 2] - this.points_[firstIndex + 2];
// we don't want a duration of 0 (divide by zero)
// we also make sure the user panned for a duration of at least one frame
// (1/60s) to compute sane displacement values
if (duration < 1000 / 60) {
return false;
}
/**
* @return {number} Angle of the kinetic panning animation (radians).
*/
getAngle() {
return this.angle_;
}
}
const dx = this.points_[lastIndex] - this.points_[firstIndex];
const dy = this.points_[lastIndex + 1] - this.points_[firstIndex + 1];
this.angle_ = Math.atan2(dy, dx);
this.initialVelocity_ = Math.sqrt(dx * dx + dy * dy) / duration;
return this.initialVelocity_ > this.minVelocity_;
};
/**
* @return {number} Total distance travelled (pixels).
*/
Kinetic.prototype.getDistance = function() {
return (this.minVelocity_ - this.initialVelocity_) / this.decay_;
};
/**
* @return {number} Angle of the kinetic panning animation (radians).
*/
Kinetic.prototype.getAngle = function() {
return this.angle_;
};
export default Kinetic;

View File

@@ -66,29 +66,31 @@ import CanvasVectorTileLayerRenderer from './renderer/canvas/VectorTileLayer.js'
* @fires module:ol/render/Event~RenderEvent#precompose
* @api
*/
const Map = function(options) {
options = assign({}, options);
if (!options.controls) {
options.controls = defaultControls();
}
if (!options.interactions) {
options.interactions = defaultInteractions();
class Map {
constructor(options) {
options = assign({}, options);
if (!options.controls) {
options.controls = defaultControls();
}
if (!options.interactions) {
options.interactions = defaultInteractions();
}
PluggableMap.call(this, options);
}
PluggableMap.call(this, options);
};
createRenderer() {
const renderer = new CanvasMapRenderer(this);
renderer.registerLayerRenderers([
CanvasImageLayerRenderer,
CanvasTileLayerRenderer,
CanvasVectorLayerRenderer,
CanvasVectorTileLayerRenderer
]);
return renderer;
}
}
inherits(Map, PluggableMap);
Map.prototype.createRenderer = function() {
const renderer = new CanvasMapRenderer(this);
renderer.registerLayerRenderers([
CanvasImageLayerRenderer,
CanvasTileLayerRenderer,
CanvasVectorLayerRenderer,
CanvasVectorTileLayerRenderer
]);
return renderer;
};
export default Map;

View File

@@ -17,66 +17,68 @@ import MapEvent from './MapEvent.js';
* @param {boolean=} opt_dragging Is the map currently being dragged?
* @param {?module:ol/PluggableMap~FrameState=} opt_frameState Frame state.
*/
const MapBrowserEvent = function(type, map, browserEvent, opt_dragging, opt_frameState) {
class MapBrowserEvent {
constructor(type, map, browserEvent, opt_dragging, opt_frameState) {
MapEvent.call(this, type, map, opt_frameState);
MapEvent.call(this, type, map, opt_frameState);
/**
* The original browser event.
* @const
* @type {Event}
* @api
*/
this.originalEvent = browserEvent;
/**
* The original browser event.
* @const
* @type {Event}
* @api
*/
this.originalEvent = browserEvent;
/**
* The map pixel relative to the viewport corresponding to the original browser event.
* @type {module:ol~Pixel}
* @api
*/
this.pixel = map.getEventPixel(browserEvent);
/**
* The map pixel relative to the viewport corresponding to the original browser event.
* @type {module:ol~Pixel}
* @api
*/
this.pixel = map.getEventPixel(browserEvent);
/**
* The coordinate in view projection corresponding to the original browser event.
* @type {module:ol/coordinate~Coordinate}
* @api
*/
this.coordinate = map.getCoordinateFromPixel(this.pixel);
/**
* The coordinate in view projection corresponding to the original browser event.
* @type {module:ol/coordinate~Coordinate}
* @api
*/
this.coordinate = map.getCoordinateFromPixel(this.pixel);
/**
* Indicates if the map is currently being dragged. Only set for
* `POINTERDRAG` and `POINTERMOVE` events. Default is `false`.
*
* @type {boolean}
* @api
*/
this.dragging = opt_dragging !== undefined ? opt_dragging : false;
/**
* Indicates if the map is currently being dragged. Only set for
* `POINTERDRAG` and `POINTERMOVE` events. Default is `false`.
*
* @type {boolean}
* @api
*/
this.dragging = opt_dragging !== undefined ? opt_dragging : false;
};
}
/**
* Prevents the default browser action.
* @see https://developer.mozilla.org/en-US/docs/Web/API/event.preventDefault
* @override
* @api
*/
preventDefault() {
MapEvent.prototype.preventDefault.call(this);
this.originalEvent.preventDefault();
}
/**
* Prevents further propagation of the current event.
* @see https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation
* @override
* @api
*/
stopPropagation() {
MapEvent.prototype.stopPropagation.call(this);
this.originalEvent.stopPropagation();
}
}
inherits(MapBrowserEvent, MapEvent);
/**
* Prevents the default browser action.
* @see https://developer.mozilla.org/en-US/docs/Web/API/event.preventDefault
* @override
* @api
*/
MapBrowserEvent.prototype.preventDefault = function() {
MapEvent.prototype.preventDefault.call(this);
this.originalEvent.preventDefault();
};
/**
* Prevents further propagation of the current event.
* @see https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation
* @override
* @api
*/
MapBrowserEvent.prototype.stopPropagation = function() {
MapEvent.prototype.stopPropagation.call(this);
this.originalEvent.stopPropagation();
};
export default MapBrowserEvent;

View File

@@ -18,319 +18,314 @@ import PointerEventHandler from './pointer/PointerEventHandler.js';
* @constructor
* @extends {module:ol/events/EventTarget}
*/
const MapBrowserEventHandler = function(map, moveTolerance) {
class MapBrowserEventHandler {
constructor(map, moveTolerance) {
EventTarget.call(this);
EventTarget.call(this);
/**
* This is the element that we will listen to the real events on.
* @type {module:ol/PluggableMap}
* @private
*/
this.map_ = map;
/**
* @type {number}
* @private
*/
this.clickTimeoutId_ = 0;
/**
* @type {boolean}
* @private
*/
this.dragging_ = false;
/**
* @type {!Array.<module:ol/events~EventsKey>}
* @private
*/
this.dragListenerKeys_ = [];
/**
* @type {number}
* @private
*/
this.moveTolerance_ = moveTolerance ?
moveTolerance * DEVICE_PIXEL_RATIO : DEVICE_PIXEL_RATIO;
/**
* The most recent "down" type event (or null if none have occurred).
* Set on pointerdown.
* @type {module:ol/pointer/PointerEvent}
* @private
*/
this.down_ = null;
const element = this.map_.getViewport();
/**
* @type {number}
* @private
*/
this.activePointers_ = 0;
/**
* @type {!Object.<number, boolean>}
* @private
*/
this.trackedTouches_ = {};
/**
* Event handler which generates pointer events for
* the viewport element.
*
* @type {module:ol/pointer/PointerEventHandler}
* @private
*/
this.pointerEventHandler_ = new PointerEventHandler(element);
/**
* Event handler which generates pointer events for
* the document (used when dragging).
*
* @type {module:ol/pointer/PointerEventHandler}
* @private
*/
this.documentPointerEventHandler_ = null;
/**
* @type {?module:ol/events~EventsKey}
* @private
*/
this.pointerdownListenerKey_ = listen(this.pointerEventHandler_,
PointerEventType.POINTERDOWN,
this.handlePointerDown_, this);
/**
* @type {?module:ol/events~EventsKey}
* @private
*/
this.relayedListenerKey_ = listen(this.pointerEventHandler_,
PointerEventType.POINTERMOVE,
this.relayEvent_, this);
}
/**
* This is the element that we will listen to the real events on.
* @type {module:ol/PluggableMap}
* @param {module:ol/pointer/PointerEvent} pointerEvent Pointer
* event.
* @private
*/
this.map_ = map;
emulateClick_(pointerEvent) {
let newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.CLICK, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
if (this.clickTimeoutId_ !== 0) {
// double-click
clearTimeout(this.clickTimeoutId_);
this.clickTimeoutId_ = 0;
newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.DBLCLICK, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
} else {
// click
this.clickTimeoutId_ = setTimeout(function() {
this.clickTimeoutId_ = 0;
const newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.SINGLECLICK, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
}.bind(this), 250);
}
}
/**
* @type {number}
* @private
*/
this.clickTimeoutId_ = 0;
/**
* @type {boolean}
* @private
*/
this.dragging_ = false;
/**
* @type {!Array.<module:ol/events~EventsKey>}
* @private
*/
this.dragListenerKeys_ = [];
/**
* @type {number}
* @private
*/
this.moveTolerance_ = moveTolerance ?
moveTolerance * DEVICE_PIXEL_RATIO : DEVICE_PIXEL_RATIO;
/**
* The most recent "down" type event (or null if none have occurred).
* Set on pointerdown.
* @type {module:ol/pointer/PointerEvent}
* @private
*/
this.down_ = null;
const element = this.map_.getViewport();
/**
* @type {number}
* @private
*/
this.activePointers_ = 0;
/**
* @type {!Object.<number, boolean>}
* @private
*/
this.trackedTouches_ = {};
/**
* Event handler which generates pointer events for
* the viewport element.
* Keeps track on how many pointers are currently active.
*
* @type {module:ol/pointer/PointerEventHandler}
* @param {module:ol/pointer/PointerEvent} pointerEvent Pointer
* event.
* @private
*/
this.pointerEventHandler_ = new PointerEventHandler(element);
updateActivePointers_(pointerEvent) {
const event = pointerEvent;
if (event.type == MapBrowserEventType.POINTERUP ||
event.type == MapBrowserEventType.POINTERCANCEL) {
delete this.trackedTouches_[event.pointerId];
} else if (event.type == MapBrowserEventType.POINTERDOWN) {
this.trackedTouches_[event.pointerId] = true;
}
this.activePointers_ = Object.keys(this.trackedTouches_).length;
}
/**
* Event handler which generates pointer events for
* the document (used when dragging).
*
* @type {module:ol/pointer/PointerEventHandler}
* @param {module:ol/pointer/PointerEvent} pointerEvent Pointer
* event.
* @private
*/
this.documentPointerEventHandler_ = null;
handlePointerUp_(pointerEvent) {
this.updateActivePointers_(pointerEvent);
const newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.POINTERUP, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
// We emulate click events on left mouse button click, touch contact, and pen
// contact. isMouseActionButton returns true in these cases (evt.button is set
// to 0).
// See http://www.w3.org/TR/pointerevents/#button-states
// We only fire click, singleclick, and doubleclick if nobody has called
// event.stopPropagation() or event.preventDefault().
if (!newEvent.propagationStopped && !this.dragging_ && this.isMouseActionButton_(pointerEvent)) {
this.emulateClick_(this.down_);
}
if (this.activePointers_ === 0) {
this.dragListenerKeys_.forEach(unlistenByKey);
this.dragListenerKeys_.length = 0;
this.dragging_ = false;
this.down_ = null;
this.documentPointerEventHandler_.dispose();
this.documentPointerEventHandler_ = null;
}
}
/**
* @type {?module:ol/events~EventsKey}
* @param {module:ol/pointer/PointerEvent} pointerEvent Pointer
* event.
* @return {boolean} If the left mouse button was pressed.
* @private
*/
this.pointerdownListenerKey_ = listen(this.pointerEventHandler_,
PointerEventType.POINTERDOWN,
this.handlePointerDown_, this);
isMouseActionButton_(pointerEvent) {
return pointerEvent.button === 0;
}
/**
* @type {?module:ol/events~EventsKey}
* @param {module:ol/pointer/PointerEvent} pointerEvent Pointer
* event.
* @private
*/
this.relayedListenerKey_ = listen(this.pointerEventHandler_,
PointerEventType.POINTERMOVE,
this.relayEvent_, this);
handlePointerDown_(pointerEvent) {
this.updateActivePointers_(pointerEvent);
const newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.POINTERDOWN, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
};
this.down_ = pointerEvent;
if (this.dragListenerKeys_.length === 0) {
/* Set up a pointer event handler on the `document`,
* which is required when the pointer is moved outside
* the viewport when dragging.
*/
this.documentPointerEventHandler_ =
new PointerEventHandler(document);
this.dragListenerKeys_.push(
listen(this.documentPointerEventHandler_,
MapBrowserEventType.POINTERMOVE,
this.handlePointerMove_, this),
listen(this.documentPointerEventHandler_,
MapBrowserEventType.POINTERUP,
this.handlePointerUp_, this),
/* Note that the listener for `pointercancel is set up on
* `pointerEventHandler_` and not `documentPointerEventHandler_` like
* the `pointerup` and `pointermove` listeners.
*
* The reason for this is the following: `TouchSource.vacuumTouches_()`
* issues `pointercancel` events, when there was no `touchend` for a
* `touchstart`. Now, let's say a first `touchstart` is registered on
* `pointerEventHandler_`. The `documentPointerEventHandler_` is set up.
* But `documentPointerEventHandler_` doesn't know about the first
* `touchstart`. If there is no `touchend` for the `touchstart`, we can
* only receive a `touchcancel` from `pointerEventHandler_`, because it is
* only registered there.
*/
listen(this.pointerEventHandler_,
MapBrowserEventType.POINTERCANCEL,
this.handlePointerUp_, this)
);
}
}
/**
* @param {module:ol/pointer/PointerEvent} pointerEvent Pointer
* event.
* @private
*/
handlePointerMove_(pointerEvent) {
// Between pointerdown and pointerup, pointermove events are triggered.
// To avoid a 'false' touchmove event to be dispatched, we test if the pointer
// moved a significant distance.
if (this.isMoving_(pointerEvent)) {
this.dragging_ = true;
const newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.POINTERDRAG, this.map_, pointerEvent,
this.dragging_);
this.dispatchEvent(newEvent);
}
// Some native android browser triggers mousemove events during small period
// of time. See: https://code.google.com/p/android/issues/detail?id=5491 or
// https://code.google.com/p/android/issues/detail?id=19827
// ex: Galaxy Tab P3110 + Android 4.1.1
pointerEvent.preventDefault();
}
/**
* Wrap and relay a pointer event. Note that this requires that the type
* string for the MapBrowserPointerEvent matches the PointerEvent type.
* @param {module:ol/pointer/PointerEvent} pointerEvent Pointer
* event.
* @private
*/
relayEvent_(pointerEvent) {
const dragging = !!(this.down_ && this.isMoving_(pointerEvent));
this.dispatchEvent(new MapBrowserPointerEvent(
pointerEvent.type, this.map_, pointerEvent, dragging));
}
/**
* @param {module:ol/pointer/PointerEvent} pointerEvent Pointer
* event.
* @return {boolean} Is moving.
* @private
*/
isMoving_(pointerEvent) {
return this.dragging_ ||
Math.abs(pointerEvent.clientX - this.down_.clientX) > this.moveTolerance_ ||
Math.abs(pointerEvent.clientY - this.down_.clientY) > this.moveTolerance_;
}
/**
* @inheritDoc
*/
disposeInternal() {
if (this.relayedListenerKey_) {
unlistenByKey(this.relayedListenerKey_);
this.relayedListenerKey_ = null;
}
if (this.pointerdownListenerKey_) {
unlistenByKey(this.pointerdownListenerKey_);
this.pointerdownListenerKey_ = null;
}
this.dragListenerKeys_.forEach(unlistenByKey);
this.dragListenerKeys_.length = 0;
if (this.documentPointerEventHandler_) {
this.documentPointerEventHandler_.dispose();
this.documentPointerEventHandler_ = null;
}
if (this.pointerEventHandler_) {
this.pointerEventHandler_.dispose();
this.pointerEventHandler_ = null;
}
EventTarget.prototype.disposeInternal.call(this);
}
}
inherits(MapBrowserEventHandler, EventTarget);
/**
* @param {module:ol/pointer/PointerEvent} pointerEvent Pointer
* event.
* @private
*/
MapBrowserEventHandler.prototype.emulateClick_ = function(pointerEvent) {
let newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.CLICK, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
if (this.clickTimeoutId_ !== 0) {
// double-click
clearTimeout(this.clickTimeoutId_);
this.clickTimeoutId_ = 0;
newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.DBLCLICK, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
} else {
// click
this.clickTimeoutId_ = setTimeout(function() {
this.clickTimeoutId_ = 0;
const newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.SINGLECLICK, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
}.bind(this), 250);
}
};
/**
* Keeps track on how many pointers are currently active.
*
* @param {module:ol/pointer/PointerEvent} pointerEvent Pointer
* event.
* @private
*/
MapBrowserEventHandler.prototype.updateActivePointers_ = function(pointerEvent) {
const event = pointerEvent;
if (event.type == MapBrowserEventType.POINTERUP ||
event.type == MapBrowserEventType.POINTERCANCEL) {
delete this.trackedTouches_[event.pointerId];
} else if (event.type == MapBrowserEventType.POINTERDOWN) {
this.trackedTouches_[event.pointerId] = true;
}
this.activePointers_ = Object.keys(this.trackedTouches_).length;
};
/**
* @param {module:ol/pointer/PointerEvent} pointerEvent Pointer
* event.
* @private
*/
MapBrowserEventHandler.prototype.handlePointerUp_ = function(pointerEvent) {
this.updateActivePointers_(pointerEvent);
const newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.POINTERUP, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
// We emulate click events on left mouse button click, touch contact, and pen
// contact. isMouseActionButton returns true in these cases (evt.button is set
// to 0).
// See http://www.w3.org/TR/pointerevents/#button-states
// We only fire click, singleclick, and doubleclick if nobody has called
// event.stopPropagation() or event.preventDefault().
if (!newEvent.propagationStopped && !this.dragging_ && this.isMouseActionButton_(pointerEvent)) {
this.emulateClick_(this.down_);
}
if (this.activePointers_ === 0) {
this.dragListenerKeys_.forEach(unlistenByKey);
this.dragListenerKeys_.length = 0;
this.dragging_ = false;
this.down_ = null;
this.documentPointerEventHandler_.dispose();
this.documentPointerEventHandler_ = null;
}
};
/**
* @param {module:ol/pointer/PointerEvent} pointerEvent Pointer
* event.
* @return {boolean} If the left mouse button was pressed.
* @private
*/
MapBrowserEventHandler.prototype.isMouseActionButton_ = function(pointerEvent) {
return pointerEvent.button === 0;
};
/**
* @param {module:ol/pointer/PointerEvent} pointerEvent Pointer
* event.
* @private
*/
MapBrowserEventHandler.prototype.handlePointerDown_ = function(pointerEvent) {
this.updateActivePointers_(pointerEvent);
const newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.POINTERDOWN, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
this.down_ = pointerEvent;
if (this.dragListenerKeys_.length === 0) {
/* Set up a pointer event handler on the `document`,
* which is required when the pointer is moved outside
* the viewport when dragging.
*/
this.documentPointerEventHandler_ =
new PointerEventHandler(document);
this.dragListenerKeys_.push(
listen(this.documentPointerEventHandler_,
MapBrowserEventType.POINTERMOVE,
this.handlePointerMove_, this),
listen(this.documentPointerEventHandler_,
MapBrowserEventType.POINTERUP,
this.handlePointerUp_, this),
/* Note that the listener for `pointercancel is set up on
* `pointerEventHandler_` and not `documentPointerEventHandler_` like
* the `pointerup` and `pointermove` listeners.
*
* The reason for this is the following: `TouchSource.vacuumTouches_()`
* issues `pointercancel` events, when there was no `touchend` for a
* `touchstart`. Now, let's say a first `touchstart` is registered on
* `pointerEventHandler_`. The `documentPointerEventHandler_` is set up.
* But `documentPointerEventHandler_` doesn't know about the first
* `touchstart`. If there is no `touchend` for the `touchstart`, we can
* only receive a `touchcancel` from `pointerEventHandler_`, because it is
* only registered there.
*/
listen(this.pointerEventHandler_,
MapBrowserEventType.POINTERCANCEL,
this.handlePointerUp_, this)
);
}
};
/**
* @param {module:ol/pointer/PointerEvent} pointerEvent Pointer
* event.
* @private
*/
MapBrowserEventHandler.prototype.handlePointerMove_ = function(pointerEvent) {
// Between pointerdown and pointerup, pointermove events are triggered.
// To avoid a 'false' touchmove event to be dispatched, we test if the pointer
// moved a significant distance.
if (this.isMoving_(pointerEvent)) {
this.dragging_ = true;
const newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.POINTERDRAG, this.map_, pointerEvent,
this.dragging_);
this.dispatchEvent(newEvent);
}
// Some native android browser triggers mousemove events during small period
// of time. See: https://code.google.com/p/android/issues/detail?id=5491 or
// https://code.google.com/p/android/issues/detail?id=19827
// ex: Galaxy Tab P3110 + Android 4.1.1
pointerEvent.preventDefault();
};
/**
* Wrap and relay a pointer event. Note that this requires that the type
* string for the MapBrowserPointerEvent matches the PointerEvent type.
* @param {module:ol/pointer/PointerEvent} pointerEvent Pointer
* event.
* @private
*/
MapBrowserEventHandler.prototype.relayEvent_ = function(pointerEvent) {
const dragging = !!(this.down_ && this.isMoving_(pointerEvent));
this.dispatchEvent(new MapBrowserPointerEvent(
pointerEvent.type, this.map_, pointerEvent, dragging));
};
/**
* @param {module:ol/pointer/PointerEvent} pointerEvent Pointer
* event.
* @return {boolean} Is moving.
* @private
*/
MapBrowserEventHandler.prototype.isMoving_ = function(pointerEvent) {
return this.dragging_ ||
Math.abs(pointerEvent.clientX - this.down_.clientX) > this.moveTolerance_ ||
Math.abs(pointerEvent.clientY - this.down_.clientY) > this.moveTolerance_;
};
/**
* @inheritDoc
*/
MapBrowserEventHandler.prototype.disposeInternal = function() {
if (this.relayedListenerKey_) {
unlistenByKey(this.relayedListenerKey_);
this.relayedListenerKey_ = null;
}
if (this.pointerdownListenerKey_) {
unlistenByKey(this.pointerdownListenerKey_);
this.pointerdownListenerKey_ = null;
}
this.dragListenerKeys_.forEach(unlistenByKey);
this.dragListenerKeys_.length = 0;
if (this.documentPointerEventHandler_) {
this.documentPointerEventHandler_.dispose();
this.documentPointerEventHandler_ = null;
}
if (this.pointerEventHandler_) {
this.pointerEventHandler_.dispose();
this.pointerEventHandler_ = null;
}
EventTarget.prototype.disposeInternal.call(this);
};
export default MapBrowserEventHandler;

View File

@@ -87,25 +87,119 @@ inherits(ObjectEvent, Event);
* @fires module:ol/Object~ObjectEvent
* @api
*/
const BaseObject = function(opt_values) {
Observable.call(this);
class BaseObject {
constructor(opt_values) {
Observable.call(this);
// Call {@link module:ol~getUid} to ensure that the order of objects' ids is
// the same as the order in which they were created. This also helps to
// ensure that object properties are always added in the same order, which
// helps many JavaScript engines generate faster code.
getUid(this);
// Call {@link module:ol~getUid} to ensure that the order of objects' ids is
// the same as the order in which they were created. This also helps to
// ensure that object properties are always added in the same order, which
// helps many JavaScript engines generate faster code.
getUid(this);
/**
* @private
* @type {!Object.<string, *>}
*/
this.values_ = {};
if (opt_values !== undefined) {
this.setProperties(opt_values);
}
}
/**
* @private
* @type {!Object.<string, *>}
* Gets a value.
* @param {string} key Key name.
* @return {*} Value.
* @api
*/
this.values_ = {};
if (opt_values !== undefined) {
this.setProperties(opt_values);
get(key) {
let value;
if (this.values_.hasOwnProperty(key)) {
value = this.values_[key];
}
return value;
}
};
/**
* Get a list of object property names.
* @return {Array.<string>} List of property names.
* @api
*/
getKeys() {
return Object.keys(this.values_);
}
/**
* Get an object of all property names and values.
* @return {Object.<string, *>} Object.
* @api
*/
getProperties() {
return assign({}, this.values_);
}
/**
* @param {string} key Key name.
* @param {*} oldValue Old value.
*/
notify(key, oldValue) {
let eventType;
eventType = getChangeEventType(key);
this.dispatchEvent(new ObjectEvent(eventType, key, oldValue));
eventType = ObjectEventType.PROPERTYCHANGE;
this.dispatchEvent(new ObjectEvent(eventType, key, oldValue));
}
/**
* Sets a value.
* @param {string} key Key name.
* @param {*} value Value.
* @param {boolean=} opt_silent Update without triggering an event.
* @api
*/
set(key, value, opt_silent) {
if (opt_silent) {
this.values_[key] = value;
} else {
const oldValue = this.values_[key];
this.values_[key] = value;
if (oldValue !== value) {
this.notify(key, oldValue);
}
}
}
/**
* Sets a collection of key-value pairs. Note that this changes any existing
* properties and adds new ones (it does not remove any existing properties).
* @param {Object.<string, *>} values Values.
* @param {boolean=} opt_silent Update without triggering an event.
* @api
*/
setProperties(values, opt_silent) {
for (const key in values) {
this.set(key, values[key], opt_silent);
}
}
/**
* Unsets a property.
* @param {string} key Key name.
* @param {boolean=} opt_silent Unset without triggering an event.
* @api
*/
unset(key, opt_silent) {
if (key in this.values_) {
const oldValue = this.values_[key];
delete this.values_[key];
if (!opt_silent) {
this.notify(key, oldValue);
}
}
}
}
inherits(BaseObject, Observable);
@@ -127,103 +221,4 @@ export function getChangeEventType(key) {
}
/**
* Gets a value.
* @param {string} key Key name.
* @return {*} Value.
* @api
*/
BaseObject.prototype.get = function(key) {
let value;
if (this.values_.hasOwnProperty(key)) {
value = this.values_[key];
}
return value;
};
/**
* Get a list of object property names.
* @return {Array.<string>} List of property names.
* @api
*/
BaseObject.prototype.getKeys = function() {
return Object.keys(this.values_);
};
/**
* Get an object of all property names and values.
* @return {Object.<string, *>} Object.
* @api
*/
BaseObject.prototype.getProperties = function() {
return assign({}, this.values_);
};
/**
* @param {string} key Key name.
* @param {*} oldValue Old value.
*/
BaseObject.prototype.notify = function(key, oldValue) {
let eventType;
eventType = getChangeEventType(key);
this.dispatchEvent(new ObjectEvent(eventType, key, oldValue));
eventType = ObjectEventType.PROPERTYCHANGE;
this.dispatchEvent(new ObjectEvent(eventType, key, oldValue));
};
/**
* Sets a value.
* @param {string} key Key name.
* @param {*} value Value.
* @param {boolean=} opt_silent Update without triggering an event.
* @api
*/
BaseObject.prototype.set = function(key, value, opt_silent) {
if (opt_silent) {
this.values_[key] = value;
} else {
const oldValue = this.values_[key];
this.values_[key] = value;
if (oldValue !== value) {
this.notify(key, oldValue);
}
}
};
/**
* Sets a collection of key-value pairs. Note that this changes any existing
* properties and adds new ones (it does not remove any existing properties).
* @param {Object.<string, *>} values Values.
* @param {boolean=} opt_silent Update without triggering an event.
* @api
*/
BaseObject.prototype.setProperties = function(values, opt_silent) {
for (const key in values) {
this.set(key, values[key], opt_silent);
}
};
/**
* Unsets a property.
* @param {string} key Key name.
* @param {boolean=} opt_silent Unset without triggering an event.
* @api
*/
BaseObject.prototype.unset = function(key, opt_silent) {
if (key in this.values_) {
const oldValue = this.values_[key];
delete this.values_[key];
if (!opt_silent) {
this.notify(key, oldValue);
}
}
};
export default BaseObject;

View File

@@ -20,17 +20,99 @@ import EventType from './events/EventType.js';
* @struct
* @api
*/
const Observable = function() {
class Observable {
constructor() {
EventTarget.call(this);
EventTarget.call(this);
/**
* @private
* @type {number}
*/
this.revision_ = 0;
}
/**
* @private
* @type {number}
* Increases the revision counter and dispatches a 'change' event.
* @api
*/
this.revision_ = 0;
changed() {
++this.revision_;
this.dispatchEvent(EventType.CHANGE);
}
};
/**
* Get the version number for this object. Each time the object is modified,
* its version number will be incremented.
* @return {number} Revision.
* @api
*/
getRevision() {
return this.revision_;
}
/**
* Listen for a certain type of event.
* @param {string|Array.<string>} type The event type or array of event types.
* @param {function(?): ?} listener The listener function.
* @return {module:ol/events~EventsKey|Array.<module:ol/events~EventsKey>} Unique key for the listener. If
* called with an array of event types as the first argument, the return
* will be an array of keys.
* @api
*/
on(type, listener) {
if (Array.isArray(type)) {
const len = type.length;
const keys = new Array(len);
for (let i = 0; i < len; ++i) {
keys[i] = listen(this, type[i], listener);
}
return keys;
} else {
return listen(this, /** @type {string} */ (type), listener);
}
}
/**
* Listen once for a certain type of event.
* @param {string|Array.<string>} type The event type or array of event types.
* @param {function(?): ?} listener The listener function.
* @return {module:ol/events~EventsKey|Array.<module:ol/events~EventsKey>} Unique key for the listener. If
* called with an array of event types as the first argument, the return
* will be an array of keys.
* @api
*/
once(type, listener) {
if (Array.isArray(type)) {
const len = type.length;
const keys = new Array(len);
for (let i = 0; i < len; ++i) {
keys[i] = listenOnce(this, type[i], listener);
}
return keys;
} else {
return listenOnce(this, /** @type {string} */ (type), listener);
}
}
/**
* Unlisten for a certain type of event.
* @param {string|Array.<string>} type The event type or array of event types.
* @param {function(?): ?} listener The listener function.
* @api
*/
un(type, listener) {
if (Array.isArray(type)) {
for (let i = 0, ii = type.length; i < ii; ++i) {
unlisten(this, type[i], listener);
}
return;
} else {
unlisten(this, /** @type {string} */ (type), listener);
}
}
}
inherits(Observable, EventTarget);
@@ -52,16 +134,6 @@ export function unByKey(key) {
}
/**
* Increases the revision counter and dispatches a 'change' event.
* @api
*/
Observable.prototype.changed = function() {
++this.revision_;
this.dispatchEvent(EventType.CHANGE);
};
/**
* Dispatches an event and calls all listeners listening for events
* of this type. The event parameter can either be a string or an
@@ -76,77 +148,4 @@ Observable.prototype.changed = function() {
Observable.prototype.dispatchEvent;
/**
* Get the version number for this object. Each time the object is modified,
* its version number will be incremented.
* @return {number} Revision.
* @api
*/
Observable.prototype.getRevision = function() {
return this.revision_;
};
/**
* Listen for a certain type of event.
* @param {string|Array.<string>} type The event type or array of event types.
* @param {function(?): ?} listener The listener function.
* @return {module:ol/events~EventsKey|Array.<module:ol/events~EventsKey>} Unique key for the listener. If
* called with an array of event types as the first argument, the return
* will be an array of keys.
* @api
*/
Observable.prototype.on = function(type, listener) {
if (Array.isArray(type)) {
const len = type.length;
const keys = new Array(len);
for (let i = 0; i < len; ++i) {
keys[i] = listen(this, type[i], listener);
}
return keys;
} else {
return listen(this, /** @type {string} */ (type), listener);
}
};
/**
* Listen once for a certain type of event.
* @param {string|Array.<string>} type The event type or array of event types.
* @param {function(?): ?} listener The listener function.
* @return {module:ol/events~EventsKey|Array.<module:ol/events~EventsKey>} Unique key for the listener. If
* called with an array of event types as the first argument, the return
* will be an array of keys.
* @api
*/
Observable.prototype.once = function(type, listener) {
if (Array.isArray(type)) {
const len = type.length;
const keys = new Array(len);
for (let i = 0; i < len; ++i) {
keys[i] = listenOnce(this, type[i], listener);
}
return keys;
} else {
return listenOnce(this, /** @type {string} */ (type), listener);
}
};
/**
* Unlisten for a certain type of event.
* @param {string|Array.<string>} type The event type or array of event types.
* @param {function(?): ?} listener The listener function.
* @api
*/
Observable.prototype.un = function(type, listener) {
if (Array.isArray(type)) {
for (let i = 0, ii = type.length; i < ii; ++i) {
unlisten(this, type[i], listener);
}
return;
} else {
unlisten(this, /** @type {string} */ (type), listener);
}
};
export default Observable;

View File

@@ -98,510 +98,490 @@ const Property = {
* @param {module:ol/Overlay~Options} options Overlay options.
* @api
*/
const Overlay = function(options) {
class Overlay {
constructor(options) {
BaseObject.call(this);
BaseObject.call(this);
/**
* @protected
* @type {module:ol/Overlay~Options}
*/
this.options = options;
/**
* @protected
* @type {module:ol/Overlay~Options}
*/
this.options = options;
/**
* @protected
* @type {number|string|undefined}
*/
this.id = options.id;
/**
* @protected
* @type {number|string|undefined}
*/
this.id = options.id;
/**
* @protected
* @type {boolean}
*/
this.insertFirst = options.insertFirst !== undefined ?
options.insertFirst : true;
/**
* @protected
* @type {boolean}
*/
this.insertFirst = options.insertFirst !== undefined ?
options.insertFirst : true;
/**
* @protected
* @type {boolean}
*/
this.stopEvent = options.stopEvent !== undefined ? options.stopEvent : true;
/**
* @protected
* @type {boolean}
*/
this.stopEvent = options.stopEvent !== undefined ? options.stopEvent : true;
/**
* @protected
* @type {HTMLElement}
*/
this.element = document.createElement('DIV');
this.element.className = options.className !== undefined ?
options.className : 'ol-overlay-container ' + CLASS_SELECTABLE;
this.element.style.position = 'absolute';
/**
* @protected
* @type {HTMLElement}
*/
this.element = document.createElement('DIV');
this.element.className = options.className !== undefined ?
options.className : 'ol-overlay-container ' + CLASS_SELECTABLE;
this.element.style.position = 'absolute';
/**
* @protected
* @type {boolean}
*/
this.autoPan = options.autoPan !== undefined ? options.autoPan : false;
/**
* @protected
* @type {boolean}
*/
this.autoPan = options.autoPan !== undefined ? options.autoPan : false;
/**
* @protected
* @type {module:ol/Overlay~PanOptions}
*/
this.autoPanAnimation = options.autoPanAnimation || /** @type {module:ol/Overlay~PanOptions} */ ({});
/**
* @protected
* @type {module:ol/Overlay~PanOptions}
*/
this.autoPanAnimation = options.autoPanAnimation || /** @type {module:ol/Overlay~PanOptions} */ ({});
/**
* @protected
* @type {number}
*/
this.autoPanMargin = options.autoPanMargin !== undefined ?
options.autoPanMargin : 20;
/**
* @protected
* @type {number}
*/
this.autoPanMargin = options.autoPanMargin !== undefined ?
options.autoPanMargin : 20;
/**
* @protected
* @type {{bottom_: string,
* left_: string,
* right_: string,
* top_: string,
* visible: boolean}}
*/
this.rendered = {
bottom_: '',
left_: '',
right_: '',
top_: '',
visible: true
};
/**
* @protected
* @type {{bottom_: string,
* left_: string,
* right_: string,
* top_: string,
* visible: boolean}}
*/
this.rendered = {
bottom_: '',
left_: '',
right_: '',
top_: '',
visible: true
};
/**
* @protected
* @type {?module:ol/events~EventsKey}
*/
this.mapPostrenderListenerKey = null;
/**
* @protected
* @type {?module:ol/events~EventsKey}
*/
this.mapPostrenderListenerKey = null;
listen(
this, getChangeEventType(Property.ELEMENT),
this.handleElementChanged, this);
listen(
this, getChangeEventType(Property.ELEMENT),
this.handleElementChanged, this);
listen(
this, getChangeEventType(Property.MAP),
this.handleMapChanged, this);
listen(
this, getChangeEventType(Property.MAP),
this.handleMapChanged, this);
listen(
this, getChangeEventType(Property.OFFSET),
this.handleOffsetChanged, this);
listen(
this, getChangeEventType(Property.OFFSET),
this.handleOffsetChanged, this);
listen(
this, getChangeEventType(Property.POSITION),
this.handlePositionChanged, this);
listen(
this, getChangeEventType(Property.POSITION),
this.handlePositionChanged, this);
listen(
this, getChangeEventType(Property.POSITIONING),
this.handlePositioningChanged, this);
listen(
this, getChangeEventType(Property.POSITIONING),
this.handlePositioningChanged, this);
if (options.element !== undefined) {
this.setElement(options.element);
}
this.setOffset(options.offset !== undefined ? options.offset : [0, 0]);
this.setPositioning(options.positioning !== undefined ?
/** @type {module:ol/OverlayPositioning} */ (options.positioning) :
OverlayPositioning.TOP_LEFT);
if (options.position !== undefined) {
this.setPosition(options.position);
}
if (options.element !== undefined) {
this.setElement(options.element);
}
this.setOffset(options.offset !== undefined ? options.offset : [0, 0]);
this.setPositioning(options.positioning !== undefined ?
/** @type {module:ol/OverlayPositioning} */ (options.positioning) :
OverlayPositioning.TOP_LEFT);
if (options.position !== undefined) {
this.setPosition(options.position);
/**
* Get the DOM element of this overlay.
* @return {HTMLElement|undefined} The Element containing the overlay.
* @observable
* @api
*/
getElement() {
return /** @type {HTMLElement|undefined} */ (this.get(Property.ELEMENT));
}
};
/**
* Get the overlay identifier which is set on constructor.
* @return {number|string|undefined} Id.
* @api
*/
getId() {
return this.id;
}
/**
* Get the map associated with this overlay.
* @return {module:ol/PluggableMap|undefined} The map that the
* overlay is part of.
* @observable
* @api
*/
getMap() {
return (
/** @type {module:ol/PluggableMap|undefined} */ (this.get(Property.MAP))
);
}
/**
* Get the offset of this overlay.
* @return {Array.<number>} The offset.
* @observable
* @api
*/
getOffset() {
return /** @type {Array.<number>} */ (this.get(Property.OFFSET));
}
/**
* Get the current position of this overlay.
* @return {module:ol/coordinate~Coordinate|undefined} The spatial point that the overlay is
* anchored at.
* @observable
* @api
*/
getPosition() {
return (
/** @type {module:ol/coordinate~Coordinate|undefined} */ (this.get(Property.POSITION))
);
}
/**
* Get the current positioning of this overlay.
* @return {module:ol/OverlayPositioning} How the overlay is positioned
* relative to its point on the map.
* @observable
* @api
*/
getPositioning() {
return (
/** @type {module:ol/OverlayPositioning} */ (this.get(Property.POSITIONING))
);
}
/**
* @protected
*/
handleElementChanged() {
removeChildren(this.element);
const element = this.getElement();
if (element) {
this.element.appendChild(element);
}
}
/**
* @protected
*/
handleMapChanged() {
if (this.mapPostrenderListenerKey) {
removeNode(this.element);
unlistenByKey(this.mapPostrenderListenerKey);
this.mapPostrenderListenerKey = null;
}
const map = this.getMap();
if (map) {
this.mapPostrenderListenerKey = listen(map,
MapEventType.POSTRENDER, this.render, this);
this.updatePixelPosition();
const container = this.stopEvent ?
map.getOverlayContainerStopEvent() : map.getOverlayContainer();
if (this.insertFirst) {
container.insertBefore(this.element, container.childNodes[0] || null);
} else {
container.appendChild(this.element);
}
}
}
/**
* @protected
*/
render() {
this.updatePixelPosition();
}
/**
* @protected
*/
handleOffsetChanged() {
this.updatePixelPosition();
}
/**
* @protected
*/
handlePositionChanged() {
this.updatePixelPosition();
if (this.get(Property.POSITION) && this.autoPan) {
this.panIntoView();
}
}
/**
* @protected
*/
handlePositioningChanged() {
this.updatePixelPosition();
}
/**
* Set the DOM element to be associated with this overlay.
* @param {HTMLElement|undefined} element The Element containing the overlay.
* @observable
* @api
*/
setElement(element) {
this.set(Property.ELEMENT, element);
}
/**
* Set the map to be associated with this overlay.
* @param {module:ol/PluggableMap|undefined} map The map that the
* overlay is part of.
* @observable
* @api
*/
setMap(map) {
this.set(Property.MAP, map);
}
/**
* Set the offset for this overlay.
* @param {Array.<number>} offset Offset.
* @observable
* @api
*/
setOffset(offset) {
this.set(Property.OFFSET, offset);
}
/**
* Set the position for this overlay. If the position is `undefined` the
* overlay is hidden.
* @param {module:ol/coordinate~Coordinate|undefined} position The spatial point that the overlay
* is anchored at.
* @observable
* @api
*/
setPosition(position) {
this.set(Property.POSITION, position);
}
/**
* Pan the map so that the overlay is entirely visible in the current viewport
* (if necessary).
* @protected
*/
panIntoView() {
const map = this.getMap();
if (!map || !map.getTargetElement()) {
return;
}
const mapRect = this.getRect(map.getTargetElement(), map.getSize());
const element = this.getElement();
const overlayRect = this.getRect(element, [outerWidth(element), outerHeight(element)]);
const margin = this.autoPanMargin;
if (!containsExtent(mapRect, overlayRect)) {
// the overlay is not completely inside the viewport, so pan the map
const offsetLeft = overlayRect[0] - mapRect[0];
const offsetRight = mapRect[2] - overlayRect[2];
const offsetTop = overlayRect[1] - mapRect[1];
const offsetBottom = mapRect[3] - overlayRect[3];
const delta = [0, 0];
if (offsetLeft < 0) {
// move map to the left
delta[0] = offsetLeft - margin;
} else if (offsetRight < 0) {
// move map to the right
delta[0] = Math.abs(offsetRight) + margin;
}
if (offsetTop < 0) {
// move map up
delta[1] = offsetTop - margin;
} else if (offsetBottom < 0) {
// move map down
delta[1] = Math.abs(offsetBottom) + margin;
}
if (delta[0] !== 0 || delta[1] !== 0) {
const center = /** @type {module:ol/coordinate~Coordinate} */ (map.getView().getCenter());
const centerPx = map.getPixelFromCoordinate(center);
const newCenterPx = [
centerPx[0] + delta[0],
centerPx[1] + delta[1]
];
map.getView().animate({
center: map.getCoordinateFromPixel(newCenterPx),
duration: this.autoPanAnimation.duration,
easing: this.autoPanAnimation.easing
});
}
}
}
/**
* Get the extent of an element relative to the document
* @param {HTMLElement|undefined} element The element.
* @param {module:ol/size~Size|undefined} size The size of the element.
* @return {module:ol/extent~Extent} The extent.
* @protected
*/
getRect(element, size) {
const box = element.getBoundingClientRect();
const offsetX = box.left + window.pageXOffset;
const offsetY = box.top + window.pageYOffset;
return [
offsetX,
offsetY,
offsetX + size[0],
offsetY + size[1]
];
}
/**
* Set the positioning for this overlay.
* @param {module:ol/OverlayPositioning} positioning how the overlay is
* positioned relative to its point on the map.
* @observable
* @api
*/
setPositioning(positioning) {
this.set(Property.POSITIONING, positioning);
}
/**
* Modify the visibility of the element.
* @param {boolean} visible Element visibility.
* @protected
*/
setVisible(visible) {
if (this.rendered.visible !== visible) {
this.element.style.display = visible ? '' : 'none';
this.rendered.visible = visible;
}
}
/**
* Update pixel position.
* @protected
*/
updatePixelPosition() {
const map = this.getMap();
const position = this.getPosition();
if (!map || !map.isRendered() || !position) {
this.setVisible(false);
return;
}
const pixel = map.getPixelFromCoordinate(position);
const mapSize = map.getSize();
this.updateRenderedPosition(pixel, mapSize);
}
/**
* @param {module:ol~Pixel} pixel The pixel location.
* @param {module:ol/size~Size|undefined} mapSize The map size.
* @protected
*/
updateRenderedPosition(pixel, mapSize) {
const style = this.element.style;
const offset = this.getOffset();
const positioning = this.getPositioning();
this.setVisible(true);
let offsetX = offset[0];
let offsetY = offset[1];
if (positioning == OverlayPositioning.BOTTOM_RIGHT ||
positioning == OverlayPositioning.CENTER_RIGHT ||
positioning == OverlayPositioning.TOP_RIGHT) {
if (this.rendered.left_ !== '') {
this.rendered.left_ = style.left = '';
}
const right = Math.round(mapSize[0] - pixel[0] - offsetX) + 'px';
if (this.rendered.right_ != right) {
this.rendered.right_ = style.right = right;
}
} else {
if (this.rendered.right_ !== '') {
this.rendered.right_ = style.right = '';
}
if (positioning == OverlayPositioning.BOTTOM_CENTER ||
positioning == OverlayPositioning.CENTER_CENTER ||
positioning == OverlayPositioning.TOP_CENTER) {
offsetX -= this.element.offsetWidth / 2;
}
const left = Math.round(pixel[0] + offsetX) + 'px';
if (this.rendered.left_ != left) {
this.rendered.left_ = style.left = left;
}
}
if (positioning == OverlayPositioning.BOTTOM_LEFT ||
positioning == OverlayPositioning.BOTTOM_CENTER ||
positioning == OverlayPositioning.BOTTOM_RIGHT) {
if (this.rendered.top_ !== '') {
this.rendered.top_ = style.top = '';
}
const bottom = Math.round(mapSize[1] - pixel[1] - offsetY) + 'px';
if (this.rendered.bottom_ != bottom) {
this.rendered.bottom_ = style.bottom = bottom;
}
} else {
if (this.rendered.bottom_ !== '') {
this.rendered.bottom_ = style.bottom = '';
}
if (positioning == OverlayPositioning.CENTER_LEFT ||
positioning == OverlayPositioning.CENTER_CENTER ||
positioning == OverlayPositioning.CENTER_RIGHT) {
offsetY -= this.element.offsetHeight / 2;
}
const top = Math.round(pixel[1] + offsetY) + 'px';
if (this.rendered.top_ != top) {
this.rendered.top_ = style.top = top;
}
}
}
/**
* returns the options this Overlay has been created with
* @return {module:ol/Overlay~Options} overlay options
*/
getOptions() {
return this.options;
}
}
inherits(Overlay, BaseObject);
/**
* Get the DOM element of this overlay.
* @return {HTMLElement|undefined} The Element containing the overlay.
* @observable
* @api
*/
Overlay.prototype.getElement = function() {
return /** @type {HTMLElement|undefined} */ (this.get(Property.ELEMENT));
};
/**
* Get the overlay identifier which is set on constructor.
* @return {number|string|undefined} Id.
* @api
*/
Overlay.prototype.getId = function() {
return this.id;
};
/**
* Get the map associated with this overlay.
* @return {module:ol/PluggableMap|undefined} The map that the
* overlay is part of.
* @observable
* @api
*/
Overlay.prototype.getMap = function() {
return (
/** @type {module:ol/PluggableMap|undefined} */ (this.get(Property.MAP))
);
};
/**
* Get the offset of this overlay.
* @return {Array.<number>} The offset.
* @observable
* @api
*/
Overlay.prototype.getOffset = function() {
return /** @type {Array.<number>} */ (this.get(Property.OFFSET));
};
/**
* Get the current position of this overlay.
* @return {module:ol/coordinate~Coordinate|undefined} The spatial point that the overlay is
* anchored at.
* @observable
* @api
*/
Overlay.prototype.getPosition = function() {
return (
/** @type {module:ol/coordinate~Coordinate|undefined} */ (this.get(Property.POSITION))
);
};
/**
* Get the current positioning of this overlay.
* @return {module:ol/OverlayPositioning} How the overlay is positioned
* relative to its point on the map.
* @observable
* @api
*/
Overlay.prototype.getPositioning = function() {
return (
/** @type {module:ol/OverlayPositioning} */ (this.get(Property.POSITIONING))
);
};
/**
* @protected
*/
Overlay.prototype.handleElementChanged = function() {
removeChildren(this.element);
const element = this.getElement();
if (element) {
this.element.appendChild(element);
}
};
/**
* @protected
*/
Overlay.prototype.handleMapChanged = function() {
if (this.mapPostrenderListenerKey) {
removeNode(this.element);
unlistenByKey(this.mapPostrenderListenerKey);
this.mapPostrenderListenerKey = null;
}
const map = this.getMap();
if (map) {
this.mapPostrenderListenerKey = listen(map,
MapEventType.POSTRENDER, this.render, this);
this.updatePixelPosition();
const container = this.stopEvent ?
map.getOverlayContainerStopEvent() : map.getOverlayContainer();
if (this.insertFirst) {
container.insertBefore(this.element, container.childNodes[0] || null);
} else {
container.appendChild(this.element);
}
}
};
/**
* @protected
*/
Overlay.prototype.render = function() {
this.updatePixelPosition();
};
/**
* @protected
*/
Overlay.prototype.handleOffsetChanged = function() {
this.updatePixelPosition();
};
/**
* @protected
*/
Overlay.prototype.handlePositionChanged = function() {
this.updatePixelPosition();
if (this.get(Property.POSITION) && this.autoPan) {
this.panIntoView();
}
};
/**
* @protected
*/
Overlay.prototype.handlePositioningChanged = function() {
this.updatePixelPosition();
};
/**
* Set the DOM element to be associated with this overlay.
* @param {HTMLElement|undefined} element The Element containing the overlay.
* @observable
* @api
*/
Overlay.prototype.setElement = function(element) {
this.set(Property.ELEMENT, element);
};
/**
* Set the map to be associated with this overlay.
* @param {module:ol/PluggableMap|undefined} map The map that the
* overlay is part of.
* @observable
* @api
*/
Overlay.prototype.setMap = function(map) {
this.set(Property.MAP, map);
};
/**
* Set the offset for this overlay.
* @param {Array.<number>} offset Offset.
* @observable
* @api
*/
Overlay.prototype.setOffset = function(offset) {
this.set(Property.OFFSET, offset);
};
/**
* Set the position for this overlay. If the position is `undefined` the
* overlay is hidden.
* @param {module:ol/coordinate~Coordinate|undefined} position The spatial point that the overlay
* is anchored at.
* @observable
* @api
*/
Overlay.prototype.setPosition = function(position) {
this.set(Property.POSITION, position);
};
/**
* Pan the map so that the overlay is entirely visible in the current viewport
* (if necessary).
* @protected
*/
Overlay.prototype.panIntoView = function() {
const map = this.getMap();
if (!map || !map.getTargetElement()) {
return;
}
const mapRect = this.getRect(map.getTargetElement(), map.getSize());
const element = this.getElement();
const overlayRect = this.getRect(element, [outerWidth(element), outerHeight(element)]);
const margin = this.autoPanMargin;
if (!containsExtent(mapRect, overlayRect)) {
// the overlay is not completely inside the viewport, so pan the map
const offsetLeft = overlayRect[0] - mapRect[0];
const offsetRight = mapRect[2] - overlayRect[2];
const offsetTop = overlayRect[1] - mapRect[1];
const offsetBottom = mapRect[3] - overlayRect[3];
const delta = [0, 0];
if (offsetLeft < 0) {
// move map to the left
delta[0] = offsetLeft - margin;
} else if (offsetRight < 0) {
// move map to the right
delta[0] = Math.abs(offsetRight) + margin;
}
if (offsetTop < 0) {
// move map up
delta[1] = offsetTop - margin;
} else if (offsetBottom < 0) {
// move map down
delta[1] = Math.abs(offsetBottom) + margin;
}
if (delta[0] !== 0 || delta[1] !== 0) {
const center = /** @type {module:ol/coordinate~Coordinate} */ (map.getView().getCenter());
const centerPx = map.getPixelFromCoordinate(center);
const newCenterPx = [
centerPx[0] + delta[0],
centerPx[1] + delta[1]
];
map.getView().animate({
center: map.getCoordinateFromPixel(newCenterPx),
duration: this.autoPanAnimation.duration,
easing: this.autoPanAnimation.easing
});
}
}
};
/**
* Get the extent of an element relative to the document
* @param {HTMLElement|undefined} element The element.
* @param {module:ol/size~Size|undefined} size The size of the element.
* @return {module:ol/extent~Extent} The extent.
* @protected
*/
Overlay.prototype.getRect = function(element, size) {
const box = element.getBoundingClientRect();
const offsetX = box.left + window.pageXOffset;
const offsetY = box.top + window.pageYOffset;
return [
offsetX,
offsetY,
offsetX + size[0],
offsetY + size[1]
];
};
/**
* Set the positioning for this overlay.
* @param {module:ol/OverlayPositioning} positioning how the overlay is
* positioned relative to its point on the map.
* @observable
* @api
*/
Overlay.prototype.setPositioning = function(positioning) {
this.set(Property.POSITIONING, positioning);
};
/**
* Modify the visibility of the element.
* @param {boolean} visible Element visibility.
* @protected
*/
Overlay.prototype.setVisible = function(visible) {
if (this.rendered.visible !== visible) {
this.element.style.display = visible ? '' : 'none';
this.rendered.visible = visible;
}
};
/**
* Update pixel position.
* @protected
*/
Overlay.prototype.updatePixelPosition = function() {
const map = this.getMap();
const position = this.getPosition();
if (!map || !map.isRendered() || !position) {
this.setVisible(false);
return;
}
const pixel = map.getPixelFromCoordinate(position);
const mapSize = map.getSize();
this.updateRenderedPosition(pixel, mapSize);
};
/**
* @param {module:ol~Pixel} pixel The pixel location.
* @param {module:ol/size~Size|undefined} mapSize The map size.
* @protected
*/
Overlay.prototype.updateRenderedPosition = function(pixel, mapSize) {
const style = this.element.style;
const offset = this.getOffset();
const positioning = this.getPositioning();
this.setVisible(true);
let offsetX = offset[0];
let offsetY = offset[1];
if (positioning == OverlayPositioning.BOTTOM_RIGHT ||
positioning == OverlayPositioning.CENTER_RIGHT ||
positioning == OverlayPositioning.TOP_RIGHT) {
if (this.rendered.left_ !== '') {
this.rendered.left_ = style.left = '';
}
const right = Math.round(mapSize[0] - pixel[0] - offsetX) + 'px';
if (this.rendered.right_ != right) {
this.rendered.right_ = style.right = right;
}
} else {
if (this.rendered.right_ !== '') {
this.rendered.right_ = style.right = '';
}
if (positioning == OverlayPositioning.BOTTOM_CENTER ||
positioning == OverlayPositioning.CENTER_CENTER ||
positioning == OverlayPositioning.TOP_CENTER) {
offsetX -= this.element.offsetWidth / 2;
}
const left = Math.round(pixel[0] + offsetX) + 'px';
if (this.rendered.left_ != left) {
this.rendered.left_ = style.left = left;
}
}
if (positioning == OverlayPositioning.BOTTOM_LEFT ||
positioning == OverlayPositioning.BOTTOM_CENTER ||
positioning == OverlayPositioning.BOTTOM_RIGHT) {
if (this.rendered.top_ !== '') {
this.rendered.top_ = style.top = '';
}
const bottom = Math.round(mapSize[1] - pixel[1] - offsetY) + 'px';
if (this.rendered.bottom_ != bottom) {
this.rendered.bottom_ = style.bottom = bottom;
}
} else {
if (this.rendered.bottom_ !== '') {
this.rendered.bottom_ = style.bottom = '';
}
if (positioning == OverlayPositioning.CENTER_LEFT ||
positioning == OverlayPositioning.CENTER_CENTER ||
positioning == OverlayPositioning.CENTER_RIGHT) {
offsetY -= this.element.offsetHeight / 2;
}
const top = Math.round(pixel[1] + offsetY) + 'px';
if (this.rendered.top_ != top) {
this.rendered.top_ = style.top = top;
}
}
};
/**
* returns the options this Overlay has been created with
* @return {module:ol/Overlay~Options} overlay options
*/
Overlay.prototype.getOptions = function() {
return this.options;
};
export default Overlay;

File diff suppressed because it is too large Load Diff

View File

@@ -47,163 +47,272 @@ import {visibleAtResolution} from '../layer/Layer.js';
* @param {module:ol/control/Attribution~Options=} opt_options Attribution options.
* @api
*/
const Attribution = function(opt_options) {
class Attribution {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
/**
* @private
* @type {Element}
*/
this.ulElement_ = document.createElement('UL');
/**
* @private
* @type {boolean}
*/
this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true;
/**
* @private
* @type {boolean}
*/
this.collapsible_ = options.collapsible !== undefined ?
options.collapsible : true;
if (!this.collapsible_) {
this.collapsed_ = false;
}
const className = options.className !== undefined ? options.className : 'ol-attribution';
const tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Attributions';
const collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\u00BB';
if (typeof collapseLabel === 'string') {
/**
* @private
* @type {Element}
*/
this.collapseLabel_ = document.createElement('span');
this.collapseLabel_.textContent = collapseLabel;
} else {
this.collapseLabel_ = collapseLabel;
}
this.ulElement_ = document.createElement('UL');
const label = options.label !== undefined ? options.label : 'i';
if (typeof label === 'string') {
/**
* @private
* @type {Element}
* @type {boolean}
*/
this.label_ = document.createElement('span');
this.label_.textContent = label;
} else {
this.label_ = label;
this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true;
/**
* @private
* @type {boolean}
*/
this.collapsible_ = options.collapsible !== undefined ?
options.collapsible : true;
if (!this.collapsible_) {
this.collapsed_ = false;
}
const className = options.className !== undefined ? options.className : 'ol-attribution';
const tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Attributions';
const collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\u00BB';
if (typeof collapseLabel === 'string') {
/**
* @private
* @type {Element}
*/
this.collapseLabel_ = document.createElement('span');
this.collapseLabel_.textContent = collapseLabel;
} else {
this.collapseLabel_ = collapseLabel;
}
const label = options.label !== undefined ? options.label : 'i';
if (typeof label === 'string') {
/**
* @private
* @type {Element}
*/
this.label_ = document.createElement('span');
this.label_.textContent = label;
} else {
this.label_ = label;
}
const activeLabel = (this.collapsible_ && !this.collapsed_) ?
this.collapseLabel_ : this.label_;
const button = document.createElement('button');
button.setAttribute('type', 'button');
button.title = tipLabel;
button.appendChild(activeLabel);
listen(button, EventType.CLICK, this.handleClick_, this);
const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL +
(this.collapsed_ && this.collapsible_ ? ' ' + CLASS_COLLAPSED : '') +
(this.collapsible_ ? '' : ' ol-uncollapsible');
const element = document.createElement('div');
element.className = cssClasses;
element.appendChild(this.ulElement_);
element.appendChild(button);
Control.call(this, {
element: element,
render: options.render || render,
target: options.target
});
/**
* A list of currently rendered resolutions.
* @type {Array.<string>}
* @private
*/
this.renderedAttributions_ = [];
/**
* @private
* @type {boolean}
*/
this.renderedVisible_ = true;
}
const activeLabel = (this.collapsible_ && !this.collapsed_) ?
this.collapseLabel_ : this.label_;
const button = document.createElement('button');
button.setAttribute('type', 'button');
button.title = tipLabel;
button.appendChild(activeLabel);
listen(button, EventType.CLICK, this.handleClick_, this);
const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL +
(this.collapsed_ && this.collapsible_ ? ' ' + CLASS_COLLAPSED : '') +
(this.collapsible_ ? '' : ' ol-uncollapsible');
const element = document.createElement('div');
element.className = cssClasses;
element.appendChild(this.ulElement_);
element.appendChild(button);
Control.call(this, {
element: element,
render: options.render || render,
target: options.target
});
/**
* A list of currently rendered resolutions.
* @type {Array.<string>}
* Get a list of visible attributions.
* @param {module:ol/PluggableMap~FrameState} frameState Frame state.
* @return {Array.<string>} Attributions.
* @private
*/
this.renderedAttributions_ = [];
getSourceAttributions_(frameState) {
/**
* Used to determine if an attribution already exists.
* @type {!Object.<string, boolean>}
*/
const lookup = {};
/**
* @private
* @type {boolean}
*/
this.renderedVisible_ = true;
/**
* A list of visible attributions.
* @type {Array.<string>}
*/
const visibleAttributions = [];
};
const layerStatesArray = frameState.layerStatesArray;
const resolution = frameState.viewState.resolution;
for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) {
const layerState = layerStatesArray[i];
if (!visibleAtResolution(layerState, resolution)) {
continue;
}
inherits(Attribution, Control);
const source = layerState.layer.getSource();
if (!source) {
continue;
}
const attributionGetter = source.getAttributions();
if (!attributionGetter) {
continue;
}
/**
* Get a list of visible attributions.
* @param {module:ol/PluggableMap~FrameState} frameState Frame state.
* @return {Array.<string>} Attributions.
* @private
*/
Attribution.prototype.getSourceAttributions_ = function(frameState) {
/**
* Used to determine if an attribution already exists.
* @type {!Object.<string, boolean>}
*/
const lookup = {};
const attributions = attributionGetter(frameState);
if (!attributions) {
continue;
}
/**
* A list of visible attributions.
* @type {Array.<string>}
*/
const visibleAttributions = [];
const layerStatesArray = frameState.layerStatesArray;
const resolution = frameState.viewState.resolution;
for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) {
const layerState = layerStatesArray[i];
if (!visibleAtResolution(layerState, resolution)) {
continue;
}
const source = layerState.layer.getSource();
if (!source) {
continue;
}
const attributionGetter = source.getAttributions();
if (!attributionGetter) {
continue;
}
const attributions = attributionGetter(frameState);
if (!attributions) {
continue;
}
if (Array.isArray(attributions)) {
for (let j = 0, jj = attributions.length; j < jj; ++j) {
if (!(attributions[j] in lookup)) {
visibleAttributions.push(attributions[j]);
lookup[attributions[j]] = true;
if (Array.isArray(attributions)) {
for (let j = 0, jj = attributions.length; j < jj; ++j) {
if (!(attributions[j] in lookup)) {
visibleAttributions.push(attributions[j]);
lookup[attributions[j]] = true;
}
}
} else {
if (!(attributions in lookup)) {
visibleAttributions.push(attributions);
lookup[attributions] = true;
}
}
} else {
if (!(attributions in lookup)) {
visibleAttributions.push(attributions);
lookup[attributions] = true;
}
return visibleAttributions;
}
/**
* @private
* @param {?module:ol/PluggableMap~FrameState} frameState Frame state.
*/
updateElement_(frameState) {
if (!frameState) {
if (this.renderedVisible_) {
this.element.style.display = 'none';
this.renderedVisible_ = false;
}
return;
}
const attributions = this.getSourceAttributions_(frameState);
const visible = attributions.length > 0;
if (this.renderedVisible_ != visible) {
this.element.style.display = visible ? '' : 'none';
this.renderedVisible_ = visible;
}
if (equals(attributions, this.renderedAttributions_)) {
return;
}
removeChildren(this.ulElement_);
// append the attributions
for (let i = 0, ii = attributions.length; i < ii; ++i) {
const element = document.createElement('LI');
element.innerHTML = attributions[i];
this.ulElement_.appendChild(element);
}
this.renderedAttributions_ = attributions;
}
/**
* @param {MouseEvent} event The event to handle
* @private
*/
handleClick_(event) {
event.preventDefault();
this.handleToggle_();
}
/**
* @private
*/
handleToggle_() {
this.element.classList.toggle(CLASS_COLLAPSED);
if (this.collapsed_) {
replaceNode(this.collapseLabel_, this.label_);
} else {
replaceNode(this.label_, this.collapseLabel_);
}
this.collapsed_ = !this.collapsed_;
}
/**
* Return `true` if the attribution is collapsible, `false` otherwise.
* @return {boolean} True if the widget is collapsible.
* @api
*/
getCollapsible() {
return this.collapsible_;
}
/**
* Set whether the attribution should be collapsible.
* @param {boolean} collapsible True if the widget is collapsible.
* @api
*/
setCollapsible(collapsible) {
if (this.collapsible_ === collapsible) {
return;
}
this.collapsible_ = collapsible;
this.element.classList.toggle('ol-uncollapsible');
if (!collapsible && this.collapsed_) {
this.handleToggle_();
}
}
return visibleAttributions;
};
/**
* Collapse or expand the attribution according to the passed parameter. Will
* not do anything if the attribution isn't collapsible or if the current
* collapsed state is already the one requested.
* @param {boolean} collapsed True if the widget is collapsed.
* @api
*/
setCollapsed(collapsed) {
if (!this.collapsible_ || this.collapsed_ === collapsed) {
return;
}
this.handleToggle_();
}
/**
* Return `true` when the attribution is currently collapsed or `false`
* otherwise.
* @return {boolean} True if the widget is collapsed.
* @api
*/
getCollapsed() {
return this.collapsed_;
}
}
inherits(Attribution, Control);
/**
@@ -217,117 +326,4 @@ export function render(mapEvent) {
}
/**
* @private
* @param {?module:ol/PluggableMap~FrameState} frameState Frame state.
*/
Attribution.prototype.updateElement_ = function(frameState) {
if (!frameState) {
if (this.renderedVisible_) {
this.element.style.display = 'none';
this.renderedVisible_ = false;
}
return;
}
const attributions = this.getSourceAttributions_(frameState);
const visible = attributions.length > 0;
if (this.renderedVisible_ != visible) {
this.element.style.display = visible ? '' : 'none';
this.renderedVisible_ = visible;
}
if (equals(attributions, this.renderedAttributions_)) {
return;
}
removeChildren(this.ulElement_);
// append the attributions
for (let i = 0, ii = attributions.length; i < ii; ++i) {
const element = document.createElement('LI');
element.innerHTML = attributions[i];
this.ulElement_.appendChild(element);
}
this.renderedAttributions_ = attributions;
};
/**
* @param {MouseEvent} event The event to handle
* @private
*/
Attribution.prototype.handleClick_ = function(event) {
event.preventDefault();
this.handleToggle_();
};
/**
* @private
*/
Attribution.prototype.handleToggle_ = function() {
this.element.classList.toggle(CLASS_COLLAPSED);
if (this.collapsed_) {
replaceNode(this.collapseLabel_, this.label_);
} else {
replaceNode(this.label_, this.collapseLabel_);
}
this.collapsed_ = !this.collapsed_;
};
/**
* Return `true` if the attribution is collapsible, `false` otherwise.
* @return {boolean} True if the widget is collapsible.
* @api
*/
Attribution.prototype.getCollapsible = function() {
return this.collapsible_;
};
/**
* Set whether the attribution should be collapsible.
* @param {boolean} collapsible True if the widget is collapsible.
* @api
*/
Attribution.prototype.setCollapsible = function(collapsible) {
if (this.collapsible_ === collapsible) {
return;
}
this.collapsible_ = collapsible;
this.element.classList.toggle('ol-uncollapsible');
if (!collapsible && this.collapsed_) {
this.handleToggle_();
}
};
/**
* Collapse or expand the attribution according to the passed parameter. Will
* not do anything if the attribution isn't collapsible or if the current
* collapsed state is already the one requested.
* @param {boolean} collapsed True if the widget is collapsed.
* @api
*/
Attribution.prototype.setCollapsed = function(collapsed) {
if (!this.collapsible_ || this.collapsed_ === collapsed) {
return;
}
this.handleToggle_();
};
/**
* Return `true` when the attribution is currently collapsed or `false`
* otherwise.
* @return {boolean} True if the widget is collapsed.
* @api
*/
Attribution.prototype.getCollapsed = function() {
return this.collapsed_;
};
export default Attribution;

View File

@@ -49,108 +49,108 @@ import {listen, unlistenByKey} from '../events.js';
* @param {module:ol/control/Control~Options} options Control options.
* @api
*/
const Control = function(options) {
class Control {
constructor(options) {
BaseObject.call(this);
BaseObject.call(this);
/**
* @protected
* @type {Element}
*/
this.element = options.element ? options.element : null;
/**
* @protected
* @type {Element}
*/
this.element = options.element ? options.element : null;
/**
* @private
* @type {Element}
*/
this.target_ = null;
/**
* @private
* @type {Element}
*/
this.target_ = null;
/**
* @private
* @type {module:ol/PluggableMap}
*/
this.map_ = null;
/**
* @private
* @type {module:ol/PluggableMap}
*/
this.map_ = null;
/**
* @protected
* @type {!Array.<module:ol/events~EventsKey>}
*/
this.listenerKeys = [];
/**
* @protected
* @type {!Array.<module:ol/events~EventsKey>}
*/
this.listenerKeys = [];
/**
* @type {function(module:ol/MapEvent)}
*/
this.render = options.render ? options.render : UNDEFINED;
/**
* @type {function(module:ol/MapEvent)}
*/
this.render = options.render ? options.render : UNDEFINED;
if (options.target) {
this.setTarget(options.target);
}
if (options.target) {
this.setTarget(options.target);
}
};
}
/**
* @inheritDoc
*/
disposeInternal() {
removeNode(this.element);
BaseObject.prototype.disposeInternal.call(this);
}
/**
* Get the map associated with this control.
* @return {module:ol/PluggableMap} Map.
* @api
*/
getMap() {
return this.map_;
}
/**
* Remove the control from its current map and attach it to the new map.
* Subclasses may set up event handlers to get notified about changes to
* the map here.
* @param {module:ol/PluggableMap} map Map.
* @api
*/
setMap(map) {
if (this.map_) {
removeNode(this.element);
}
for (let i = 0, ii = this.listenerKeys.length; i < ii; ++i) {
unlistenByKey(this.listenerKeys[i]);
}
this.listenerKeys.length = 0;
this.map_ = map;
if (this.map_) {
const target = this.target_ ?
this.target_ : map.getOverlayContainerStopEvent();
target.appendChild(this.element);
if (this.render !== UNDEFINED) {
this.listenerKeys.push(listen(map,
MapEventType.POSTRENDER, this.render, this));
}
map.render();
}
}
/**
* This function is used to set a target element for the control. It has no
* effect if it is called after the control has been added to the map (i.e.
* after `setMap` is called on the control). If no `target` is set in the
* options passed to the control constructor and if `setTarget` is not called
* then the control is added to the map's overlay container.
* @param {Element|string} target Target.
* @api
*/
setTarget(target) {
this.target_ = typeof target === 'string' ?
document.getElementById(target) :
target;
}
}
inherits(Control, BaseObject);
/**
* @inheritDoc
*/
Control.prototype.disposeInternal = function() {
removeNode(this.element);
BaseObject.prototype.disposeInternal.call(this);
};
/**
* Get the map associated with this control.
* @return {module:ol/PluggableMap} Map.
* @api
*/
Control.prototype.getMap = function() {
return this.map_;
};
/**
* Remove the control from its current map and attach it to the new map.
* Subclasses may set up event handlers to get notified about changes to
* the map here.
* @param {module:ol/PluggableMap} map Map.
* @api
*/
Control.prototype.setMap = function(map) {
if (this.map_) {
removeNode(this.element);
}
for (let i = 0, ii = this.listenerKeys.length; i < ii; ++i) {
unlistenByKey(this.listenerKeys[i]);
}
this.listenerKeys.length = 0;
this.map_ = map;
if (this.map_) {
const target = this.target_ ?
this.target_ : map.getOverlayContainerStopEvent();
target.appendChild(this.element);
if (this.render !== UNDEFINED) {
this.listenerKeys.push(listen(map,
MapEventType.POSTRENDER, this.render, this));
}
map.render();
}
};
/**
* This function is used to set a target element for the control. It has no
* effect if it is called after the control has been added to the map (i.e.
* after `setMap` is called on the control). If no `target` is set in the
* options passed to the control constructor and if `setTarget` is not called
* then the control is added to the map's overlay container.
* @param {Element|string} target Target.
* @api
*/
Control.prototype.setTarget = function(target) {
this.target_ = typeof target === 'string' ?
document.getElementById(target) :
target;
};
export default Control;

View File

@@ -67,149 +67,148 @@ const getChangeType = (function() {
* @param {module:ol/control/FullScreen~Options=} opt_options Options.
* @api
*/
const FullScreen = function(opt_options) {
class FullScreen {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
/**
* @private
* @type {string}
*/
this.cssClassName_ = options.className !== undefined ? options.className :
'ol-full-screen';
const label = options.label !== undefined ? options.label : '\u2922';
/**
* @private
* @type {Element}
*/
this.labelNode_ = typeof label === 'string' ?
document.createTextNode(label) : label;
const labelActive = options.labelActive !== undefined ? options.labelActive : '\u00d7';
/**
* @private
* @type {Element}
*/
this.labelActiveNode_ = typeof labelActive === 'string' ?
document.createTextNode(labelActive) : labelActive;
const tipLabel = options.tipLabel ? options.tipLabel : 'Toggle full-screen';
const button = document.createElement('button');
button.className = this.cssClassName_ + '-' + isFullScreen();
button.setAttribute('type', 'button');
button.title = tipLabel;
button.appendChild(this.labelNode_);
listen(button, EventType.CLICK,
this.handleClick_, this);
const cssClasses = this.cssClassName_ + ' ' + CLASS_UNSELECTABLE +
' ' + CLASS_CONTROL + ' ' +
(!isFullScreenSupported() ? CLASS_UNSUPPORTED : '');
const element = document.createElement('div');
element.className = cssClasses;
element.appendChild(button);
Control.call(this, {
element: element,
target: options.target
});
/**
* @private
* @type {boolean}
*/
this.keys_ = options.keys !== undefined ? options.keys : false;
/**
* @private
* @type {Element|string|undefined}
*/
this.source_ = options.source;
}
/**
* @param {MouseEvent} event The event to handle
* @private
*/
handleClick_(event) {
event.preventDefault();
this.handleFullScreen_();
}
/**
* @private
* @type {string}
*/
this.cssClassName_ = options.className !== undefined ? options.className :
'ol-full-screen';
handleFullScreen_() {
if (!isFullScreenSupported()) {
return;
}
const map = this.getMap();
if (!map) {
return;
}
if (isFullScreen()) {
exitFullScreen();
} else {
let element;
if (this.source_) {
element = typeof this.source_ === 'string' ?
document.getElementById(this.source_) :
this.source_;
} else {
element = map.getTargetElement();
}
if (this.keys_) {
requestFullScreenWithKeys(element);
const label = options.label !== undefined ? options.label : '\u2922';
} else {
requestFullScreen(element);
}
}
}
/**
* @private
* @type {Element}
*/
this.labelNode_ = typeof label === 'string' ?
document.createTextNode(label) : label;
const labelActive = options.labelActive !== undefined ? options.labelActive : '\u00d7';
handleFullScreenChange_() {
const button = this.element.firstElementChild;
const map = this.getMap();
if (isFullScreen()) {
button.className = this.cssClassName_ + '-true';
replaceNode(this.labelActiveNode_, this.labelNode_);
} else {
button.className = this.cssClassName_ + '-false';
replaceNode(this.labelNode_, this.labelActiveNode_);
}
if (map) {
map.updateSize();
}
}
/**
* @private
* @type {Element}
* @inheritDoc
* @api
*/
this.labelActiveNode_ = typeof labelActive === 'string' ?
document.createTextNode(labelActive) : labelActive;
const tipLabel = options.tipLabel ? options.tipLabel : 'Toggle full-screen';
const button = document.createElement('button');
button.className = this.cssClassName_ + '-' + isFullScreen();
button.setAttribute('type', 'button');
button.title = tipLabel;
button.appendChild(this.labelNode_);
listen(button, EventType.CLICK,
this.handleClick_, this);
const cssClasses = this.cssClassName_ + ' ' + CLASS_UNSELECTABLE +
' ' + CLASS_CONTROL + ' ' +
(!isFullScreenSupported() ? CLASS_UNSUPPORTED : '');
const element = document.createElement('div');
element.className = cssClasses;
element.appendChild(button);
Control.call(this, {
element: element,
target: options.target
});
/**
* @private
* @type {boolean}
*/
this.keys_ = options.keys !== undefined ? options.keys : false;
/**
* @private
* @type {Element|string|undefined}
*/
this.source_ = options.source;
};
setMap(map) {
Control.prototype.setMap.call(this, map);
if (map) {
this.listenerKeys.push(listen(document,
getChangeType(),
this.handleFullScreenChange_, this)
);
}
}
}
inherits(FullScreen, Control);
/**
* @param {MouseEvent} event The event to handle
* @private
*/
FullScreen.prototype.handleClick_ = function(event) {
event.preventDefault();
this.handleFullScreen_();
};
/**
* @private
*/
FullScreen.prototype.handleFullScreen_ = function() {
if (!isFullScreenSupported()) {
return;
}
const map = this.getMap();
if (!map) {
return;
}
if (isFullScreen()) {
exitFullScreen();
} else {
let element;
if (this.source_) {
element = typeof this.source_ === 'string' ?
document.getElementById(this.source_) :
this.source_;
} else {
element = map.getTargetElement();
}
if (this.keys_) {
requestFullScreenWithKeys(element);
} else {
requestFullScreen(element);
}
}
};
/**
* @private
*/
FullScreen.prototype.handleFullScreenChange_ = function() {
const button = this.element.firstElementChild;
const map = this.getMap();
if (isFullScreen()) {
button.className = this.cssClassName_ + '-true';
replaceNode(this.labelActiveNode_, this.labelNode_);
} else {
button.className = this.cssClassName_ + '-false';
replaceNode(this.labelNode_, this.labelActiveNode_);
}
if (map) {
map.updateSize();
}
};
/**
* @inheritDoc
* @api
*/
FullScreen.prototype.setMap = function(map) {
Control.prototype.setMap.call(this, map);
if (map) {
this.listenerKeys.push(listen(document,
getChangeType(),
this.handleFullScreenChange_, this)
);
}
};
/**
* @return {boolean} Fullscreen is supported by the current platform.
*/

View File

@@ -52,67 +52,197 @@ const COORDINATE_FORMAT = 'coordinateFormat';
* options.
* @api
*/
const MousePosition = function(opt_options) {
class MousePosition {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
const element = document.createElement('DIV');
element.className = options.className !== undefined ? options.className : 'ol-mouse-position';
const element = document.createElement('DIV');
element.className = options.className !== undefined ? options.className : 'ol-mouse-position';
Control.call(this, {
element: element,
render: options.render || render,
target: options.target
});
Control.call(this, {
element: element,
render: options.render || render,
target: options.target
});
listen(this,
getChangeEventType(PROJECTION),
this.handleProjectionChanged_, this);
listen(this,
getChangeEventType(PROJECTION),
this.handleProjectionChanged_, this);
if (options.coordinateFormat) {
this.setCoordinateFormat(options.coordinateFormat);
}
if (options.projection) {
this.setProjection(options.projection);
}
/**
* @private
* @type {string}
*/
this.undefinedHTML_ = 'undefinedHTML' in options ? options.undefinedHTML : '&nbsp;';
/**
* @private
* @type {boolean}
*/
this.renderOnMouseOut_ = !!this.undefinedHTML_;
/**
* @private
* @type {string}
*/
this.renderedHTML_ = element.innerHTML;
/**
* @private
* @type {module:ol/proj/Projection}
*/
this.mapProjection_ = null;
/**
* @private
* @type {?module:ol/proj~TransformFunction}
*/
this.transform_ = null;
/**
* @private
* @type {module:ol~Pixel}
*/
this.lastMouseMovePixel_ = null;
if (options.coordinateFormat) {
this.setCoordinateFormat(options.coordinateFormat);
}
if (options.projection) {
this.setProjection(options.projection);
}
/**
* @private
* @type {string}
*/
this.undefinedHTML_ = 'undefinedHTML' in options ? options.undefinedHTML : '&nbsp;';
handleProjectionChanged_() {
this.transform_ = null;
}
/**
* @private
* @type {boolean}
* Return the coordinate format type used to render the current position or
* undefined.
* @return {module:ol/coordinate~CoordinateFormat|undefined} The format to render the current
* position in.
* @observable
* @api
*/
this.renderOnMouseOut_ = !!this.undefinedHTML_;
getCoordinateFormat() {
return (
/** @type {module:ol/coordinate~CoordinateFormat|undefined} */ (this.get(COORDINATE_FORMAT))
);
}
/**
* @private
* @type {string}
* Return the projection that is used to report the mouse position.
* @return {module:ol/proj/Projection|undefined} The projection to report mouse
* position in.
* @observable
* @api
*/
this.renderedHTML_ = element.innerHTML;
getProjection() {
return (
/** @type {module:ol/proj/Projection|undefined} */ (this.get(PROJECTION))
);
}
/**
* @private
* @type {module:ol/proj/Projection}
* @param {Event} event Browser event.
* @protected
*/
this.mapProjection_ = null;
handleMouseMove(event) {
const map = this.getMap();
this.lastMouseMovePixel_ = map.getEventPixel(event);
this.updateHTML_(this.lastMouseMovePixel_);
}
/**
* @private
* @type {?module:ol/proj~TransformFunction}
* @param {Event} event Browser event.
* @protected
*/
this.transform_ = null;
handleMouseOut(event) {
this.updateHTML_(null);
this.lastMouseMovePixel_ = null;
}
/**
* @private
* @type {module:ol~Pixel}
* @inheritDoc
* @api
*/
this.lastMouseMovePixel_ = null;
setMap(map) {
Control.prototype.setMap.call(this, map);
if (map) {
const viewport = map.getViewport();
this.listenerKeys.push(
listen(viewport, EventType.MOUSEMOVE, this.handleMouseMove, this)
);
if (this.renderOnMouseOut_) {
this.listenerKeys.push(
listen(viewport, EventType.MOUSEOUT, this.handleMouseOut, this)
);
}
}
}
};
/**
* Set the coordinate format type used to render the current position.
* @param {module:ol/coordinate~CoordinateFormat} format The format to render the current
* position in.
* @observable
* @api
*/
setCoordinateFormat(format) {
this.set(COORDINATE_FORMAT, format);
}
/**
* Set the projection that is used to report the mouse position.
* @param {module:ol/proj~ProjectionLike} projection The projection to report mouse
* position in.
* @observable
* @api
*/
setProjection(projection) {
this.set(PROJECTION, getProjection(projection));
}
/**
* @param {?module:ol~Pixel} pixel Pixel.
* @private
*/
updateHTML_(pixel) {
let html = this.undefinedHTML_;
if (pixel && this.mapProjection_) {
if (!this.transform_) {
const projection = this.getProjection();
if (projection) {
this.transform_ = getTransformFromProjections(
this.mapProjection_, projection);
} else {
this.transform_ = identityTransform;
}
}
const map = this.getMap();
const coordinate = map.getCoordinateFromPixel(pixel);
if (coordinate) {
this.transform_(coordinate, coordinate);
const coordinateFormat = this.getCoordinateFormat();
if (coordinateFormat) {
html = coordinateFormat(coordinate);
} else {
html = coordinate.toString();
}
}
}
if (!this.renderedHTML_ || html !== this.renderedHTML_) {
this.element.innerHTML = html;
this.renderedHTML_ = html;
}
}
}
inherits(MousePosition, Control);
@@ -137,141 +267,4 @@ export function render(mapEvent) {
}
/**
* @private
*/
MousePosition.prototype.handleProjectionChanged_ = function() {
this.transform_ = null;
};
/**
* Return the coordinate format type used to render the current position or
* undefined.
* @return {module:ol/coordinate~CoordinateFormat|undefined} The format to render the current
* position in.
* @observable
* @api
*/
MousePosition.prototype.getCoordinateFormat = function() {
return (
/** @type {module:ol/coordinate~CoordinateFormat|undefined} */ (this.get(COORDINATE_FORMAT))
);
};
/**
* Return the projection that is used to report the mouse position.
* @return {module:ol/proj/Projection|undefined} The projection to report mouse
* position in.
* @observable
* @api
*/
MousePosition.prototype.getProjection = function() {
return (
/** @type {module:ol/proj/Projection|undefined} */ (this.get(PROJECTION))
);
};
/**
* @param {Event} event Browser event.
* @protected
*/
MousePosition.prototype.handleMouseMove = function(event) {
const map = this.getMap();
this.lastMouseMovePixel_ = map.getEventPixel(event);
this.updateHTML_(this.lastMouseMovePixel_);
};
/**
* @param {Event} event Browser event.
* @protected
*/
MousePosition.prototype.handleMouseOut = function(event) {
this.updateHTML_(null);
this.lastMouseMovePixel_ = null;
};
/**
* @inheritDoc
* @api
*/
MousePosition.prototype.setMap = function(map) {
Control.prototype.setMap.call(this, map);
if (map) {
const viewport = map.getViewport();
this.listenerKeys.push(
listen(viewport, EventType.MOUSEMOVE, this.handleMouseMove, this)
);
if (this.renderOnMouseOut_) {
this.listenerKeys.push(
listen(viewport, EventType.MOUSEOUT, this.handleMouseOut, this)
);
}
}
};
/**
* Set the coordinate format type used to render the current position.
* @param {module:ol/coordinate~CoordinateFormat} format The format to render the current
* position in.
* @observable
* @api
*/
MousePosition.prototype.setCoordinateFormat = function(format) {
this.set(COORDINATE_FORMAT, format);
};
/**
* Set the projection that is used to report the mouse position.
* @param {module:ol/proj~ProjectionLike} projection The projection to report mouse
* position in.
* @observable
* @api
*/
MousePosition.prototype.setProjection = function(projection) {
this.set(PROJECTION, getProjection(projection));
};
/**
* @param {?module:ol~Pixel} pixel Pixel.
* @private
*/
MousePosition.prototype.updateHTML_ = function(pixel) {
let html = this.undefinedHTML_;
if (pixel && this.mapProjection_) {
if (!this.transform_) {
const projection = this.getProjection();
if (projection) {
this.transform_ = getTransformFromProjections(
this.mapProjection_, projection);
} else {
this.transform_ = identityTransform;
}
}
const map = this.getMap();
const coordinate = map.getCoordinateFromPixel(pixel);
if (coordinate) {
this.transform_(coordinate, coordinate);
const coordinateFormat = this.getCoordinateFormat();
if (coordinateFormat) {
html = coordinateFormat(coordinate);
} else {
html = coordinate.toString();
}
}
}
if (!this.renderedHTML_ || html !== this.renderedHTML_) {
this.element.innerHTML = html;
this.renderedHTML_ = html;
}
};
export default MousePosition;

View File

@@ -66,258 +66,506 @@ const MIN_RATIO = 0.1;
* @param {module:ol/control/OverviewMap~Options=} opt_options OverviewMap options.
* @api
*/
const OverviewMap = function(opt_options) {
class OverviewMap {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
/**
* @type {boolean}
* @private
*/
this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true;
/**
* @type {boolean}
* @private
*/
this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true;
/**
* @private
* @type {boolean}
*/
this.collapsible_ = options.collapsible !== undefined ?
options.collapsible : true;
if (!this.collapsible_) {
this.collapsed_ = false;
}
const className = options.className !== undefined ? options.className : 'ol-overviewmap';
const tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Overview map';
const collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\u00AB';
if (typeof collapseLabel === 'string') {
/**
* @private
* @type {Element}
* @type {boolean}
*/
this.collapseLabel_ = document.createElement('span');
this.collapseLabel_.textContent = collapseLabel;
} else {
this.collapseLabel_ = collapseLabel;
}
this.collapsible_ = options.collapsible !== undefined ?
options.collapsible : true;
const label = options.label !== undefined ? options.label : '\u00BB';
if (!this.collapsible_) {
this.collapsed_ = false;
}
const className = options.className !== undefined ? options.className : 'ol-overviewmap';
if (typeof label === 'string') {
/**
* @private
* @type {Element}
*/
this.label_ = document.createElement('span');
this.label_.textContent = label;
} else {
this.label_ = label;
}
const tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Overview map';
const activeLabel = (this.collapsible_ && !this.collapsed_) ?
this.collapseLabel_ : this.label_;
const button = document.createElement('button');
button.setAttribute('type', 'button');
button.title = tipLabel;
button.appendChild(activeLabel);
const collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\u00AB';
listen(button, EventType.CLICK,
this.handleClick_, this);
/**
* @type {Element}
* @private
*/
this.ovmapDiv_ = document.createElement('DIV');
this.ovmapDiv_.className = 'ol-overviewmap-map';
/**
* @type {module:ol/Map}
* @private
*/
this.ovmap_ = new Map({
controls: new Collection(),
interactions: new Collection(),
view: options.view
});
const ovmap = this.ovmap_;
if (options.layers) {
options.layers.forEach(
if (typeof collapseLabel === 'string') {
/**
* @param {module:ol/layer/Layer} layer Layer.
* @private
* @type {Element}
*/
(function(layer) {
ovmap.addLayer(layer);
}).bind(this));
}
this.collapseLabel_ = document.createElement('span');
this.collapseLabel_.textContent = collapseLabel;
} else {
this.collapseLabel_ = collapseLabel;
}
const box = document.createElement('DIV');
box.className = 'ol-overviewmap-box';
box.style.boxSizing = 'border-box';
const label = options.label !== undefined ? options.label : '\u00BB';
if (typeof label === 'string') {
/**
* @private
* @type {Element}
*/
this.label_ = document.createElement('span');
this.label_.textContent = label;
} else {
this.label_ = label;
}
const activeLabel = (this.collapsible_ && !this.collapsed_) ?
this.collapseLabel_ : this.label_;
const button = document.createElement('button');
button.setAttribute('type', 'button');
button.title = tipLabel;
button.appendChild(activeLabel);
listen(button, EventType.CLICK,
this.handleClick_, this);
/**
* @type {Element}
* @private
*/
this.ovmapDiv_ = document.createElement('DIV');
this.ovmapDiv_.className = 'ol-overviewmap-map';
/**
* @type {module:ol/Map}
* @private
*/
this.ovmap_ = new Map({
controls: new Collection(),
interactions: new Collection(),
view: options.view
});
const ovmap = this.ovmap_;
if (options.layers) {
options.layers.forEach(
/**
* @param {module:ol/layer/Layer} layer Layer.
*/
(function(layer) {
ovmap.addLayer(layer);
}).bind(this));
}
const box = document.createElement('DIV');
box.className = 'ol-overviewmap-box';
box.style.boxSizing = 'border-box';
/**
* @type {module:ol/Overlay}
* @private
*/
this.boxOverlay_ = new Overlay({
position: [0, 0],
positioning: OverlayPositioning.BOTTOM_LEFT,
element: box
});
this.ovmap_.addOverlay(this.boxOverlay_);
const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL +
(this.collapsed_ && this.collapsible_ ? ' ' + CLASS_COLLAPSED : '') +
(this.collapsible_ ? '' : ' ol-uncollapsible');
const element = document.createElement('div');
element.className = cssClasses;
element.appendChild(this.ovmapDiv_);
element.appendChild(button);
Control.call(this, {
element: element,
render: options.render || render,
target: options.target
});
/* Interactive map */
const scope = this;
const overlay = this.boxOverlay_;
const overlayBox = this.boxOverlay_.getElement();
/* Functions definition */
const computeDesiredMousePosition = function(mousePosition) {
return {
clientX: mousePosition.clientX - (overlayBox.offsetWidth / 2),
clientY: mousePosition.clientY + (overlayBox.offsetHeight / 2)
};
};
const move = function(event) {
const coordinates = ovmap.getEventCoordinate(computeDesiredMousePosition(event));
overlay.setPosition(coordinates);
};
const endMoving = function(event) {
const coordinates = ovmap.getEventCoordinate(event);
scope.getMap().getView().setCenter(coordinates);
window.removeEventListener('mousemove', move);
window.removeEventListener('mouseup', endMoving);
};
/* Binding */
overlayBox.addEventListener('mousedown', function() {
window.addEventListener('mousemove', move);
window.addEventListener('mouseup', endMoving);
});
}
/**
* @type {module:ol/Overlay}
* @private
* @inheritDoc
* @api
*/
this.boxOverlay_ = new Overlay({
position: [0, 0],
positioning: OverlayPositioning.BOTTOM_LEFT,
element: box
});
this.ovmap_.addOverlay(this.boxOverlay_);
const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL +
(this.collapsed_ && this.collapsible_ ? ' ' + CLASS_COLLAPSED : '') +
(this.collapsible_ ? '' : ' ol-uncollapsible');
const element = document.createElement('div');
element.className = cssClasses;
element.appendChild(this.ovmapDiv_);
element.appendChild(button);
Control.call(this, {
element: element,
render: options.render || render,
target: options.target
});
/* Interactive map */
const scope = this;
const overlay = this.boxOverlay_;
const overlayBox = this.boxOverlay_.getElement();
/* Functions definition */
const computeDesiredMousePosition = function(mousePosition) {
return {
clientX: mousePosition.clientX - (overlayBox.offsetWidth / 2),
clientY: mousePosition.clientY + (overlayBox.offsetHeight / 2)
};
};
const move = function(event) {
const coordinates = ovmap.getEventCoordinate(computeDesiredMousePosition(event));
overlay.setPosition(coordinates);
};
const endMoving = function(event) {
const coordinates = ovmap.getEventCoordinate(event);
scope.getMap().getView().setCenter(coordinates);
window.removeEventListener('mousemove', move);
window.removeEventListener('mouseup', endMoving);
};
/* Binding */
overlayBox.addEventListener('mousedown', function() {
window.addEventListener('mousemove', move);
window.addEventListener('mouseup', endMoving);
});
};
inherits(OverviewMap, Control);
/**
* @inheritDoc
* @api
*/
OverviewMap.prototype.setMap = function(map) {
const oldMap = this.getMap();
if (map === oldMap) {
return;
}
if (oldMap) {
const oldView = oldMap.getView();
if (oldView) {
this.unbindView_(oldView);
setMap(map) {
const oldMap = this.getMap();
if (map === oldMap) {
return;
}
this.ovmap_.setTarget(null);
}
Control.prototype.setMap.call(this, map);
if (map) {
this.ovmap_.setTarget(this.ovmapDiv_);
this.listenerKeys.push(listen(
map, ObjectEventType.PROPERTYCHANGE,
this.handleMapPropertyChange_, this));
// TODO: to really support map switching, this would need to be reworked
if (this.ovmap_.getLayers().getLength() === 0) {
this.ovmap_.setLayerGroup(map.getLayerGroup());
if (oldMap) {
const oldView = oldMap.getView();
if (oldView) {
this.unbindView_(oldView);
}
this.ovmap_.setTarget(null);
}
Control.prototype.setMap.call(this, map);
const view = map.getView();
if (view) {
this.bindView_(view);
if (view.isDef()) {
this.ovmap_.updateSize();
this.resetExtent_();
if (map) {
this.ovmap_.setTarget(this.ovmapDiv_);
this.listenerKeys.push(listen(
map, ObjectEventType.PROPERTYCHANGE,
this.handleMapPropertyChange_, this));
// TODO: to really support map switching, this would need to be reworked
if (this.ovmap_.getLayers().getLength() === 0) {
this.ovmap_.setLayerGroup(map.getLayerGroup());
}
const view = map.getView();
if (view) {
this.bindView_(view);
if (view.isDef()) {
this.ovmap_.updateSize();
this.resetExtent_();
}
}
}
}
};
/**
* Handle map property changes. This only deals with changes to the map's view.
* @param {module:ol/Object~ObjectEvent} event The propertychange event.
* @private
*/
OverviewMap.prototype.handleMapPropertyChange_ = function(event) {
if (event.key === MapProperty.VIEW) {
const oldView = /** @type {module:ol/View} */ (event.oldValue);
if (oldView) {
this.unbindView_(oldView);
/**
* Handle map property changes. This only deals with changes to the map's view.
* @param {module:ol/Object~ObjectEvent} event The propertychange event.
* @private
*/
handleMapPropertyChange_(event) {
if (event.key === MapProperty.VIEW) {
const oldView = /** @type {module:ol/View} */ (event.oldValue);
if (oldView) {
this.unbindView_(oldView);
}
const newView = this.getMap().getView();
this.bindView_(newView);
}
const newView = this.getMap().getView();
this.bindView_(newView);
}
};
/**
* Register listeners for view property changes.
* @param {module:ol/View} view The view.
* @private
*/
bindView_(view) {
listen(view,
getChangeEventType(ViewProperty.ROTATION),
this.handleRotationChanged_, this);
}
/**
* Register listeners for view property changes.
* @param {module:ol/View} view The view.
* @private
*/
OverviewMap.prototype.bindView_ = function(view) {
listen(view,
getChangeEventType(ViewProperty.ROTATION),
this.handleRotationChanged_, this);
};
/**
* Unregister listeners for view property changes.
* @param {module:ol/View} view The view.
* @private
*/
unbindView_(view) {
unlisten(view,
getChangeEventType(ViewProperty.ROTATION),
this.handleRotationChanged_, this);
}
/**
* Handle rotation changes to the main map.
* TODO: This should rotate the extent rectrangle instead of the
* overview map's view.
* @private
*/
handleRotationChanged_() {
this.ovmap_.getView().setRotation(this.getMap().getView().getRotation());
}
/**
* Unregister listeners for view property changes.
* @param {module:ol/View} view The view.
* @private
*/
OverviewMap.prototype.unbindView_ = function(view) {
unlisten(view,
getChangeEventType(ViewProperty.ROTATION),
this.handleRotationChanged_, this);
};
/**
* Reset the overview map extent if the box size (width or
* height) is less than the size of the overview map size times minRatio
* or is greater than the size of the overview size times maxRatio.
*
* If the map extent was not reset, the box size can fits in the defined
* ratio sizes. This method then checks if is contained inside the overview
* map current extent. If not, recenter the overview map to the current
* main map center location.
* @private
*/
validateExtent_() {
const map = this.getMap();
const ovmap = this.ovmap_;
if (!map.isRendered() || !ovmap.isRendered()) {
return;
}
/**
* Handle rotation changes to the main map.
* TODO: This should rotate the extent rectrangle instead of the
* overview map's view.
* @private
*/
OverviewMap.prototype.handleRotationChanged_ = function() {
this.ovmap_.getView().setRotation(this.getMap().getView().getRotation());
};
const mapSize = /** @type {module:ol/size~Size} */ (map.getSize());
const view = map.getView();
const extent = view.calculateExtent(mapSize);
const ovmapSize = /** @type {module:ol/size~Size} */ (ovmap.getSize());
const ovview = ovmap.getView();
const ovextent = ovview.calculateExtent(ovmapSize);
const topLeftPixel =
ovmap.getPixelFromCoordinate(getTopLeft(extent));
const bottomRightPixel =
ovmap.getPixelFromCoordinate(getBottomRight(extent));
const boxWidth = Math.abs(topLeftPixel[0] - bottomRightPixel[0]);
const boxHeight = Math.abs(topLeftPixel[1] - bottomRightPixel[1]);
const ovmapWidth = ovmapSize[0];
const ovmapHeight = ovmapSize[1];
if (boxWidth < ovmapWidth * MIN_RATIO ||
boxHeight < ovmapHeight * MIN_RATIO ||
boxWidth > ovmapWidth * MAX_RATIO ||
boxHeight > ovmapHeight * MAX_RATIO) {
this.resetExtent_();
} else if (!containsExtent(ovextent, extent)) {
this.recenter_();
}
}
/**
* Reset the overview map extent to half calculated min and max ratio times
* the extent of the main map.
* @private
*/
resetExtent_() {
if (MAX_RATIO === 0 || MIN_RATIO === 0) {
return;
}
const map = this.getMap();
const ovmap = this.ovmap_;
const mapSize = /** @type {module:ol/size~Size} */ (map.getSize());
const view = map.getView();
const extent = view.calculateExtent(mapSize);
const ovview = ovmap.getView();
// get how many times the current map overview could hold different
// box sizes using the min and max ratio, pick the step in the middle used
// to calculate the extent from the main map to set it to the overview map,
const steps = Math.log(
MAX_RATIO / MIN_RATIO) / Math.LN2;
const ratio = 1 / (Math.pow(2, steps / 2) * MIN_RATIO);
scaleFromCenter(extent, ratio);
ovview.fit(extent);
}
/**
* Set the center of the overview map to the map center without changing its
* resolution.
* @private
*/
recenter_() {
const map = this.getMap();
const ovmap = this.ovmap_;
const view = map.getView();
const ovview = ovmap.getView();
ovview.setCenter(view.getCenter());
}
/**
* Update the box using the main map extent
* @private
*/
updateBox_() {
const map = this.getMap();
const ovmap = this.ovmap_;
if (!map.isRendered() || !ovmap.isRendered()) {
return;
}
const mapSize = /** @type {module:ol/size~Size} */ (map.getSize());
const view = map.getView();
const ovview = ovmap.getView();
const rotation = view.getRotation();
const overlay = this.boxOverlay_;
const box = this.boxOverlay_.getElement();
const extent = view.calculateExtent(mapSize);
const ovresolution = ovview.getResolution();
const bottomLeft = getBottomLeft(extent);
const topRight = getTopRight(extent);
// set position using bottom left coordinates
const rotateBottomLeft = this.calculateCoordinateRotate_(rotation, bottomLeft);
overlay.setPosition(rotateBottomLeft);
// set box size calculated from map extent size and overview map resolution
if (box) {
box.style.width = Math.abs((bottomLeft[0] - topRight[0]) / ovresolution) + 'px';
box.style.height = Math.abs((topRight[1] - bottomLeft[1]) / ovresolution) + 'px';
}
}
/**
* @param {number} rotation Target rotation.
* @param {module:ol/coordinate~Coordinate} coordinate Coordinate.
* @return {module:ol/coordinate~Coordinate|undefined} Coordinate for rotation and center anchor.
* @private
*/
calculateCoordinateRotate_(rotation, coordinate) {
let coordinateRotate;
const map = this.getMap();
const view = map.getView();
const currentCenter = view.getCenter();
if (currentCenter) {
coordinateRotate = [
coordinate[0] - currentCenter[0],
coordinate[1] - currentCenter[1]
];
rotateCoordinate(coordinateRotate, rotation);
addCoordinate(coordinateRotate, currentCenter);
}
return coordinateRotate;
}
/**
* @param {MouseEvent} event The event to handle
* @private
*/
handleClick_(event) {
event.preventDefault();
this.handleToggle_();
}
/**
* @private
*/
handleToggle_() {
this.element.classList.toggle(CLASS_COLLAPSED);
if (this.collapsed_) {
replaceNode(this.collapseLabel_, this.label_);
} else {
replaceNode(this.label_, this.collapseLabel_);
}
this.collapsed_ = !this.collapsed_;
// manage overview map if it had not been rendered before and control
// is expanded
const ovmap = this.ovmap_;
if (!this.collapsed_ && !ovmap.isRendered()) {
ovmap.updateSize();
this.resetExtent_();
listenOnce(ovmap, MapEventType.POSTRENDER,
function(event) {
this.updateBox_();
},
this);
}
}
/**
* Return `true` if the overview map is collapsible, `false` otherwise.
* @return {boolean} True if the widget is collapsible.
* @api
*/
getCollapsible() {
return this.collapsible_;
}
/**
* Set whether the overview map should be collapsible.
* @param {boolean} collapsible True if the widget is collapsible.
* @api
*/
setCollapsible(collapsible) {
if (this.collapsible_ === collapsible) {
return;
}
this.collapsible_ = collapsible;
this.element.classList.toggle('ol-uncollapsible');
if (!collapsible && this.collapsed_) {
this.handleToggle_();
}
}
/**
* Collapse or expand the overview map according to the passed parameter. Will
* not do anything if the overview map isn't collapsible or if the current
* collapsed state is already the one requested.
* @param {boolean} collapsed True if the widget is collapsed.
* @api
*/
setCollapsed(collapsed) {
if (!this.collapsible_ || this.collapsed_ === collapsed) {
return;
}
this.handleToggle_();
}
/**
* Determine if the overview map is collapsed.
* @return {boolean} The overview map is collapsed.
* @api
*/
getCollapsed() {
return this.collapsed_;
}
/**
* Return the overview map.
* @return {module:ol/PluggableMap} Overview map.
* @api
*/
getOverviewMap() {
return this.ovmap_;
}
}
inherits(OverviewMap, Control);
/**
@@ -332,266 +580,4 @@ export function render(mapEvent) {
}
/**
* Reset the overview map extent if the box size (width or
* height) is less than the size of the overview map size times minRatio
* or is greater than the size of the overview size times maxRatio.
*
* If the map extent was not reset, the box size can fits in the defined
* ratio sizes. This method then checks if is contained inside the overview
* map current extent. If not, recenter the overview map to the current
* main map center location.
* @private
*/
OverviewMap.prototype.validateExtent_ = function() {
const map = this.getMap();
const ovmap = this.ovmap_;
if (!map.isRendered() || !ovmap.isRendered()) {
return;
}
const mapSize = /** @type {module:ol/size~Size} */ (map.getSize());
const view = map.getView();
const extent = view.calculateExtent(mapSize);
const ovmapSize = /** @type {module:ol/size~Size} */ (ovmap.getSize());
const ovview = ovmap.getView();
const ovextent = ovview.calculateExtent(ovmapSize);
const topLeftPixel =
ovmap.getPixelFromCoordinate(getTopLeft(extent));
const bottomRightPixel =
ovmap.getPixelFromCoordinate(getBottomRight(extent));
const boxWidth = Math.abs(topLeftPixel[0] - bottomRightPixel[0]);
const boxHeight = Math.abs(topLeftPixel[1] - bottomRightPixel[1]);
const ovmapWidth = ovmapSize[0];
const ovmapHeight = ovmapSize[1];
if (boxWidth < ovmapWidth * MIN_RATIO ||
boxHeight < ovmapHeight * MIN_RATIO ||
boxWidth > ovmapWidth * MAX_RATIO ||
boxHeight > ovmapHeight * MAX_RATIO) {
this.resetExtent_();
} else if (!containsExtent(ovextent, extent)) {
this.recenter_();
}
};
/**
* Reset the overview map extent to half calculated min and max ratio times
* the extent of the main map.
* @private
*/
OverviewMap.prototype.resetExtent_ = function() {
if (MAX_RATIO === 0 || MIN_RATIO === 0) {
return;
}
const map = this.getMap();
const ovmap = this.ovmap_;
const mapSize = /** @type {module:ol/size~Size} */ (map.getSize());
const view = map.getView();
const extent = view.calculateExtent(mapSize);
const ovview = ovmap.getView();
// get how many times the current map overview could hold different
// box sizes using the min and max ratio, pick the step in the middle used
// to calculate the extent from the main map to set it to the overview map,
const steps = Math.log(
MAX_RATIO / MIN_RATIO) / Math.LN2;
const ratio = 1 / (Math.pow(2, steps / 2) * MIN_RATIO);
scaleFromCenter(extent, ratio);
ovview.fit(extent);
};
/**
* Set the center of the overview map to the map center without changing its
* resolution.
* @private
*/
OverviewMap.prototype.recenter_ = function() {
const map = this.getMap();
const ovmap = this.ovmap_;
const view = map.getView();
const ovview = ovmap.getView();
ovview.setCenter(view.getCenter());
};
/**
* Update the box using the main map extent
* @private
*/
OverviewMap.prototype.updateBox_ = function() {
const map = this.getMap();
const ovmap = this.ovmap_;
if (!map.isRendered() || !ovmap.isRendered()) {
return;
}
const mapSize = /** @type {module:ol/size~Size} */ (map.getSize());
const view = map.getView();
const ovview = ovmap.getView();
const rotation = view.getRotation();
const overlay = this.boxOverlay_;
const box = this.boxOverlay_.getElement();
const extent = view.calculateExtent(mapSize);
const ovresolution = ovview.getResolution();
const bottomLeft = getBottomLeft(extent);
const topRight = getTopRight(extent);
// set position using bottom left coordinates
const rotateBottomLeft = this.calculateCoordinateRotate_(rotation, bottomLeft);
overlay.setPosition(rotateBottomLeft);
// set box size calculated from map extent size and overview map resolution
if (box) {
box.style.width = Math.abs((bottomLeft[0] - topRight[0]) / ovresolution) + 'px';
box.style.height = Math.abs((topRight[1] - bottomLeft[1]) / ovresolution) + 'px';
}
};
/**
* @param {number} rotation Target rotation.
* @param {module:ol/coordinate~Coordinate} coordinate Coordinate.
* @return {module:ol/coordinate~Coordinate|undefined} Coordinate for rotation and center anchor.
* @private
*/
OverviewMap.prototype.calculateCoordinateRotate_ = function(
rotation, coordinate) {
let coordinateRotate;
const map = this.getMap();
const view = map.getView();
const currentCenter = view.getCenter();
if (currentCenter) {
coordinateRotate = [
coordinate[0] - currentCenter[0],
coordinate[1] - currentCenter[1]
];
rotateCoordinate(coordinateRotate, rotation);
addCoordinate(coordinateRotate, currentCenter);
}
return coordinateRotate;
};
/**
* @param {MouseEvent} event The event to handle
* @private
*/
OverviewMap.prototype.handleClick_ = function(event) {
event.preventDefault();
this.handleToggle_();
};
/**
* @private
*/
OverviewMap.prototype.handleToggle_ = function() {
this.element.classList.toggle(CLASS_COLLAPSED);
if (this.collapsed_) {
replaceNode(this.collapseLabel_, this.label_);
} else {
replaceNode(this.label_, this.collapseLabel_);
}
this.collapsed_ = !this.collapsed_;
// manage overview map if it had not been rendered before and control
// is expanded
const ovmap = this.ovmap_;
if (!this.collapsed_ && !ovmap.isRendered()) {
ovmap.updateSize();
this.resetExtent_();
listenOnce(ovmap, MapEventType.POSTRENDER,
function(event) {
this.updateBox_();
},
this);
}
};
/**
* Return `true` if the overview map is collapsible, `false` otherwise.
* @return {boolean} True if the widget is collapsible.
* @api
*/
OverviewMap.prototype.getCollapsible = function() {
return this.collapsible_;
};
/**
* Set whether the overview map should be collapsible.
* @param {boolean} collapsible True if the widget is collapsible.
* @api
*/
OverviewMap.prototype.setCollapsible = function(collapsible) {
if (this.collapsible_ === collapsible) {
return;
}
this.collapsible_ = collapsible;
this.element.classList.toggle('ol-uncollapsible');
if (!collapsible && this.collapsed_) {
this.handleToggle_();
}
};
/**
* Collapse or expand the overview map according to the passed parameter. Will
* not do anything if the overview map isn't collapsible or if the current
* collapsed state is already the one requested.
* @param {boolean} collapsed True if the widget is collapsed.
* @api
*/
OverviewMap.prototype.setCollapsed = function(collapsed) {
if (!this.collapsible_ || this.collapsed_ === collapsed) {
return;
}
this.handleToggle_();
};
/**
* Determine if the overview map is collapsed.
* @return {boolean} The overview map is collapsed.
* @api
*/
OverviewMap.prototype.getCollapsed = function() {
return this.collapsed_;
};
/**
* Return the overview map.
* @return {module:ol/PluggableMap} Overview map.
* @api
*/
OverviewMap.prototype.getOverviewMap = function() {
return this.ovmap_;
};
export default OverviewMap;

View File

@@ -38,117 +38,117 @@ import {inherits} from '../util.js';
* @param {module:ol/control/Rotate~Options=} opt_options Rotate options.
* @api
*/
const Rotate = function(opt_options) {
class Rotate {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
const className = options.className !== undefined ? options.className : 'ol-rotate';
const className = options.className !== undefined ? options.className : 'ol-rotate';
const label = options.label !== undefined ? options.label : '\u21E7';
const label = options.label !== undefined ? options.label : '\u21E7';
/**
* @type {Element}
* @private
*/
this.label_ = null;
/**
* @type {Element}
* @private
*/
this.label_ = null;
if (typeof label === 'string') {
this.label_ = document.createElement('span');
this.label_.className = 'ol-compass';
this.label_.textContent = label;
} else {
this.label_ = label;
this.label_.classList.add('ol-compass');
}
const tipLabel = options.tipLabel ? options.tipLabel : 'Reset rotation';
const button = document.createElement('button');
button.className = className + '-reset';
button.setAttribute('type', 'button');
button.title = tipLabel;
button.appendChild(this.label_);
listen(button, EventType.CLICK,
Rotate.prototype.handleClick_, this);
const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL;
const element = document.createElement('div');
element.className = cssClasses;
element.appendChild(button);
this.callResetNorth_ = options.resetNorth ? options.resetNorth : undefined;
Control.call(this, {
element: element,
render: options.render || render,
target: options.target
});
/**
* @type {number}
* @private
*/
this.duration_ = options.duration !== undefined ? options.duration : 250;
/**
* @type {boolean}
* @private
*/
this.autoHide_ = options.autoHide !== undefined ? options.autoHide : true;
/**
* @private
* @type {number|undefined}
*/
this.rotation_ = undefined;
if (this.autoHide_) {
this.element.classList.add(CLASS_HIDDEN);
}
};
inherits(Rotate, Control);
/**
* @param {MouseEvent} event The event to handle
* @private
*/
Rotate.prototype.handleClick_ = function(event) {
event.preventDefault();
if (this.callResetNorth_ !== undefined) {
this.callResetNorth_();
} else {
this.resetNorth_();
}
};
/**
* @private
*/
Rotate.prototype.resetNorth_ = function() {
const map = this.getMap();
const view = map.getView();
if (!view) {
// the map does not have a view, so we can't act
// upon it
return;
}
if (view.getRotation() !== undefined) {
if (this.duration_ > 0) {
view.animate({
rotation: 0,
duration: this.duration_,
easing: easeOut
});
if (typeof label === 'string') {
this.label_ = document.createElement('span');
this.label_.className = 'ol-compass';
this.label_.textContent = label;
} else {
view.setRotation(0);
this.label_ = label;
this.label_.classList.add('ol-compass');
}
const tipLabel = options.tipLabel ? options.tipLabel : 'Reset rotation';
const button = document.createElement('button');
button.className = className + '-reset';
button.setAttribute('type', 'button');
button.title = tipLabel;
button.appendChild(this.label_);
listen(button, EventType.CLICK,
Rotate.prototype.handleClick_, this);
const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL;
const element = document.createElement('div');
element.className = cssClasses;
element.appendChild(button);
this.callResetNorth_ = options.resetNorth ? options.resetNorth : undefined;
Control.call(this, {
element: element,
render: options.render || render,
target: options.target
});
/**
* @type {number}
* @private
*/
this.duration_ = options.duration !== undefined ? options.duration : 250;
/**
* @type {boolean}
* @private
*/
this.autoHide_ = options.autoHide !== undefined ? options.autoHide : true;
/**
* @private
* @type {number|undefined}
*/
this.rotation_ = undefined;
if (this.autoHide_) {
this.element.classList.add(CLASS_HIDDEN);
}
}
/**
* @param {MouseEvent} event The event to handle
* @private
*/
handleClick_(event) {
event.preventDefault();
if (this.callResetNorth_ !== undefined) {
this.callResetNorth_();
} else {
this.resetNorth_();
}
}
};
/**
* @private
*/
resetNorth_() {
const map = this.getMap();
const view = map.getView();
if (!view) {
// the map does not have a view, so we can't act
// upon it
return;
}
if (view.getRotation() !== undefined) {
if (this.duration_ > 0) {
view.animate({
rotation: 0,
duration: this.duration_,
easing: easeOut
});
} else {
view.setRotation(0);
}
}
}
}
inherits(Rotate, Control);
/**

View File

@@ -64,89 +64,229 @@ const LEADING_DIGITS = [1, 2, 5];
* @param {module:ol/control/ScaleLine~Options=} opt_options Scale line options.
* @api
*/
const ScaleLine = function(opt_options) {
class ScaleLine {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
const className = options.className !== undefined ? options.className : 'ol-scale-line';
const className = options.className !== undefined ? options.className : 'ol-scale-line';
/**
* @private
* @type {HTMLElement}
*/
this.innerElement_ = document.createElement('DIV');
this.innerElement_.className = className + '-inner';
/**
* @private
* @type {HTMLElement}
*/
this.element_ = document.createElement('DIV');
this.element_.className = className + ' ' + CLASS_UNSELECTABLE;
this.element_.appendChild(this.innerElement_);
/**
* @private
* @type {?module:ol/View~State}
*/
this.viewState_ = null;
/**
* @private
* @type {number}
*/
this.minWidth_ = options.minWidth !== undefined ? options.minWidth : 64;
/**
* @private
* @type {boolean}
*/
this.renderedVisible_ = false;
/**
* @private
* @type {number|undefined}
*/
this.renderedWidth_ = undefined;
/**
* @private
* @type {string}
*/
this.renderedHTML_ = '';
Control.call(this, {
element: this.element_,
render: options.render || render,
target: options.target
});
listen(
this, getChangeEventType(UNITS_PROP),
this.handleUnitsChanged_, this);
this.setUnits(/** @type {module:ol/control/ScaleLine~Units} */ (options.units) ||
Units.METRIC);
}
/**
* Return the units to use in the scale line.
* @return {module:ol/control/ScaleLine~Units|undefined} The units
* to use in the scale line.
* @observable
* @api
*/
getUnits() {
return (
/** @type {module:ol/control/ScaleLine~Units|undefined} */ (this.get(UNITS_PROP))
);
}
/**
* @private
* @type {HTMLElement}
*/
this.innerElement_ = document.createElement('DIV');
this.innerElement_.className = className + '-inner';
handleUnitsChanged_() {
this.updateElement_();
}
/**
* Set the units to use in the scale line.
* @param {module:ol/control/ScaleLine~Units} units The units to use in the scale line.
* @observable
* @api
*/
setUnits(units) {
this.set(UNITS_PROP, units);
}
/**
* @private
* @type {HTMLElement}
*/
this.element_ = document.createElement('DIV');
this.element_.className = className + ' ' + CLASS_UNSELECTABLE;
this.element_.appendChild(this.innerElement_);
updateElement_() {
const viewState = this.viewState_;
/**
* @private
* @type {?module:ol/View~State}
*/
this.viewState_ = null;
if (!viewState) {
if (this.renderedVisible_) {
this.element_.style.display = 'none';
this.renderedVisible_ = false;
}
return;
}
/**
* @private
* @type {number}
*/
this.minWidth_ = options.minWidth !== undefined ? options.minWidth : 64;
const center = viewState.center;
const projection = viewState.projection;
const units = this.getUnits();
const pointResolutionUnits = units == Units.DEGREES ?
ProjUnits.DEGREES :
ProjUnits.METERS;
let pointResolution =
getPointResolution(projection, viewState.resolution, center, pointResolutionUnits);
if (projection.getUnits() != ProjUnits.DEGREES && projection.getMetersPerUnit()
&& pointResolutionUnits == ProjUnits.METERS) {
pointResolution *= projection.getMetersPerUnit();
}
/**
* @private
* @type {boolean}
*/
this.renderedVisible_ = false;
let nominalCount = this.minWidth_ * pointResolution;
let suffix = '';
if (units == Units.DEGREES) {
const metersPerDegree = METERS_PER_UNIT[ProjUnits.DEGREES];
if (projection.getUnits() == ProjUnits.DEGREES) {
nominalCount *= metersPerDegree;
} else {
pointResolution /= metersPerDegree;
}
if (nominalCount < metersPerDegree / 60) {
suffix = '\u2033'; // seconds
pointResolution *= 3600;
} else if (nominalCount < metersPerDegree) {
suffix = '\u2032'; // minutes
pointResolution *= 60;
} else {
suffix = '\u00b0'; // degrees
}
} else if (units == Units.IMPERIAL) {
if (nominalCount < 0.9144) {
suffix = 'in';
pointResolution /= 0.0254;
} else if (nominalCount < 1609.344) {
suffix = 'ft';
pointResolution /= 0.3048;
} else {
suffix = 'mi';
pointResolution /= 1609.344;
}
} else if (units == Units.NAUTICAL) {
pointResolution /= 1852;
suffix = 'nm';
} else if (units == Units.METRIC) {
if (nominalCount < 0.001) {
suffix = 'μm';
pointResolution *= 1000000;
} else if (nominalCount < 1) {
suffix = 'mm';
pointResolution *= 1000;
} else if (nominalCount < 1000) {
suffix = 'm';
} else {
suffix = 'km';
pointResolution /= 1000;
}
} else if (units == Units.US) {
if (nominalCount < 0.9144) {
suffix = 'in';
pointResolution *= 39.37;
} else if (nominalCount < 1609.344) {
suffix = 'ft';
pointResolution /= 0.30480061;
} else {
suffix = 'mi';
pointResolution /= 1609.3472;
}
} else {
assert(false, 33); // Invalid units
}
/**
* @private
* @type {number|undefined}
*/
this.renderedWidth_ = undefined;
let i = 3 * Math.floor(
Math.log(this.minWidth_ * pointResolution) / Math.log(10));
let count, width;
while (true) {
count = LEADING_DIGITS[((i % 3) + 3) % 3] *
Math.pow(10, Math.floor(i / 3));
width = Math.round(count / pointResolution);
if (isNaN(width)) {
this.element_.style.display = 'none';
this.renderedVisible_ = false;
return;
} else if (width >= this.minWidth_) {
break;
}
++i;
}
/**
* @private
* @type {string}
*/
this.renderedHTML_ = '';
const html = count + ' ' + suffix;
if (this.renderedHTML_ != html) {
this.innerElement_.innerHTML = html;
this.renderedHTML_ = html;
}
Control.call(this, {
element: this.element_,
render: options.render || render,
target: options.target
});
if (this.renderedWidth_ != width) {
this.innerElement_.style.width = width + 'px';
this.renderedWidth_ = width;
}
listen(
this, getChangeEventType(UNITS_PROP),
this.handleUnitsChanged_, this);
if (!this.renderedVisible_) {
this.element_.style.display = '';
this.renderedVisible_ = true;
}
this.setUnits(/** @type {module:ol/control/ScaleLine~Units} */ (options.units) ||
Units.METRIC);
};
}
}
inherits(ScaleLine, Control);
/**
* Return the units to use in the scale line.
* @return {module:ol/control/ScaleLine~Units|undefined} The units
* to use in the scale line.
* @observable
* @api
*/
ScaleLine.prototype.getUnits = function() {
return (
/** @type {module:ol/control/ScaleLine~Units|undefined} */ (this.get(UNITS_PROP))
);
};
/**
* Update the scale line element.
* @param {module:ol/MapEvent} mapEvent Map event.
@@ -164,145 +304,4 @@ export function render(mapEvent) {
}
/**
* @private
*/
ScaleLine.prototype.handleUnitsChanged_ = function() {
this.updateElement_();
};
/**
* Set the units to use in the scale line.
* @param {module:ol/control/ScaleLine~Units} units The units to use in the scale line.
* @observable
* @api
*/
ScaleLine.prototype.setUnits = function(units) {
this.set(UNITS_PROP, units);
};
/**
* @private
*/
ScaleLine.prototype.updateElement_ = function() {
const viewState = this.viewState_;
if (!viewState) {
if (this.renderedVisible_) {
this.element_.style.display = 'none';
this.renderedVisible_ = false;
}
return;
}
const center = viewState.center;
const projection = viewState.projection;
const units = this.getUnits();
const pointResolutionUnits = units == Units.DEGREES ?
ProjUnits.DEGREES :
ProjUnits.METERS;
let pointResolution =
getPointResolution(projection, viewState.resolution, center, pointResolutionUnits);
if (projection.getUnits() != ProjUnits.DEGREES && projection.getMetersPerUnit()
&& pointResolutionUnits == ProjUnits.METERS) {
pointResolution *= projection.getMetersPerUnit();
}
let nominalCount = this.minWidth_ * pointResolution;
let suffix = '';
if (units == Units.DEGREES) {
const metersPerDegree = METERS_PER_UNIT[ProjUnits.DEGREES];
if (projection.getUnits() == ProjUnits.DEGREES) {
nominalCount *= metersPerDegree;
} else {
pointResolution /= metersPerDegree;
}
if (nominalCount < metersPerDegree / 60) {
suffix = '\u2033'; // seconds
pointResolution *= 3600;
} else if (nominalCount < metersPerDegree) {
suffix = '\u2032'; // minutes
pointResolution *= 60;
} else {
suffix = '\u00b0'; // degrees
}
} else if (units == Units.IMPERIAL) {
if (nominalCount < 0.9144) {
suffix = 'in';
pointResolution /= 0.0254;
} else if (nominalCount < 1609.344) {
suffix = 'ft';
pointResolution /= 0.3048;
} else {
suffix = 'mi';
pointResolution /= 1609.344;
}
} else if (units == Units.NAUTICAL) {
pointResolution /= 1852;
suffix = 'nm';
} else if (units == Units.METRIC) {
if (nominalCount < 0.001) {
suffix = 'μm';
pointResolution *= 1000000;
} else if (nominalCount < 1) {
suffix = 'mm';
pointResolution *= 1000;
} else if (nominalCount < 1000) {
suffix = 'm';
} else {
suffix = 'km';
pointResolution /= 1000;
}
} else if (units == Units.US) {
if (nominalCount < 0.9144) {
suffix = 'in';
pointResolution *= 39.37;
} else if (nominalCount < 1609.344) {
suffix = 'ft';
pointResolution /= 0.30480061;
} else {
suffix = 'mi';
pointResolution /= 1609.3472;
}
} else {
assert(false, 33); // Invalid units
}
let i = 3 * Math.floor(
Math.log(this.minWidth_ * pointResolution) / Math.log(10));
let count, width;
while (true) {
count = LEADING_DIGITS[((i % 3) + 3) % 3] *
Math.pow(10, Math.floor(i / 3));
width = Math.round(count / pointResolution);
if (isNaN(width)) {
this.element_.style.display = 'none';
this.renderedVisible_ = false;
return;
} else if (width >= this.minWidth_) {
break;
}
++i;
}
const html = count + ' ' + suffix;
if (this.renderedHTML_ != html) {
this.innerElement_.innerHTML = html;
this.renderedHTML_ = html;
}
if (this.renderedWidth_ != width) {
this.innerElement_.style.width = width + 'px';
this.renderedWidth_ = width;
}
if (!this.renderedVisible_) {
this.element_.style.display = '';
this.renderedVisible_ = true;
}
};
export default ScaleLine;

View File

@@ -36,104 +36,106 @@ import {easeOut} from '../easing.js';
* @param {module:ol/control/Zoom~Options=} opt_options Zoom options.
* @api
*/
const Zoom = function(opt_options) {
class Zoom {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
const className = options.className !== undefined ? options.className : 'ol-zoom';
const className = options.className !== undefined ? options.className : 'ol-zoom';
const delta = options.delta !== undefined ? options.delta : 1;
const delta = options.delta !== undefined ? options.delta : 1;
const zoomInLabel = options.zoomInLabel !== undefined ? options.zoomInLabel : '+';
const zoomOutLabel = options.zoomOutLabel !== undefined ? options.zoomOutLabel : '\u2212';
const zoomInLabel = options.zoomInLabel !== undefined ? options.zoomInLabel : '+';
const zoomOutLabel = options.zoomOutLabel !== undefined ? options.zoomOutLabel : '\u2212';
const zoomInTipLabel = options.zoomInTipLabel !== undefined ?
options.zoomInTipLabel : 'Zoom in';
const zoomOutTipLabel = options.zoomOutTipLabel !== undefined ?
options.zoomOutTipLabel : 'Zoom out';
const zoomInTipLabel = options.zoomInTipLabel !== undefined ?
options.zoomInTipLabel : 'Zoom in';
const zoomOutTipLabel = options.zoomOutTipLabel !== undefined ?
options.zoomOutTipLabel : 'Zoom out';
const inElement = document.createElement('button');
inElement.className = className + '-in';
inElement.setAttribute('type', 'button');
inElement.title = zoomInTipLabel;
inElement.appendChild(
typeof zoomInLabel === 'string' ? document.createTextNode(zoomInLabel) : zoomInLabel
);
const inElement = document.createElement('button');
inElement.className = className + '-in';
inElement.setAttribute('type', 'button');
inElement.title = zoomInTipLabel;
inElement.appendChild(
typeof zoomInLabel === 'string' ? document.createTextNode(zoomInLabel) : zoomInLabel
);
listen(inElement, EventType.CLICK,
Zoom.prototype.handleClick_.bind(this, delta));
listen(inElement, EventType.CLICK,
Zoom.prototype.handleClick_.bind(this, delta));
const outElement = document.createElement('button');
outElement.className = className + '-out';
outElement.setAttribute('type', 'button');
outElement.title = zoomOutTipLabel;
outElement.appendChild(
typeof zoomOutLabel === 'string' ? document.createTextNode(zoomOutLabel) : zoomOutLabel
);
const outElement = document.createElement('button');
outElement.className = className + '-out';
outElement.setAttribute('type', 'button');
outElement.title = zoomOutTipLabel;
outElement.appendChild(
typeof zoomOutLabel === 'string' ? document.createTextNode(zoomOutLabel) : zoomOutLabel
);
listen(outElement, EventType.CLICK,
Zoom.prototype.handleClick_.bind(this, -delta));
listen(outElement, EventType.CLICK,
Zoom.prototype.handleClick_.bind(this, -delta));
const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL;
const element = document.createElement('div');
element.className = cssClasses;
element.appendChild(inElement);
element.appendChild(outElement);
const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL;
const element = document.createElement('div');
element.className = cssClasses;
element.appendChild(inElement);
element.appendChild(outElement);
Control.call(this, {
element: element,
target: options.target
});
Control.call(this, {
element: element,
target: options.target
});
/**
* @type {number}
* @private
*/
this.duration_ = options.duration !== undefined ? options.duration : 250;
}
/**
* @type {number}
* @param {number} delta Zoom delta.
* @param {MouseEvent} event The event to handle
* @private
*/
this.duration_ = options.duration !== undefined ? options.duration : 250;
handleClick_(delta, event) {
event.preventDefault();
this.zoomByDelta_(delta);
}
};
/**
* @param {number} delta Zoom delta.
* @private
*/
zoomByDelta_(delta) {
const map = this.getMap();
const view = map.getView();
if (!view) {
// the map does not have a view, so we can't act
// upon it
return;
}
const currentResolution = view.getResolution();
if (currentResolution) {
const newResolution = view.constrainResolution(currentResolution, delta);
if (this.duration_ > 0) {
if (view.getAnimating()) {
view.cancelAnimations();
}
view.animate({
resolution: newResolution,
duration: this.duration_,
easing: easeOut
});
} else {
view.setResolution(newResolution);
}
}
}
}
inherits(Zoom, Control);
/**
* @param {number} delta Zoom delta.
* @param {MouseEvent} event The event to handle
* @private
*/
Zoom.prototype.handleClick_ = function(delta, event) {
event.preventDefault();
this.zoomByDelta_(delta);
};
/**
* @param {number} delta Zoom delta.
* @private
*/
Zoom.prototype.zoomByDelta_ = function(delta) {
const map = this.getMap();
const view = map.getView();
if (!view) {
// the map does not have a view, so we can't act
// upon it
return;
}
const currentResolution = view.getResolution();
if (currentResolution) {
const newResolution = view.constrainResolution(currentResolution, delta);
if (this.duration_ > 0) {
if (view.getAnimating()) {
view.cancelAnimations();
}
view.animate({
resolution: newResolution,
duration: this.duration_,
easing: easeOut
});
} else {
view.setResolution(newResolution);
}
}
};
export default Zoom;

View File

@@ -47,164 +47,303 @@ const Direction = {
* @param {module:ol/control/ZoomSlider~Options=} opt_options Zoom slider options.
* @api
*/
const ZoomSlider = function(opt_options) {
class ZoomSlider {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
/**
* Will hold the current resolution of the view.
*
* @type {number|undefined}
* @private
*/
this.currentResolution_ = undefined;
/**
* The direction of the slider. Will be determined from actual display of the
* container and defaults to Direction.VERTICAL.
*
* @type {Direction}
* @private
*/
this.direction_ = Direction.VERTICAL;
/**
* @type {boolean}
* @private
*/
this.dragging_;
/**
* @type {number}
* @private
*/
this.heightLimit_ = 0;
/**
* @type {number}
* @private
*/
this.widthLimit_ = 0;
/**
* @type {number|undefined}
* @private
*/
this.previousX_;
/**
* @type {number|undefined}
* @private
*/
this.previousY_;
/**
* The calculated thumb size (border box plus margins). Set when initSlider_
* is called.
* @type {module:ol/size~Size}
* @private
*/
this.thumbSize_ = null;
/**
* Whether the slider is initialized.
* @type {boolean}
* @private
*/
this.sliderInitialized_ = false;
/**
* @type {number}
* @private
*/
this.duration_ = options.duration !== undefined ? options.duration : 200;
const className = options.className !== undefined ? options.className : 'ol-zoomslider';
const thumbElement = document.createElement('button');
thumbElement.setAttribute('type', 'button');
thumbElement.className = className + '-thumb ' + CLASS_UNSELECTABLE;
const containerElement = document.createElement('div');
containerElement.className = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL;
containerElement.appendChild(thumbElement);
/**
* @type {module:ol/pointer/PointerEventHandler}
* @private
*/
this.dragger_ = new PointerEventHandler(containerElement);
listen(this.dragger_, PointerEventType.POINTERDOWN,
this.handleDraggerStart_, this);
listen(this.dragger_, PointerEventType.POINTERMOVE,
this.handleDraggerDrag_, this);
listen(this.dragger_, PointerEventType.POINTERUP,
this.handleDraggerEnd_, this);
listen(containerElement, EventType.CLICK, this.handleContainerClick_, this);
listen(thumbElement, EventType.CLICK, stopPropagation);
Control.call(this, {
element: containerElement,
render: options.render || render
});
}
/**
* Will hold the current resolution of the view.
* @inheritDoc
*/
disposeInternal() {
this.dragger_.dispose();
Control.prototype.disposeInternal.call(this);
}
/**
* @inheritDoc
*/
setMap(map) {
Control.prototype.setMap.call(this, map);
if (map) {
map.render();
}
}
/**
* Initializes the slider element. This will determine and set this controls
* direction_ and also constrain the dragging of the thumb to always be within
* the bounds of the container.
*
* @type {number|undefined}
* @private
*/
this.currentResolution_ = undefined;
initSlider_() {
const container = this.element;
const containerSize = {
width: container.offsetWidth, height: container.offsetHeight
};
const thumb = container.firstElementChild;
const computedStyle = getComputedStyle(thumb);
const thumbWidth = thumb.offsetWidth +
parseFloat(computedStyle['marginRight']) +
parseFloat(computedStyle['marginLeft']);
const thumbHeight = thumb.offsetHeight +
parseFloat(computedStyle['marginTop']) +
parseFloat(computedStyle['marginBottom']);
this.thumbSize_ = [thumbWidth, thumbHeight];
if (containerSize.width > containerSize.height) {
this.direction_ = Direction.HORIZONTAL;
this.widthLimit_ = containerSize.width - thumbWidth;
} else {
this.direction_ = Direction.VERTICAL;
this.heightLimit_ = containerSize.height - thumbHeight;
}
this.sliderInitialized_ = true;
}
/**
* The direction of the slider. Will be determined from actual display of the
* container and defaults to Direction.VERTICAL.
* @param {MouseEvent} event The browser event to handle.
* @private
*/
handleContainerClick_(event) {
const view = this.getMap().getView();
const relativePosition = this.getRelativePosition_(
event.offsetX - this.thumbSize_[0] / 2,
event.offsetY - this.thumbSize_[1] / 2);
const resolution = this.getResolutionForPosition_(relativePosition);
view.animate({
resolution: view.constrainResolution(resolution),
duration: this.duration_,
easing: easeOut
});
}
/**
* Handle dragger start events.
* @param {module:ol/pointer/PointerEvent} event The drag event.
* @private
*/
handleDraggerStart_(event) {
if (!this.dragging_ && event.originalEvent.target === this.element.firstElementChild) {
this.getMap().getView().setHint(ViewHint.INTERACTING, 1);
this.previousX_ = event.clientX;
this.previousY_ = event.clientY;
this.dragging_ = true;
}
}
/**
* Handle dragger drag events.
*
* @type {Direction}
* @param {module:ol/pointer/PointerEvent|Event} event The drag event.
* @private
*/
this.direction_ = Direction.VERTICAL;
handleDraggerDrag_(event) {
if (this.dragging_) {
const element = this.element.firstElementChild;
const deltaX = event.clientX - this.previousX_ + parseInt(element.style.left, 10);
const deltaY = event.clientY - this.previousY_ + parseInt(element.style.top, 10);
const relativePosition = this.getRelativePosition_(deltaX, deltaY);
this.currentResolution_ = this.getResolutionForPosition_(relativePosition);
this.getMap().getView().setResolution(this.currentResolution_);
this.setThumbPosition_(this.currentResolution_);
this.previousX_ = event.clientX;
this.previousY_ = event.clientY;
}
}
/**
* @type {boolean}
* Handle dragger end events.
* @param {module:ol/pointer/PointerEvent|Event} event The drag event.
* @private
*/
this.dragging_;
handleDraggerEnd_(event) {
if (this.dragging_) {
const view = this.getMap().getView();
view.setHint(ViewHint.INTERACTING, -1);
view.animate({
resolution: view.constrainResolution(this.currentResolution_),
duration: this.duration_,
easing: easeOut
});
this.dragging_ = false;
this.previousX_ = undefined;
this.previousY_ = undefined;
}
}
/**
* @type {number}
* Positions the thumb inside its container according to the given resolution.
*
* @param {number} res The res.
* @private
*/
this.heightLimit_ = 0;
setThumbPosition_(res) {
const position = this.getPositionForResolution_(res);
const thumb = this.element.firstElementChild;
if (this.direction_ == Direction.HORIZONTAL) {
thumb.style.left = this.widthLimit_ * position + 'px';
} else {
thumb.style.top = this.heightLimit_ * position + 'px';
}
}
/**
* @type {number}
* Calculates the relative position of the thumb given x and y offsets. The
* relative position scales from 0 to 1. The x and y offsets are assumed to be
* in pixel units within the dragger limits.
*
* @param {number} x Pixel position relative to the left of the slider.
* @param {number} y Pixel position relative to the top of the slider.
* @return {number} The relative position of the thumb.
* @private
*/
this.widthLimit_ = 0;
getRelativePosition_(x, y) {
let amount;
if (this.direction_ === Direction.HORIZONTAL) {
amount = x / this.widthLimit_;
} else {
amount = y / this.heightLimit_;
}
return clamp(amount, 0, 1);
}
/**
* @type {number|undefined}
* Calculates the corresponding resolution of the thumb given its relative
* position (where 0 is the minimum and 1 is the maximum).
*
* @param {number} position The relative position of the thumb.
* @return {number} The corresponding resolution.
* @private
*/
this.previousX_;
getResolutionForPosition_(position) {
const fn = this.getMap().getView().getResolutionForValueFunction();
return fn(1 - position);
}
/**
* @type {number|undefined}
* Determines the relative position of the slider for the given resolution. A
* relative position of 0 corresponds to the minimum view resolution. A
* relative position of 1 corresponds to the maximum view resolution.
*
* @param {number} res The resolution.
* @return {number} The relative position value (between 0 and 1).
* @private
*/
this.previousY_;
/**
* The calculated thumb size (border box plus margins). Set when initSlider_
* is called.
* @type {module:ol/size~Size}
* @private
*/
this.thumbSize_ = null;
/**
* Whether the slider is initialized.
* @type {boolean}
* @private
*/
this.sliderInitialized_ = false;
/**
* @type {number}
* @private
*/
this.duration_ = options.duration !== undefined ? options.duration : 200;
const className = options.className !== undefined ? options.className : 'ol-zoomslider';
const thumbElement = document.createElement('button');
thumbElement.setAttribute('type', 'button');
thumbElement.className = className + '-thumb ' + CLASS_UNSELECTABLE;
const containerElement = document.createElement('div');
containerElement.className = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL;
containerElement.appendChild(thumbElement);
/**
* @type {module:ol/pointer/PointerEventHandler}
* @private
*/
this.dragger_ = new PointerEventHandler(containerElement);
listen(this.dragger_, PointerEventType.POINTERDOWN,
this.handleDraggerStart_, this);
listen(this.dragger_, PointerEventType.POINTERMOVE,
this.handleDraggerDrag_, this);
listen(this.dragger_, PointerEventType.POINTERUP,
this.handleDraggerEnd_, this);
listen(containerElement, EventType.CLICK, this.handleContainerClick_, this);
listen(thumbElement, EventType.CLICK, stopPropagation);
Control.call(this, {
element: containerElement,
render: options.render || render
});
};
getPositionForResolution_(res) {
const fn = this.getMap().getView().getValueForResolutionFunction();
return 1 - fn(res);
}
}
inherits(ZoomSlider, Control);
/**
* @inheritDoc
*/
ZoomSlider.prototype.disposeInternal = function() {
this.dragger_.dispose();
Control.prototype.disposeInternal.call(this);
};
/**
* @inheritDoc
*/
ZoomSlider.prototype.setMap = function(map) {
Control.prototype.setMap.call(this, map);
if (map) {
map.render();
}
};
/**
* Initializes the slider element. This will determine and set this controls
* direction_ and also constrain the dragging of the thumb to always be within
* the bounds of the container.
*
* @private
*/
ZoomSlider.prototype.initSlider_ = function() {
const container = this.element;
const containerSize = {
width: container.offsetWidth, height: container.offsetHeight
};
const thumb = container.firstElementChild;
const computedStyle = getComputedStyle(thumb);
const thumbWidth = thumb.offsetWidth +
parseFloat(computedStyle['marginRight']) +
parseFloat(computedStyle['marginLeft']);
const thumbHeight = thumb.offsetHeight +
parseFloat(computedStyle['marginTop']) +
parseFloat(computedStyle['marginBottom']);
this.thumbSize_ = [thumbWidth, thumbHeight];
if (containerSize.width > containerSize.height) {
this.direction_ = Direction.HORIZONTAL;
this.widthLimit_ = containerSize.width - thumbWidth;
} else {
this.direction_ = Direction.VERTICAL;
this.heightLimit_ = containerSize.height - thumbHeight;
}
this.sliderInitialized_ = true;
};
/**
* Update the zoomslider element.
* @param {module:ol/MapEvent} mapEvent Map event.
@@ -226,151 +365,4 @@ export function render(mapEvent) {
}
/**
* @param {MouseEvent} event The browser event to handle.
* @private
*/
ZoomSlider.prototype.handleContainerClick_ = function(event) {
const view = this.getMap().getView();
const relativePosition = this.getRelativePosition_(
event.offsetX - this.thumbSize_[0] / 2,
event.offsetY - this.thumbSize_[1] / 2);
const resolution = this.getResolutionForPosition_(relativePosition);
view.animate({
resolution: view.constrainResolution(resolution),
duration: this.duration_,
easing: easeOut
});
};
/**
* Handle dragger start events.
* @param {module:ol/pointer/PointerEvent} event The drag event.
* @private
*/
ZoomSlider.prototype.handleDraggerStart_ = function(event) {
if (!this.dragging_ && event.originalEvent.target === this.element.firstElementChild) {
this.getMap().getView().setHint(ViewHint.INTERACTING, 1);
this.previousX_ = event.clientX;
this.previousY_ = event.clientY;
this.dragging_ = true;
}
};
/**
* Handle dragger drag events.
*
* @param {module:ol/pointer/PointerEvent|Event} event The drag event.
* @private
*/
ZoomSlider.prototype.handleDraggerDrag_ = function(event) {
if (this.dragging_) {
const element = this.element.firstElementChild;
const deltaX = event.clientX - this.previousX_ + parseInt(element.style.left, 10);
const deltaY = event.clientY - this.previousY_ + parseInt(element.style.top, 10);
const relativePosition = this.getRelativePosition_(deltaX, deltaY);
this.currentResolution_ = this.getResolutionForPosition_(relativePosition);
this.getMap().getView().setResolution(this.currentResolution_);
this.setThumbPosition_(this.currentResolution_);
this.previousX_ = event.clientX;
this.previousY_ = event.clientY;
}
};
/**
* Handle dragger end events.
* @param {module:ol/pointer/PointerEvent|Event} event The drag event.
* @private
*/
ZoomSlider.prototype.handleDraggerEnd_ = function(event) {
if (this.dragging_) {
const view = this.getMap().getView();
view.setHint(ViewHint.INTERACTING, -1);
view.animate({
resolution: view.constrainResolution(this.currentResolution_),
duration: this.duration_,
easing: easeOut
});
this.dragging_ = false;
this.previousX_ = undefined;
this.previousY_ = undefined;
}
};
/**
* Positions the thumb inside its container according to the given resolution.
*
* @param {number} res The res.
* @private
*/
ZoomSlider.prototype.setThumbPosition_ = function(res) {
const position = this.getPositionForResolution_(res);
const thumb = this.element.firstElementChild;
if (this.direction_ == Direction.HORIZONTAL) {
thumb.style.left = this.widthLimit_ * position + 'px';
} else {
thumb.style.top = this.heightLimit_ * position + 'px';
}
};
/**
* Calculates the relative position of the thumb given x and y offsets. The
* relative position scales from 0 to 1. The x and y offsets are assumed to be
* in pixel units within the dragger limits.
*
* @param {number} x Pixel position relative to the left of the slider.
* @param {number} y Pixel position relative to the top of the slider.
* @return {number} The relative position of the thumb.
* @private
*/
ZoomSlider.prototype.getRelativePosition_ = function(x, y) {
let amount;
if (this.direction_ === Direction.HORIZONTAL) {
amount = x / this.widthLimit_;
} else {
amount = y / this.heightLimit_;
}
return clamp(amount, 0, 1);
};
/**
* Calculates the corresponding resolution of the thumb given its relative
* position (where 0 is the minimum and 1 is the maximum).
*
* @param {number} position The relative position of the thumb.
* @return {number} The corresponding resolution.
* @private
*/
ZoomSlider.prototype.getResolutionForPosition_ = function(position) {
const fn = this.getMap().getView().getResolutionForValueFunction();
return fn(1 - position);
};
/**
* Determines the relative position of the slider for the given resolution. A
* relative position of 0 corresponds to the minimum view resolution. A
* relative position of 1 corresponds to the maximum view resolution.
*
* @param {number} res The resolution.
* @return {number} The relative position value (between 0 and 1).
* @private
*/
ZoomSlider.prototype.getPositionForResolution_ = function(res) {
const fn = this.getMap().getView().getValueForResolutionFunction();
return 1 - fn(res);
};
export default ZoomSlider;

View File

@@ -31,59 +31,61 @@ import {CLASS_CONTROL, CLASS_UNSELECTABLE} from '../css.js';
* @param {module:ol/control/ZoomToExtent~Options=} opt_options Options.
* @api
*/
const ZoomToExtent = function(opt_options) {
const options = opt_options ? opt_options : {};
class ZoomToExtent {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
/**
* @type {module:ol/extent~Extent}
* @protected
*/
this.extent = options.extent ? options.extent : null;
/**
* @type {module:ol/extent~Extent}
* @protected
*/
this.extent = options.extent ? options.extent : null;
const className = options.className !== undefined ? options.className : 'ol-zoom-extent';
const className = options.className !== undefined ? options.className : 'ol-zoom-extent';
const label = options.label !== undefined ? options.label : 'E';
const tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Fit to extent';
const button = document.createElement('button');
button.setAttribute('type', 'button');
button.title = tipLabel;
button.appendChild(
typeof label === 'string' ? document.createTextNode(label) : label
);
const label = options.label !== undefined ? options.label : 'E';
const tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Fit to extent';
const button = document.createElement('button');
button.setAttribute('type', 'button');
button.title = tipLabel;
button.appendChild(
typeof label === 'string' ? document.createTextNode(label) : label
);
listen(button, EventType.CLICK, this.handleClick_, this);
listen(button, EventType.CLICK, this.handleClick_, this);
const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL;
const element = document.createElement('div');
element.className = cssClasses;
element.appendChild(button);
const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL;
const element = document.createElement('div');
element.className = cssClasses;
element.appendChild(button);
Control.call(this, {
element: element,
target: options.target
});
};
Control.call(this, {
element: element,
target: options.target
});
}
/**
* @param {MouseEvent} event The event to handle
* @private
*/
handleClick_(event) {
event.preventDefault();
this.handleZoomToExtent();
}
/**
* @protected
*/
handleZoomToExtent() {
const map = this.getMap();
const view = map.getView();
const extent = !this.extent ? view.getProjection().getExtent() : this.extent;
view.fit(extent);
}
}
inherits(ZoomToExtent, Control);
/**
* @param {MouseEvent} event The event to handle
* @private
*/
ZoomToExtent.prototype.handleClick_ = function(event) {
event.preventDefault();
this.handleZoomToExtent();
};
/**
* @protected
*/
ZoomToExtent.prototype.handleZoomToExtent = function() {
const map = this.getMap();
const view = map.getView();
const extent = !this.extent ? view.getProjection().getExtent() : this.extent;
view.fit(extent);
};
export default ZoomToExtent;

View File

@@ -31,137 +31,135 @@ import Event from '../events/Event.js';
* @constructor
* @extends {module:ol/Disposable}
*/
const EventTarget = function() {
class EventTarget {
constructor() {
Disposable.call(this);
Disposable.call(this);
/**
* @private
* @type {!Object.<string, number>}
*/
this.pendingRemovals_ = {};
/**
* @private
* @type {!Object.<string, number>}
*/
this.dispatching_ = {};
/**
* @private
* @type {!Object.<string, Array.<module:ol/events~ListenerFunction>>}
*/
this.listeners_ = {};
}
/**
* @private
* @type {!Object.<string, number>}
* @param {string} type Type.
* @param {module:ol/events~ListenerFunction} listener Listener.
*/
this.pendingRemovals_ = {};
addEventListener(type, listener) {
let listeners = this.listeners_[type];
if (!listeners) {
listeners = this.listeners_[type] = [];
}
if (listeners.indexOf(listener) === -1) {
listeners.push(listener);
}
}
/**
* @private
* @type {!Object.<string, number>}
* @param {{type: string,
* target: (EventTarget|module:ol/events/EventTarget|undefined)}|module:ol/events/Event|
* string} event Event or event type.
* @return {boolean|undefined} `false` if anyone called preventDefault on the
* event object or if any of the listeners returned false.
*/
this.dispatching_ = {};
dispatchEvent(event) {
const evt = typeof event === 'string' ? new Event(event) : event;
const type = evt.type;
evt.target = this;
const listeners = this.listeners_[type];
let propagate;
if (listeners) {
if (!(type in this.dispatching_)) {
this.dispatching_[type] = 0;
this.pendingRemovals_[type] = 0;
}
++this.dispatching_[type];
for (let i = 0, ii = listeners.length; i < ii; ++i) {
if (listeners[i].call(this, evt) === false || evt.propagationStopped) {
propagate = false;
break;
}
}
--this.dispatching_[type];
if (this.dispatching_[type] === 0) {
let pendingRemovals = this.pendingRemovals_[type];
delete this.pendingRemovals_[type];
while (pendingRemovals--) {
this.removeEventListener(type, UNDEFINED);
}
delete this.dispatching_[type];
}
return propagate;
}
}
/**
* @private
* @type {!Object.<string, Array.<module:ol/events~ListenerFunction>>}
* @inheritDoc
*/
this.listeners_ = {};
disposeInternal() {
unlistenAll(this);
}
};
/**
* Get the listeners for a specified event type. Listeners are returned in the
* order that they will be called in.
*
* @param {string} type Type.
* @return {Array.<module:ol/events~ListenerFunction>} Listeners.
*/
getListeners(type) {
return this.listeners_[type];
}
/**
* @param {string=} opt_type Type. If not provided,
* `true` will be returned if this EventTarget has any listeners.
* @return {boolean} Has listeners.
*/
hasListener(opt_type) {
return opt_type ?
opt_type in this.listeners_ :
Object.keys(this.listeners_).length > 0;
}
/**
* @param {string} type Type.
* @param {module:ol/events~ListenerFunction} listener Listener.
*/
removeEventListener(type, listener) {
const listeners = this.listeners_[type];
if (listeners) {
const index = listeners.indexOf(listener);
if (type in this.pendingRemovals_) {
// make listener a no-op, and remove later in #dispatchEvent()
listeners[index] = UNDEFINED;
++this.pendingRemovals_[type];
} else {
listeners.splice(index, 1);
if (listeners.length === 0) {
delete this.listeners_[type];
}
}
}
}
}
inherits(EventTarget, Disposable);
/**
* @param {string} type Type.
* @param {module:ol/events~ListenerFunction} listener Listener.
*/
EventTarget.prototype.addEventListener = function(type, listener) {
let listeners = this.listeners_[type];
if (!listeners) {
listeners = this.listeners_[type] = [];
}
if (listeners.indexOf(listener) === -1) {
listeners.push(listener);
}
};
/**
* @param {{type: string,
* target: (EventTarget|module:ol/events/EventTarget|undefined)}|module:ol/events/Event|
* string} event Event or event type.
* @return {boolean|undefined} `false` if anyone called preventDefault on the
* event object or if any of the listeners returned false.
*/
EventTarget.prototype.dispatchEvent = function(event) {
const evt = typeof event === 'string' ? new Event(event) : event;
const type = evt.type;
evt.target = this;
const listeners = this.listeners_[type];
let propagate;
if (listeners) {
if (!(type in this.dispatching_)) {
this.dispatching_[type] = 0;
this.pendingRemovals_[type] = 0;
}
++this.dispatching_[type];
for (let i = 0, ii = listeners.length; i < ii; ++i) {
if (listeners[i].call(this, evt) === false || evt.propagationStopped) {
propagate = false;
break;
}
}
--this.dispatching_[type];
if (this.dispatching_[type] === 0) {
let pendingRemovals = this.pendingRemovals_[type];
delete this.pendingRemovals_[type];
while (pendingRemovals--) {
this.removeEventListener(type, UNDEFINED);
}
delete this.dispatching_[type];
}
return propagate;
}
};
/**
* @inheritDoc
*/
EventTarget.prototype.disposeInternal = function() {
unlistenAll(this);
};
/**
* Get the listeners for a specified event type. Listeners are returned in the
* order that they will be called in.
*
* @param {string} type Type.
* @return {Array.<module:ol/events~ListenerFunction>} Listeners.
*/
EventTarget.prototype.getListeners = function(type) {
return this.listeners_[type];
};
/**
* @param {string=} opt_type Type. If not provided,
* `true` will be returned if this EventTarget has any listeners.
* @return {boolean} Has listeners.
*/
EventTarget.prototype.hasListener = function(opt_type) {
return opt_type ?
opt_type in this.listeners_ :
Object.keys(this.listeners_).length > 0;
};
/**
* @param {string} type Type.
* @param {module:ol/events~ListenerFunction} listener Listener.
*/
EventTarget.prototype.removeEventListener = function(type, listener) {
const listeners = this.listeners_[type];
if (listeners) {
const index = listeners.indexOf(listener);
if (type in this.pendingRemovals_) {
// make listener a no-op, and remove later in #dispatchEvent()
listeners[index] = UNDEFINED;
++this.pendingRemovals_[type];
} else {
listeners.splice(index, 1);
if (listeners.length === 0) {
delete this.listeners_[type];
}
}
}
};
export default EventTarget;

View File

@@ -63,20 +63,148 @@ GEOMETRY_WRITERS[GeometryType.MULTI_POLYGON] = writeMultiPolygonGeometry;
* @param {module:ol/format/EsriJSON~Options=} opt_options Options.
* @api
*/
const EsriJSON = function(opt_options) {
class EsriJSON {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
JSONFeature.call(this);
JSONFeature.call(this);
/**
* Name of the geometry attribute for features.
* @type {string|undefined}
* @private
*/
this.geometryName_ = options.geometryName;
}
/**
* Name of the geometry attribute for features.
* @type {string|undefined}
* @private
* @inheritDoc
*/
this.geometryName_ = options.geometryName;
readFeatureFromObject(object, opt_options) {
const esriJSONFeature = /** @type {EsriJSONFeature} */ (object);
const geometry = readGeometry(esriJSONFeature.geometry, opt_options);
const feature = new Feature();
if (this.geometryName_) {
feature.setGeometryName(this.geometryName_);
}
feature.setGeometry(geometry);
if (opt_options && opt_options.idField &&
esriJSONFeature.attributes[opt_options.idField]) {
feature.setId(/** @type {number} */(esriJSONFeature.attributes[opt_options.idField]));
}
if (esriJSONFeature.attributes) {
feature.setProperties(esriJSONFeature.attributes);
}
return feature;
}
};
/**
* @inheritDoc
*/
readFeaturesFromObject(object, opt_options) {
const esriJSONObject = /** @type {EsriJSONObject} */ (object);
const options = opt_options ? opt_options : {};
if (esriJSONObject.features) {
const esriJSONFeatureCollection = /** @type {EsriJSONFeatureCollection} */ (object);
/** @type {Array.<module:ol/Feature>} */
const features = [];
const esriJSONFeatures = esriJSONFeatureCollection.features;
options.idField = object.objectIdFieldName;
for (let i = 0, ii = esriJSONFeatures.length; i < ii; ++i) {
features.push(this.readFeatureFromObject(esriJSONFeatures[i], options));
}
return features;
} else {
return [this.readFeatureFromObject(object, options)];
}
}
/**
* @inheritDoc
*/
readGeometryFromObject(object, opt_options) {
return readGeometry(/** @type {EsriJSONGeometry} */(object), opt_options);
}
/**
* @inheritDoc
*/
readProjectionFromObject(object) {
const esriJSONObject = /** @type {EsriJSONObject} */ (object);
if (esriJSONObject.spatialReference && esriJSONObject.spatialReference.wkid) {
const crs = esriJSONObject.spatialReference.wkid;
return getProjection('EPSG:' + crs);
} else {
return null;
}
}
/**
* Encode a geometry as a EsriJSON object.
*
* @param {module:ol/geom/Geometry} geometry Geometry.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {EsriJSONGeometry} Object.
* @override
* @api
*/
writeGeometryObject(geometry, opt_options) {
return writeGeometry(geometry, this.adaptOptions(opt_options));
}
/**
* Encode a feature as a esriJSON Feature object.
*
* @param {module:ol/Feature} feature Feature.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {Object} Object.
* @override
* @api
*/
writeFeatureObject(feature, opt_options) {
opt_options = this.adaptOptions(opt_options);
const object = {};
const geometry = feature.getGeometry();
if (geometry) {
object['geometry'] = writeGeometry(geometry, opt_options);
if (opt_options && opt_options.featureProjection) {
object['geometry']['spatialReference'] = /** @type {EsriJSONCRS} */({
wkid: getProjection(opt_options.featureProjection).getCode().split(':').pop()
});
}
}
const properties = feature.getProperties();
delete properties[feature.getGeometryName()];
if (!isEmpty(properties)) {
object['attributes'] = properties;
} else {
object['attributes'] = {};
}
return object;
}
/**
* Encode an array of features as a EsriJSON object.
*
* @param {Array.<module:ol/Feature>} features Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {Object} EsriJSON Object.
* @override
* @api
*/
writeFeaturesObject(features, opt_options) {
opt_options = this.adaptOptions(opt_options);
const objects = [];
for (let i = 0, ii = features.length; i < ii; ++i) {
objects.push(this.writeFeatureObject(features[i], opt_options));
}
return /** @type {EsriJSONFeatureCollection} */ ({
'features': objects
});
}
}
inherits(EsriJSON, JSONFeature);
@@ -439,50 +567,6 @@ EsriJSON.prototype.readFeature;
EsriJSON.prototype.readFeatures;
/**
* @inheritDoc
*/
EsriJSON.prototype.readFeatureFromObject = function(object, opt_options) {
const esriJSONFeature = /** @type {EsriJSONFeature} */ (object);
const geometry = readGeometry(esriJSONFeature.geometry, opt_options);
const feature = new Feature();
if (this.geometryName_) {
feature.setGeometryName(this.geometryName_);
}
feature.setGeometry(geometry);
if (opt_options && opt_options.idField &&
esriJSONFeature.attributes[opt_options.idField]) {
feature.setId(/** @type {number} */(esriJSONFeature.attributes[opt_options.idField]));
}
if (esriJSONFeature.attributes) {
feature.setProperties(esriJSONFeature.attributes);
}
return feature;
};
/**
* @inheritDoc
*/
EsriJSON.prototype.readFeaturesFromObject = function(object, opt_options) {
const esriJSONObject = /** @type {EsriJSONObject} */ (object);
const options = opt_options ? opt_options : {};
if (esriJSONObject.features) {
const esriJSONFeatureCollection = /** @type {EsriJSONFeatureCollection} */ (object);
/** @type {Array.<module:ol/Feature>} */
const features = [];
const esriJSONFeatures = esriJSONFeatureCollection.features;
options.idField = object.objectIdFieldName;
for (let i = 0, ii = esriJSONFeatures.length; i < ii; ++i) {
features.push(this.readFeatureFromObject(esriJSONFeatures[i], options));
}
return features;
} else {
return [this.readFeatureFromObject(object, options)];
}
};
/**
* Read a geometry from a EsriJSON source.
*
@@ -495,14 +579,6 @@ EsriJSON.prototype.readFeaturesFromObject = function(object, opt_options) {
EsriJSON.prototype.readGeometry;
/**
* @inheritDoc
*/
EsriJSON.prototype.readGeometryFromObject = function(object, opt_options) {
return readGeometry(/** @type {EsriJSONGeometry} */(object), opt_options);
};
/**
* Read the projection from a EsriJSON source.
*
@@ -514,20 +590,6 @@ EsriJSON.prototype.readGeometryFromObject = function(object, opt_options) {
EsriJSON.prototype.readProjection;
/**
* @inheritDoc
*/
EsriJSON.prototype.readProjectionFromObject = function(object) {
const esriJSONObject = /** @type {EsriJSONObject} */ (object);
if (esriJSONObject.spatialReference && esriJSONObject.spatialReference.wkid) {
const crs = esriJSONObject.spatialReference.wkid;
return getProjection('EPSG:' + crs);
} else {
return null;
}
};
/**
* @param {module:ol/geom/Geometry} geometry Geometry.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
@@ -552,20 +614,6 @@ function writeGeometry(geometry, opt_options) {
EsriJSON.prototype.writeGeometry;
/**
* Encode a geometry as a EsriJSON object.
*
* @param {module:ol/geom/Geometry} geometry Geometry.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {EsriJSONGeometry} Object.
* @override
* @api
*/
EsriJSON.prototype.writeGeometryObject = function(geometry, opt_options) {
return writeGeometry(geometry, this.adaptOptions(opt_options));
};
/**
* Encode a feature as a EsriJSON Feature string.
*
@@ -578,38 +626,6 @@ EsriJSON.prototype.writeGeometryObject = function(geometry, opt_options) {
EsriJSON.prototype.writeFeature;
/**
* Encode a feature as a esriJSON Feature object.
*
* @param {module:ol/Feature} feature Feature.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {Object} Object.
* @override
* @api
*/
EsriJSON.prototype.writeFeatureObject = function(feature, opt_options) {
opt_options = this.adaptOptions(opt_options);
const object = {};
const geometry = feature.getGeometry();
if (geometry) {
object['geometry'] = writeGeometry(geometry, opt_options);
if (opt_options && opt_options.featureProjection) {
object['geometry']['spatialReference'] = /** @type {EsriJSONCRS} */({
wkid: getProjection(opt_options.featureProjection).getCode().split(':').pop()
});
}
}
const properties = feature.getProperties();
delete properties[feature.getGeometryName()];
if (!isEmpty(properties)) {
object['attributes'] = properties;
} else {
object['attributes'] = {};
}
return object;
};
/**
* Encode an array of features as EsriJSON.
*
@@ -622,24 +638,4 @@ EsriJSON.prototype.writeFeatureObject = function(feature, opt_options) {
EsriJSON.prototype.writeFeatures;
/**
* Encode an array of features as a EsriJSON object.
*
* @param {Array.<module:ol/Feature>} features Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {Object} EsriJSON Object.
* @override
* @api
*/
EsriJSON.prototype.writeFeaturesObject = function(features, opt_options) {
opt_options = this.adaptOptions(opt_options);
const objects = [];
for (let i = 0, ii = features.length; i < ii; ++i) {
objects.push(this.writeFeatureObject(features[i], opt_options));
}
return /** @type {EsriJSONFeatureCollection} */ ({
'features': objects
});
};
export default EsriJSON;

View File

@@ -60,150 +60,141 @@ import {get as getProjection, equivalent as equivalentProjection, transformExten
* @abstract
* @api
*/
const FeatureFormat = function() {
class FeatureFormat {
constructor() {
/**
* @protected
* @type {module:ol/proj/Projection}
*/
this.dataProjection = null;
/**
* @protected
* @type {module:ol/proj/Projection}
*/
this.dataProjection = null;
/**
* @protected
* @type {module:ol/proj/Projection}
*/
this.defaultFeatureProjection = null;
/**
* @protected
* @type {module:ol/proj/Projection}
*/
this.defaultFeatureProjection = null;
};
}
/**
* Adds the data projection to the read options.
* @param {Document|Node|Object|string} source Source.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Options.
* @return {module:ol/format/Feature~ReadOptions|undefined} Options.
* @protected
*/
getReadOptions(source, opt_options) {
let options;
if (opt_options) {
options = {
dataProjection: opt_options.dataProjection ?
opt_options.dataProjection : this.readProjection(source),
featureProjection: opt_options.featureProjection
};
}
return this.adaptOptions(options);
}
/**
* Adds the data projection to the read options.
* @param {Document|Node|Object|string} source Source.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Options.
* @return {module:ol/format/Feature~ReadOptions|undefined} Options.
* @protected
*/
FeatureFormat.prototype.getReadOptions = function(source, opt_options) {
let options;
if (opt_options) {
options = {
dataProjection: opt_options.dataProjection ?
opt_options.dataProjection : this.readProjection(source),
featureProjection: opt_options.featureProjection
};
}
return this.adaptOptions(options);
};
/**
* Sets the `dataProjection` on the options, if no `dataProjection`
* is set.
* @param {module:ol/format/Feature~WriteOptions|module:ol/format/Feature~ReadOptions|undefined} options
* Options.
* @protected
* @return {module:ol/format/Feature~WriteOptions|module:ol/format/Feature~ReadOptions|undefined}
* Updated options.
*/
adaptOptions(options) {
return assign({
dataProjection: this.dataProjection,
featureProjection: this.defaultFeatureProjection
}, options);
}
/**
* Get the extent from the source of the last {@link readFeatures} call.
* @return {module:ol/extent~Extent} Tile extent.
*/
getLastExtent() {
return null;
}
/**
* Sets the `dataProjection` on the options, if no `dataProjection`
* is set.
* @param {module:ol/format/Feature~WriteOptions|module:ol/format/Feature~ReadOptions|undefined} options
* Options.
* @protected
* @return {module:ol/format/Feature~WriteOptions|module:ol/format/Feature~ReadOptions|undefined}
* Updated options.
*/
FeatureFormat.prototype.adaptOptions = function(options) {
return assign({
dataProjection: this.dataProjection,
featureProjection: this.defaultFeatureProjection
}, options);
};
/**
* @abstract
* @return {module:ol/format/FormatType} Format.
*/
getType() {}
/**
* Read a single feature from a source.
*
* @abstract
* @param {Document|Node|Object|string} source Source.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @return {module:ol/Feature} Feature.
*/
readFeature(source, opt_options) {}
/**
* Get the extent from the source of the last {@link readFeatures} call.
* @return {module:ol/extent~Extent} Tile extent.
*/
FeatureFormat.prototype.getLastExtent = function() {
return null;
};
/**
* Read all features from a source.
*
* @abstract
* @param {Document|Node|ArrayBuffer|Object|string} source Source.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @return {Array.<module:ol/Feature>} Features.
*/
readFeatures(source, opt_options) {}
/**
* Read a single geometry from a source.
*
* @abstract
* @param {Document|Node|Object|string} source Source.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @return {module:ol/geom/Geometry} Geometry.
*/
readGeometry(source, opt_options) {}
/**
* @abstract
* @return {module:ol/format/FormatType} Format.
*/
FeatureFormat.prototype.getType = function() {};
/**
* Read the projection from a source.
*
* @abstract
* @param {Document|Node|Object|string} source Source.
* @return {module:ol/proj/Projection} Projection.
*/
readProjection(source) {}
/**
* Encode a feature in this format.
*
* @abstract
* @param {module:ol/Feature} feature Feature.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {string} Result.
*/
writeFeature(feature, opt_options) {}
/**
* Read a single feature from a source.
*
* @abstract
* @param {Document|Node|Object|string} source Source.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @return {module:ol/Feature} Feature.
*/
FeatureFormat.prototype.readFeature = function(source, opt_options) {};
/**
* Encode an array of features in this format.
*
* @abstract
* @param {Array.<module:ol/Feature>} features Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {string} Result.
*/
writeFeatures(features, opt_options) {}
/**
* Read all features from a source.
*
* @abstract
* @param {Document|Node|ArrayBuffer|Object|string} source Source.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @return {Array.<module:ol/Feature>} Features.
*/
FeatureFormat.prototype.readFeatures = function(source, opt_options) {};
/**
* Read a single geometry from a source.
*
* @abstract
* @param {Document|Node|Object|string} source Source.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @return {module:ol/geom/Geometry} Geometry.
*/
FeatureFormat.prototype.readGeometry = function(source, opt_options) {};
/**
* Read the projection from a source.
*
* @abstract
* @param {Document|Node|Object|string} source Source.
* @return {module:ol/proj/Projection} Projection.
*/
FeatureFormat.prototype.readProjection = function(source) {};
/**
* Encode a feature in this format.
*
* @abstract
* @param {module:ol/Feature} feature Feature.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {string} Result.
*/
FeatureFormat.prototype.writeFeature = function(feature, opt_options) {};
/**
* Encode an array of features in this format.
*
* @abstract
* @param {Array.<module:ol/Feature>} features Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {string} Result.
*/
FeatureFormat.prototype.writeFeatures = function(features, opt_options) {};
/**
* Write a single geometry in this format.
*
* @abstract
* @param {module:ol/geom/Geometry} geometry Geometry.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {string} Result.
*/
FeatureFormat.prototype.writeGeometry = function(geometry, opt_options) {};
/**
* Write a single geometry in this format.
*
* @abstract
* @param {module:ol/geom/Geometry} geometry Geometry.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {string} Result.
*/
writeGeometry(geometry, opt_options) {}
}
export default FeatureFormat;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -75,44 +75,385 @@ export const GMLNS = 'http://www.opengis.net/gml';
* Optional configuration object.
* @extends {module:ol/format/XMLFeature}
*/
const GMLBase = function(opt_options) {
const options = /** @type {module:ol/format/GMLBase~Options} */ (opt_options ? opt_options : {});
class GMLBase {
constructor(opt_options) {
const options = /** @type {module:ol/format/GMLBase~Options} */ (opt_options ? opt_options : {});
/**
* @protected
* @type {Array.<string>|string|undefined}
*/
this.featureType = options.featureType;
/**
* @protected
* @type {Object.<string, string>|string|undefined}
*/
this.featureNS = options.featureNS;
/**
* @protected
* @type {string}
*/
this.srsName = options.srsName;
/**
* @protected
* @type {string}
*/
this.schemaLocation = '';
/**
* @type {Object.<string, Object.<string, Object>>}
*/
this.FEATURE_COLLECTION_PARSERS = {};
this.FEATURE_COLLECTION_PARSERS[GMLNS] = {
'featureMember': makeReplacer(GMLBase.prototype.readFeaturesInternal),
'featureMembers': makeReplacer(GMLBase.prototype.readFeaturesInternal)
};
XMLFeature.call(this);
}
/**
* @protected
* @type {Array.<string>|string|undefined}
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {Array.<module:ol/Feature> | undefined} Features.
*/
this.featureType = options.featureType;
readFeaturesInternal(node, objectStack) {
const localName = node.localName;
let features = null;
if (localName == 'FeatureCollection') {
if (node.namespaceURI === 'http://www.opengis.net/wfs') {
features = pushParseAndPop([],
this.FEATURE_COLLECTION_PARSERS, node,
objectStack, this);
} else {
features = pushParseAndPop(null,
this.FEATURE_COLLECTION_PARSERS, node,
objectStack, this);
}
} else if (localName == 'featureMembers' || localName == 'featureMember') {
const context = objectStack[0];
let featureType = context['featureType'];
let featureNS = context['featureNS'];
const prefix = 'p';
const defaultPrefix = 'p0';
if (!featureType && node.childNodes) {
featureType = [], featureNS = {};
for (let i = 0, ii = node.childNodes.length; i < ii; ++i) {
const child = node.childNodes[i];
if (child.nodeType === 1) {
const ft = child.nodeName.split(':').pop();
if (featureType.indexOf(ft) === -1) {
let key = '';
let count = 0;
const uri = child.namespaceURI;
for (const candidate in featureNS) {
if (featureNS[candidate] === uri) {
key = candidate;
break;
}
++count;
}
if (!key) {
key = prefix + count;
featureNS[key] = uri;
}
featureType.push(key + ':' + ft);
}
}
}
if (localName != 'featureMember') {
// recheck featureType for each featureMember
context['featureType'] = featureType;
context['featureNS'] = featureNS;
}
}
if (typeof featureNS === 'string') {
const ns = featureNS;
featureNS = {};
featureNS[defaultPrefix] = ns;
}
const parsersNS = {};
const featureTypes = Array.isArray(featureType) ? featureType : [featureType];
for (const p in featureNS) {
const parsers = {};
for (let i = 0, ii = featureTypes.length; i < ii; ++i) {
const featurePrefix = featureTypes[i].indexOf(':') === -1 ?
defaultPrefix : featureTypes[i].split(':')[0];
if (featurePrefix === p) {
parsers[featureTypes[i].split(':').pop()] =
(localName == 'featureMembers') ?
makeArrayPusher(this.readFeatureElement, this) :
makeReplacer(this.readFeatureElement, this);
}
}
parsersNS[featureNS[p]] = parsers;
}
if (localName == 'featureMember') {
features = pushParseAndPop(undefined, parsersNS, node, objectStack);
} else {
features = pushParseAndPop([], parsersNS, node, objectStack);
}
}
if (features === null) {
features = [];
}
return features;
}
/**
* @protected
* @type {Object.<string, string>|string|undefined}
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/geom/Geometry|undefined} Geometry.
*/
this.featureNS = options.featureNS;
readGeometryElement(node, objectStack) {
const context = /** @type {Object} */ (objectStack[0]);
context['srsName'] = node.firstElementChild.getAttribute('srsName');
context['srsDimension'] = node.firstElementChild.getAttribute('srsDimension');
/** @type {module:ol/geom/Geometry} */
const geometry = pushParseAndPop(null, this.GEOMETRY_PARSERS_, node, objectStack, this);
if (geometry) {
return (
/** @type {module:ol/geom/Geometry} */ (transformWithOptions(geometry, false, context))
);
} else {
return undefined;
}
}
/**
* @protected
* @type {string}
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/Feature} Feature.
*/
this.srsName = options.srsName;
readFeatureElement(node, objectStack) {
let n;
const fid = node.getAttribute('fid') || getAttributeNS(node, GMLNS, 'id');
const values = {};
let geometryName;
for (n = node.firstElementChild; n; n = n.nextElementSibling) {
const localName = n.localName;
// Assume attribute elements have one child node and that the child
// is a text or CDATA node (to be treated as text).
// Otherwise assume it is a geometry node.
if (n.childNodes.length === 0 ||
(n.childNodes.length === 1 &&
(n.firstChild.nodeType === 3 || n.firstChild.nodeType === 4))) {
let value = getAllTextContent(n, false);
if (ONLY_WHITESPACE_RE.test(value)) {
value = undefined;
}
values[localName] = value;
} else {
// boundedBy is an extent and must not be considered as a geometry
if (localName !== 'boundedBy') {
geometryName = localName;
}
values[localName] = this.readGeometryElement(n, objectStack);
}
}
const feature = new Feature(values);
if (geometryName) {
feature.setGeometryName(geometryName);
}
if (fid) {
feature.setId(fid);
}
return feature;
}
/**
* @protected
* @type {string}
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/geom/Point|undefined} Point.
*/
this.schemaLocation = '';
readPoint(node, objectStack) {
const flatCoordinates = this.readFlatCoordinatesFromNode_(node, objectStack);
if (flatCoordinates) {
return new Point(flatCoordinates, GeometryLayout.XYZ);
}
}
/**
* @type {Object.<string, Object.<string, Object>>}
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/geom/MultiPoint|undefined} MultiPoint.
*/
this.FEATURE_COLLECTION_PARSERS = {};
this.FEATURE_COLLECTION_PARSERS[GMLNS] = {
'featureMember': makeReplacer(GMLBase.prototype.readFeaturesInternal),
'featureMembers': makeReplacer(GMLBase.prototype.readFeaturesInternal)
};
readMultiPoint(node, objectStack) {
/** @type {Array.<Array.<number>>} */
const coordinates = pushParseAndPop([],
this.MULTIPOINT_PARSERS_, node, objectStack, this);
if (coordinates) {
return new MultiPoint(coordinates);
} else {
return undefined;
}
}
XMLFeature.call(this);
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/geom/MultiLineString|undefined} MultiLineString.
*/
readMultiLineString(node, objectStack) {
/** @type {Array.<module:ol/geom/LineString>} */
const lineStrings = pushParseAndPop([],
this.MULTILINESTRING_PARSERS_, node, objectStack, this);
if (lineStrings) {
return new MultiLineString(lineStrings);
}
}
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/geom/MultiPolygon|undefined} MultiPolygon.
*/
readMultiPolygon(node, objectStack) {
/** @type {Array.<module:ol/geom/Polygon>} */
const polygons = pushParseAndPop([], this.MULTIPOLYGON_PARSERS_, node, objectStack, this);
if (polygons) {
return new MultiPolygon(polygons);
}
}
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
*/
pointMemberParser_(node, objectStack) {
parseNode(this.POINTMEMBER_PARSERS_, node, objectStack, this);
}
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
*/
lineStringMemberParser_(node, objectStack) {
parseNode(this.LINESTRINGMEMBER_PARSERS_, node, objectStack, this);
}
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
*/
polygonMemberParser_(node, objectStack) {
parseNode(this.POLYGONMEMBER_PARSERS_, node, objectStack, this);
}
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/geom/LineString|undefined} LineString.
*/
readLineString(node, objectStack) {
const flatCoordinates = this.readFlatCoordinatesFromNode_(node, objectStack);
if (flatCoordinates) {
const lineString = new LineString(flatCoordinates, GeometryLayout.XYZ);
return lineString;
} else {
return undefined;
}
}
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
* @return {Array.<number>|undefined} LinearRing flat coordinates.
*/
readFlatLinearRing_(node, objectStack) {
const ring = pushParseAndPop(null,
this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node,
objectStack, this);
if (ring) {
return ring;
} else {
return undefined;
}
}
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/geom/LinearRing|undefined} LinearRing.
*/
readLinearRing(node, objectStack) {
const flatCoordinates = this.readFlatCoordinatesFromNode_(node, objectStack);
if (flatCoordinates) {
return new LinearRing(flatCoordinates, GeometryLayout.XYZ);
}
}
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/geom/Polygon|undefined} Polygon.
*/
readPolygon(node, objectStack) {
/** @type {Array.<Array.<number>>} */
const flatLinearRings = pushParseAndPop([null],
this.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack, this);
if (flatLinearRings && flatLinearRings[0]) {
const flatCoordinates = flatLinearRings[0];
const ends = [flatCoordinates.length];
let i, ii;
for (i = 1, ii = flatLinearRings.length; i < ii; ++i) {
extend(flatCoordinates, flatLinearRings[i]);
ends.push(flatCoordinates.length);
}
return new Polygon(flatCoordinates, GeometryLayout.XYZ, ends);
} else {
return undefined;
}
}
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
* @return {Array.<number>} Flat coordinates.
*/
readFlatCoordinatesFromNode_(node, objectStack) {
return pushParseAndPop(null, this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node, objectStack, this);
}
/**
* @inheritDoc
*/
readGeometryFromNode(node, opt_options) {
const geometry = this.readGeometryElement(node,
[this.getReadOptions(node, opt_options ? opt_options : {})]);
return geometry ? geometry : null;
}
/**
* @inheritDoc
*/
readFeaturesFromNode(node, opt_options) {
const options = {
featureType: this.featureType,
featureNS: this.featureNS
};
if (opt_options) {
assign(options, this.getReadOptions(node, opt_options));
}
const features = this.readFeaturesInternal(node, [options]);
return features || [];
}
/**
* @inheritDoc
*/
readProjectionFromNode(node) {
return getProjection(this.srsName ? this.srsName : node.firstElementChild.getAttribute('srsName'));
}
}
inherits(GMLBase, XMLFeature);
@@ -131,329 +472,6 @@ inherits(GMLBase, XMLFeature);
const ONLY_WHITESPACE_RE = /^[\s\xa0]*$/;
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {Array.<module:ol/Feature> | undefined} Features.
*/
GMLBase.prototype.readFeaturesInternal = function(node, objectStack) {
const localName = node.localName;
let features = null;
if (localName == 'FeatureCollection') {
if (node.namespaceURI === 'http://www.opengis.net/wfs') {
features = pushParseAndPop([],
this.FEATURE_COLLECTION_PARSERS, node,
objectStack, this);
} else {
features = pushParseAndPop(null,
this.FEATURE_COLLECTION_PARSERS, node,
objectStack, this);
}
} else if (localName == 'featureMembers' || localName == 'featureMember') {
const context = objectStack[0];
let featureType = context['featureType'];
let featureNS = context['featureNS'];
const prefix = 'p';
const defaultPrefix = 'p0';
if (!featureType && node.childNodes) {
featureType = [], featureNS = {};
for (let i = 0, ii = node.childNodes.length; i < ii; ++i) {
const child = node.childNodes[i];
if (child.nodeType === 1) {
const ft = child.nodeName.split(':').pop();
if (featureType.indexOf(ft) === -1) {
let key = '';
let count = 0;
const uri = child.namespaceURI;
for (const candidate in featureNS) {
if (featureNS[candidate] === uri) {
key = candidate;
break;
}
++count;
}
if (!key) {
key = prefix + count;
featureNS[key] = uri;
}
featureType.push(key + ':' + ft);
}
}
}
if (localName != 'featureMember') {
// recheck featureType for each featureMember
context['featureType'] = featureType;
context['featureNS'] = featureNS;
}
}
if (typeof featureNS === 'string') {
const ns = featureNS;
featureNS = {};
featureNS[defaultPrefix] = ns;
}
const parsersNS = {};
const featureTypes = Array.isArray(featureType) ? featureType : [featureType];
for (const p in featureNS) {
const parsers = {};
for (let i = 0, ii = featureTypes.length; i < ii; ++i) {
const featurePrefix = featureTypes[i].indexOf(':') === -1 ?
defaultPrefix : featureTypes[i].split(':')[0];
if (featurePrefix === p) {
parsers[featureTypes[i].split(':').pop()] =
(localName == 'featureMembers') ?
makeArrayPusher(this.readFeatureElement, this) :
makeReplacer(this.readFeatureElement, this);
}
}
parsersNS[featureNS[p]] = parsers;
}
if (localName == 'featureMember') {
features = pushParseAndPop(undefined, parsersNS, node, objectStack);
} else {
features = pushParseAndPop([], parsersNS, node, objectStack);
}
}
if (features === null) {
features = [];
}
return features;
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/geom/Geometry|undefined} Geometry.
*/
GMLBase.prototype.readGeometryElement = function(node, objectStack) {
const context = /** @type {Object} */ (objectStack[0]);
context['srsName'] = node.firstElementChild.getAttribute('srsName');
context['srsDimension'] = node.firstElementChild.getAttribute('srsDimension');
/** @type {module:ol/geom/Geometry} */
const geometry = pushParseAndPop(null, this.GEOMETRY_PARSERS_, node, objectStack, this);
if (geometry) {
return (
/** @type {module:ol/geom/Geometry} */ (transformWithOptions(geometry, false, context))
);
} else {
return undefined;
}
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/Feature} Feature.
*/
GMLBase.prototype.readFeatureElement = function(node, objectStack) {
let n;
const fid = node.getAttribute('fid') || getAttributeNS(node, GMLNS, 'id');
const values = {};
let geometryName;
for (n = node.firstElementChild; n; n = n.nextElementSibling) {
const localName = n.localName;
// Assume attribute elements have one child node and that the child
// is a text or CDATA node (to be treated as text).
// Otherwise assume it is a geometry node.
if (n.childNodes.length === 0 ||
(n.childNodes.length === 1 &&
(n.firstChild.nodeType === 3 || n.firstChild.nodeType === 4))) {
let value = getAllTextContent(n, false);
if (ONLY_WHITESPACE_RE.test(value)) {
value = undefined;
}
values[localName] = value;
} else {
// boundedBy is an extent and must not be considered as a geometry
if (localName !== 'boundedBy') {
geometryName = localName;
}
values[localName] = this.readGeometryElement(n, objectStack);
}
}
const feature = new Feature(values);
if (geometryName) {
feature.setGeometryName(geometryName);
}
if (fid) {
feature.setId(fid);
}
return feature;
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/geom/Point|undefined} Point.
*/
GMLBase.prototype.readPoint = function(node, objectStack) {
const flatCoordinates = this.readFlatCoordinatesFromNode_(node, objectStack);
if (flatCoordinates) {
return new Point(flatCoordinates, GeometryLayout.XYZ);
}
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/geom/MultiPoint|undefined} MultiPoint.
*/
GMLBase.prototype.readMultiPoint = function(node, objectStack) {
/** @type {Array.<Array.<number>>} */
const coordinates = pushParseAndPop([],
this.MULTIPOINT_PARSERS_, node, objectStack, this);
if (coordinates) {
return new MultiPoint(coordinates);
} else {
return undefined;
}
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/geom/MultiLineString|undefined} MultiLineString.
*/
GMLBase.prototype.readMultiLineString = function(node, objectStack) {
/** @type {Array.<module:ol/geom/LineString>} */
const lineStrings = pushParseAndPop([],
this.MULTILINESTRING_PARSERS_, node, objectStack, this);
if (lineStrings) {
return new MultiLineString(lineStrings);
}
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/geom/MultiPolygon|undefined} MultiPolygon.
*/
GMLBase.prototype.readMultiPolygon = function(node, objectStack) {
/** @type {Array.<module:ol/geom/Polygon>} */
const polygons = pushParseAndPop([], this.MULTIPOLYGON_PARSERS_, node, objectStack, this);
if (polygons) {
return new MultiPolygon(polygons);
}
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
*/
GMLBase.prototype.pointMemberParser_ = function(node, objectStack) {
parseNode(this.POINTMEMBER_PARSERS_, node, objectStack, this);
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
*/
GMLBase.prototype.lineStringMemberParser_ = function(node, objectStack) {
parseNode(this.LINESTRINGMEMBER_PARSERS_, node, objectStack, this);
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
*/
GMLBase.prototype.polygonMemberParser_ = function(node, objectStack) {
parseNode(this.POLYGONMEMBER_PARSERS_, node, objectStack, this);
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/geom/LineString|undefined} LineString.
*/
GMLBase.prototype.readLineString = function(node, objectStack) {
const flatCoordinates = this.readFlatCoordinatesFromNode_(node, objectStack);
if (flatCoordinates) {
const lineString = new LineString(flatCoordinates, GeometryLayout.XYZ);
return lineString;
} else {
return undefined;
}
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
* @return {Array.<number>|undefined} LinearRing flat coordinates.
*/
GMLBase.prototype.readFlatLinearRing_ = function(node, objectStack) {
const ring = pushParseAndPop(null,
this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node,
objectStack, this);
if (ring) {
return ring;
} else {
return undefined;
}
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/geom/LinearRing|undefined} LinearRing.
*/
GMLBase.prototype.readLinearRing = function(node, objectStack) {
const flatCoordinates = this.readFlatCoordinatesFromNode_(node, objectStack);
if (flatCoordinates) {
return new LinearRing(flatCoordinates, GeometryLayout.XYZ);
}
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {module:ol/geom/Polygon|undefined} Polygon.
*/
GMLBase.prototype.readPolygon = function(node, objectStack) {
/** @type {Array.<Array.<number>>} */
const flatLinearRings = pushParseAndPop([null],
this.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack, this);
if (flatLinearRings && flatLinearRings[0]) {
const flatCoordinates = flatLinearRings[0];
const ends = [flatCoordinates.length];
let i, ii;
for (i = 1, ii = flatLinearRings.length; i < ii; ++i) {
extend(flatCoordinates, flatLinearRings[i]);
ends.push(flatCoordinates.length);
}
return new Polygon(flatCoordinates, GeometryLayout.XYZ, ends);
} else {
return undefined;
}
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
* @return {Array.<number>} Flat coordinates.
*/
GMLBase.prototype.readFlatCoordinatesFromNode_ = function(node, objectStack) {
return pushParseAndPop(null, this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node, objectStack, this);
};
/**
* @const
* @type {Object.<string, Object.<string, module:ol/xml~Parser>>}
@@ -541,16 +559,6 @@ GMLBase.prototype.RING_PARSERS = {
};
/**
* @inheritDoc
*/
GMLBase.prototype.readGeometryFromNode = function(node, opt_options) {
const geometry = this.readGeometryElement(node,
[this.getReadOptions(node, opt_options ? opt_options : {})]);
return geometry ? geometry : null;
};
/**
* Read all features from a GML FeatureCollection.
*
@@ -563,26 +571,4 @@ GMLBase.prototype.readGeometryFromNode = function(node, opt_options) {
GMLBase.prototype.readFeatures;
/**
* @inheritDoc
*/
GMLBase.prototype.readFeaturesFromNode = function(node, opt_options) {
const options = {
featureType: this.featureType,
featureNS: this.featureNS
};
if (opt_options) {
assign(options, this.getReadOptions(node, opt_options));
}
const features = this.readFeaturesInternal(node, [options]);
return features || [];
};
/**
* @inheritDoc
*/
GMLBase.prototype.readProjectionFromNode = function(node) {
return getProjection(this.srsName ? this.srsName : node.firstElementChild.getAttribute('srsName'));
};
export default GMLBase;

View File

@@ -43,23 +43,109 @@ import {createElementNS, makeArrayPusher, makeArraySerializer, makeChildAppender
* @param {module:ol/format/GPX~Options=} opt_options Options.
* @api
*/
const GPX = function(opt_options) {
class GPX {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
XMLFeature.call(this);
XMLFeature.call(this);
/**
* @inheritDoc
*/
this.dataProjection = getProjection('EPSG:4326');
/**
* @type {function(module:ol/Feature, Node)|undefined}
* @private
*/
this.readExtensions_ = options.readExtensions;
}
/**
* @param {Array.<module:ol/Feature>} features List of features.
* @private
*/
handleReadExtensions_(features) {
if (!features) {
features = [];
}
for (let i = 0, ii = features.length; i < ii; ++i) {
const feature = features[i];
if (this.readExtensions_) {
const extensionsNode = feature.get('extensionsNode_') || null;
this.readExtensions_(feature, extensionsNode);
}
feature.set('extensionsNode_', undefined);
}
}
/**
* @inheritDoc
*/
this.dataProjection = getProjection('EPSG:4326');
readFeatureFromNode(node, opt_options) {
if (!includes(NAMESPACE_URIS, node.namespaceURI)) {
return null;
}
const featureReader = FEATURE_READER[node.localName];
if (!featureReader) {
return null;
}
const feature = featureReader(node, [this.getReadOptions(node, opt_options)]);
if (!feature) {
return null;
}
this.handleReadExtensions_([feature]);
return feature;
}
/**
* @type {function(module:ol/Feature, Node)|undefined}
* @private
* @inheritDoc
*/
this.readExtensions_ = options.readExtensions;
};
readFeaturesFromNode(node, opt_options) {
if (!includes(NAMESPACE_URIS, node.namespaceURI)) {
return [];
}
if (node.localName == 'gpx') {
/** @type {Array.<module:ol/Feature>} */
const features = pushParseAndPop([], GPX_PARSERS,
node, [this.getReadOptions(node, opt_options)]);
if (features) {
this.handleReadExtensions_(features);
return features;
} else {
return [];
}
}
return [];
}
/**
* Encode an array of features in the GPX format as an XML node.
* LineString geometries are output as routes (`<rte>`), and MultiLineString
* as tracks (`<trk>`).
*
* @param {Array.<module:ol/Feature>} features Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Options.
* @return {Node} Node.
* @override
* @api
*/
writeFeaturesNode(features, opt_options) {
opt_options = this.adaptOptions(opt_options);
//FIXME Serialize metadata
const gpx = createElementNS('http://www.topografix.com/GPX/1/1', 'gpx');
const xmlnsUri = 'http://www.w3.org/2000/xmlns/';
gpx.setAttributeNS(xmlnsUri, 'xmlns:xsi', XML_SCHEMA_INSTANCE_URI);
gpx.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', SCHEMA_LOCATION);
gpx.setAttribute('version', '1.1');
gpx.setAttribute('creator', 'OpenLayers');
pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */
({node: gpx}), GPX_SERIALIZERS, GPX_NODE_FACTORY, features, [opt_options]);
return gpx;
}
}
inherits(GPX, XMLFeature);
@@ -614,25 +700,6 @@ function readWpt(node, objectStack) {
}
/**
* @param {Array.<module:ol/Feature>} features List of features.
* @private
*/
GPX.prototype.handleReadExtensions_ = function(features) {
if (!features) {
features = [];
}
for (let i = 0, ii = features.length; i < ii; ++i) {
const feature = features[i];
if (this.readExtensions_) {
const extensionsNode = feature.get('extensionsNode_') || null;
this.readExtensions_(feature, extensionsNode);
}
feature.set('extensionsNode_', undefined);
}
};
/**
* Read the first feature from a GPX source.
* Routes (`<rte>`) are converted into LineString geometries, and tracks (`<trk>`)
@@ -647,26 +714,6 @@ GPX.prototype.handleReadExtensions_ = function(features) {
GPX.prototype.readFeature;
/**
* @inheritDoc
*/
GPX.prototype.readFeatureFromNode = function(node, opt_options) {
if (!includes(NAMESPACE_URIS, node.namespaceURI)) {
return null;
}
const featureReader = FEATURE_READER[node.localName];
if (!featureReader) {
return null;
}
const feature = featureReader(node, [this.getReadOptions(node, opt_options)]);
if (!feature) {
return null;
}
this.handleReadExtensions_([feature]);
return feature;
};
/**
* Read all features from a GPX source.
* Routes (`<rte>`) are converted into LineString geometries, and tracks (`<trk>`)
@@ -681,28 +728,6 @@ GPX.prototype.readFeatureFromNode = function(node, opt_options) {
GPX.prototype.readFeatures;
/**
* @inheritDoc
*/
GPX.prototype.readFeaturesFromNode = function(node, opt_options) {
if (!includes(NAMESPACE_URIS, node.namespaceURI)) {
return [];
}
if (node.localName == 'gpx') {
/** @type {Array.<module:ol/Feature>} */
const features = pushParseAndPop([], GPX_PARSERS,
node, [this.getReadOptions(node, opt_options)]);
if (features) {
this.handleReadExtensions_(features);
return features;
} else {
return [];
}
}
return [];
};
/**
* Read the projection from a GPX source.
*
@@ -874,29 +899,4 @@ function writeWpt(node, feature, objectStack) {
GPX.prototype.writeFeatures;
/**
* Encode an array of features in the GPX format as an XML node.
* LineString geometries are output as routes (`<rte>`), and MultiLineString
* as tracks (`<trk>`).
*
* @param {Array.<module:ol/Feature>} features Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Options.
* @return {Node} Node.
* @override
* @api
*/
GPX.prototype.writeFeaturesNode = function(features, opt_options) {
opt_options = this.adaptOptions(opt_options);
//FIXME Serialize metadata
const gpx = createElementNS('http://www.topografix.com/GPX/1/1', 'gpx');
const xmlnsUri = 'http://www.w3.org/2000/xmlns/';
gpx.setAttributeNS(xmlnsUri, 'xmlns:xsi', XML_SCHEMA_INSTANCE_URI);
gpx.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', SCHEMA_LOCATION);
gpx.setAttribute('version', '1.1');
gpx.setAttribute('creator', 'OpenLayers');
pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */
({node: gpx}), GPX_SERIALIZERS, GPX_NODE_FACTORY, features, [opt_options]);
return gpx;
};
export default GPX;

View File

@@ -42,38 +42,191 @@ import {get as getProjection} from '../proj.js';
* @param {module:ol/format/GeoJSON~Options=} opt_options Options.
* @api
*/
const GeoJSON = function(opt_options) {
class GeoJSON {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
JSONFeature.call(this);
JSONFeature.call(this);
/**
* @inheritDoc
*/
this.dataProjection = getProjection(
options.dataProjection ?
options.dataProjection : 'EPSG:4326');
if (options.featureProjection) {
this.defaultFeatureProjection = getProjection(options.featureProjection);
}
/**
* Name of the geometry attribute for features.
* @type {string|undefined}
* @private
*/
this.geometryName_ = options.geometryName;
/**
* Look for the geometry name in the feature GeoJSON
* @type {boolean|undefined}
* @private
*/
this.extractGeometryName_ = options.extractGeometryName;
}
/**
* @inheritDoc
*/
this.dataProjection = getProjection(
options.dataProjection ?
options.dataProjection : 'EPSG:4326');
readFeatureFromObject(object, opt_options) {
/**
* @type {GeoJSONFeature}
*/
let geoJSONFeature = null;
if (object.type === 'Feature') {
geoJSONFeature = /** @type {GeoJSONFeature} */ (object);
} else {
geoJSONFeature = /** @type {GeoJSONFeature} */ ({
type: 'Feature',
geometry: /** @type {GeoJSONGeometry|GeoJSONGeometryCollection} */ (object)
});
}
if (options.featureProjection) {
this.defaultFeatureProjection = getProjection(options.featureProjection);
const geometry = readGeometry(geoJSONFeature.geometry, opt_options);
const feature = new Feature();
if (this.geometryName_) {
feature.setGeometryName(this.geometryName_);
} else if (this.extractGeometryName_ && geoJSONFeature.geometry_name !== undefined) {
feature.setGeometryName(geoJSONFeature.geometry_name);
}
feature.setGeometry(geometry);
if (geoJSONFeature.id !== undefined) {
feature.setId(geoJSONFeature.id);
}
if (geoJSONFeature.properties) {
feature.setProperties(geoJSONFeature.properties);
}
return feature;
}
/**
* Name of the geometry attribute for features.
* @type {string|undefined}
* @private
* @inheritDoc
*/
this.geometryName_ = options.geometryName;
readFeaturesFromObject(object, opt_options) {
const geoJSONObject = /** @type {GeoJSONObject} */ (object);
/** @type {Array.<module:ol/Feature>} */
let features = null;
if (geoJSONObject.type === 'FeatureCollection') {
const geoJSONFeatureCollection = /** @type {GeoJSONFeatureCollection} */ (object);
features = [];
const geoJSONFeatures = geoJSONFeatureCollection.features;
for (let i = 0, ii = geoJSONFeatures.length; i < ii; ++i) {
features.push(this.readFeatureFromObject(geoJSONFeatures[i], opt_options));
}
} else {
features = [this.readFeatureFromObject(object, opt_options)];
}
return features;
}
/**
* Look for the geometry name in the feature GeoJSON
* @type {boolean|undefined}
* @private
* @inheritDoc
*/
this.extractGeometryName_ = options.extractGeometryName;
readGeometryFromObject(object, opt_options) {
return readGeometry(/** @type {GeoJSONGeometry} */ (object), opt_options);
}
};
/**
* @inheritDoc
*/
readProjectionFromObject(object) {
const geoJSONObject = /** @type {GeoJSONObject} */ (object);
const crs = geoJSONObject.crs;
let projection;
if (crs) {
if (crs.type == 'name') {
projection = getProjection(crs.properties.name);
} else {
assert(false, 36); // Unknown SRS type
}
} else {
projection = this.dataProjection;
}
return (
/** @type {module:ol/proj/Projection} */ (projection)
);
}
/**
* Encode a feature as a GeoJSON Feature object.
*
* @param {module:ol/Feature} feature Feature.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {GeoJSONFeature} Object.
* @override
* @api
*/
writeFeatureObject(feature, opt_options) {
opt_options = this.adaptOptions(opt_options);
const object = /** @type {GeoJSONFeature} */ ({
'type': 'Feature'
});
const id = feature.getId();
if (id !== undefined) {
object.id = id;
}
const geometry = feature.getGeometry();
if (geometry) {
object.geometry = writeGeometry(geometry, opt_options);
} else {
object.geometry = null;
}
const properties = feature.getProperties();
delete properties[feature.getGeometryName()];
if (!isEmpty(properties)) {
object.properties = properties;
} else {
object.properties = null;
}
return object;
}
/**
* Encode an array of features as a GeoJSON object.
*
* @param {Array.<module:ol/Feature>} features Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {GeoJSONFeatureCollection} GeoJSON Object.
* @override
* @api
*/
writeFeaturesObject(features, opt_options) {
opt_options = this.adaptOptions(opt_options);
const objects = [];
for (let i = 0, ii = features.length; i < ii; ++i) {
objects.push(this.writeFeatureObject(features[i], opt_options));
}
return /** @type {GeoJSONFeatureCollection} */ ({
type: 'FeatureCollection',
features: objects
});
}
/**
* Encode a geometry as a GeoJSON object.
*
* @param {module:ol/geom/Geometry} geometry Geometry.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {GeoJSONGeometry|GeoJSONGeometryCollection} Object.
* @override
* @api
*/
writeGeometryObject(geometry, opt_options) {
return writeGeometry(geometry, this.adaptOptions(opt_options));
}
}
inherits(GeoJSON, JSONFeature);
@@ -354,62 +507,6 @@ GeoJSON.prototype.readFeature;
GeoJSON.prototype.readFeatures;
/**
* @inheritDoc
*/
GeoJSON.prototype.readFeatureFromObject = function(object, opt_options) {
/**
* @type {GeoJSONFeature}
*/
let geoJSONFeature = null;
if (object.type === 'Feature') {
geoJSONFeature = /** @type {GeoJSONFeature} */ (object);
} else {
geoJSONFeature = /** @type {GeoJSONFeature} */ ({
type: 'Feature',
geometry: /** @type {GeoJSONGeometry|GeoJSONGeometryCollection} */ (object)
});
}
const geometry = readGeometry(geoJSONFeature.geometry, opt_options);
const feature = new Feature();
if (this.geometryName_) {
feature.setGeometryName(this.geometryName_);
} else if (this.extractGeometryName_ && geoJSONFeature.geometry_name !== undefined) {
feature.setGeometryName(geoJSONFeature.geometry_name);
}
feature.setGeometry(geometry);
if (geoJSONFeature.id !== undefined) {
feature.setId(geoJSONFeature.id);
}
if (geoJSONFeature.properties) {
feature.setProperties(geoJSONFeature.properties);
}
return feature;
};
/**
* @inheritDoc
*/
GeoJSON.prototype.readFeaturesFromObject = function(object, opt_options) {
const geoJSONObject = /** @type {GeoJSONObject} */ (object);
/** @type {Array.<module:ol/Feature>} */
let features = null;
if (geoJSONObject.type === 'FeatureCollection') {
const geoJSONFeatureCollection = /** @type {GeoJSONFeatureCollection} */ (object);
features = [];
const geoJSONFeatures = geoJSONFeatureCollection.features;
for (let i = 0, ii = geoJSONFeatures.length; i < ii; ++i) {
features.push(this.readFeatureFromObject(geoJSONFeatures[i], opt_options));
}
} else {
features = [this.readFeatureFromObject(object, opt_options)];
}
return features;
};
/**
* Read a geometry from a GeoJSON source.
*
@@ -422,14 +519,6 @@ GeoJSON.prototype.readFeaturesFromObject = function(object, opt_options) {
GeoJSON.prototype.readGeometry;
/**
* @inheritDoc
*/
GeoJSON.prototype.readGeometryFromObject = function(object, opt_options) {
return readGeometry(/** @type {GeoJSONGeometry} */ (object), opt_options);
};
/**
* Read the projection from a GeoJSON source.
*
@@ -441,28 +530,6 @@ GeoJSON.prototype.readGeometryFromObject = function(object, opt_options) {
GeoJSON.prototype.readProjection;
/**
* @inheritDoc
*/
GeoJSON.prototype.readProjectionFromObject = function(object) {
const geoJSONObject = /** @type {GeoJSONObject} */ (object);
const crs = geoJSONObject.crs;
let projection;
if (crs) {
if (crs.type == 'name') {
projection = getProjection(crs.properties.name);
} else {
assert(false, 36); // Unknown SRS type
}
} else {
projection = this.dataProjection;
}
return (
/** @type {module:ol/proj/Projection} */ (projection)
);
};
/**
* Encode a feature as a GeoJSON Feature string.
*
@@ -476,42 +543,6 @@ GeoJSON.prototype.readProjectionFromObject = function(object) {
GeoJSON.prototype.writeFeature;
/**
* Encode a feature as a GeoJSON Feature object.
*
* @param {module:ol/Feature} feature Feature.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {GeoJSONFeature} Object.
* @override
* @api
*/
GeoJSON.prototype.writeFeatureObject = function(feature, opt_options) {
opt_options = this.adaptOptions(opt_options);
const object = /** @type {GeoJSONFeature} */ ({
'type': 'Feature'
});
const id = feature.getId();
if (id !== undefined) {
object.id = id;
}
const geometry = feature.getGeometry();
if (geometry) {
object.geometry = writeGeometry(geometry, opt_options);
} else {
object.geometry = null;
}
const properties = feature.getProperties();
delete properties[feature.getGeometryName()];
if (!isEmpty(properties)) {
object.properties = properties;
} else {
object.properties = null;
}
return object;
};
/**
* Encode an array of features as GeoJSON.
*
@@ -524,28 +555,6 @@ GeoJSON.prototype.writeFeatureObject = function(feature, opt_options) {
GeoJSON.prototype.writeFeatures;
/**
* Encode an array of features as a GeoJSON object.
*
* @param {Array.<module:ol/Feature>} features Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {GeoJSONFeatureCollection} GeoJSON Object.
* @override
* @api
*/
GeoJSON.prototype.writeFeaturesObject = function(features, opt_options) {
opt_options = this.adaptOptions(opt_options);
const objects = [];
for (let i = 0, ii = features.length; i < ii; ++i) {
objects.push(this.writeFeatureObject(features[i], opt_options));
}
return /** @type {GeoJSONFeatureCollection} */ ({
type: 'FeatureCollection',
features: objects
});
};
/**
* Encode a geometry as a GeoJSON string.
*
@@ -558,16 +567,4 @@ GeoJSON.prototype.writeFeaturesObject = function(features, opt_options) {
GeoJSON.prototype.writeGeometry;
/**
* Encode a geometry as a GeoJSON object.
*
* @param {module:ol/geom/Geometry} geometry Geometry.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {GeoJSONGeometry|GeoJSONGeometryCollection} Object.
* @override
* @api
*/
GeoJSON.prototype.writeGeometryObject = function(geometry, opt_options) {
return writeGeometry(geometry, this.adaptOptions(opt_options));
};
export default GeoJSON;

View File

@@ -36,23 +36,136 @@ const IGCZ = {
* @param {module:ol/format/IGC~Options=} opt_options Options.
* @api
*/
const IGC = function(opt_options) {
class IGC {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
TextFeature.call(this);
TextFeature.call(this);
/**
* @inheritDoc
*/
this.dataProjection = getProjection('EPSG:4326');
/**
* @private
* @type {IGCZ}
*/
this.altitudeMode_ = options.altitudeMode ? options.altitudeMode : IGCZ.NONE;
}
/**
* @inheritDoc
*/
this.dataProjection = getProjection('EPSG:4326');
readFeatureFromText(text, opt_options) {
const altitudeMode = this.altitudeMode_;
const lines = text.split(NEWLINE_RE);
/** @type {Object.<string, string>} */
const properties = {};
const flatCoordinates = [];
let year = 2000;
let month = 0;
let day = 1;
let lastDateTime = -1;
let i, ii;
for (i = 0, ii = lines.length; i < ii; ++i) {
const line = lines[i];
let m;
if (line.charAt(0) == 'B') {
m = B_RECORD_RE.exec(line);
if (m) {
const hour = parseInt(m[1], 10);
const minute = parseInt(m[2], 10);
const second = parseInt(m[3], 10);
let y = parseInt(m[4], 10) + parseInt(m[5], 10) / 60000;
if (m[6] == 'S') {
y = -y;
}
let x = parseInt(m[7], 10) + parseInt(m[8], 10) / 60000;
if (m[9] == 'W') {
x = -x;
}
flatCoordinates.push(x, y);
if (altitudeMode != IGCZ.NONE) {
let z;
if (altitudeMode == IGCZ.GPS) {
z = parseInt(m[11], 10);
} else if (altitudeMode == IGCZ.BAROMETRIC) {
z = parseInt(m[12], 10);
} else {
z = 0;
}
flatCoordinates.push(z);
}
let dateTime = Date.UTC(year, month, day, hour, minute, second);
// Detect UTC midnight wrap around.
if (dateTime < lastDateTime) {
dateTime = Date.UTC(year, month, day + 1, hour, minute, second);
}
flatCoordinates.push(dateTime / 1000);
lastDateTime = dateTime;
}
} else if (line.charAt(0) == 'H') {
m = HFDTE_RECORD_RE.exec(line);
if (m) {
day = parseInt(m[1], 10);
month = parseInt(m[2], 10) - 1;
year = 2000 + parseInt(m[3], 10);
} else {
m = H_RECORD_RE.exec(line);
if (m) {
properties[m[1]] = m[2].trim();
}
}
}
}
if (flatCoordinates.length === 0) {
return null;
}
const layout = altitudeMode == IGCZ.NONE ? GeometryLayout.XYM : GeometryLayout.XYZM;
const lineString = new LineString(flatCoordinates, layout);
const feature = new Feature(transformWithOptions(lineString, false, opt_options));
feature.setProperties(properties);
return feature;
}
/**
* @private
* @type {IGCZ}
* @inheritDoc
*/
this.altitudeMode_ = options.altitudeMode ? options.altitudeMode : IGCZ.NONE;
};
readFeaturesFromText(text, opt_options) {
const feature = this.readFeatureFromText(text, opt_options);
if (feature) {
return [feature];
} else {
return [];
}
}
/**
* Not implemented.
* @inheritDoc
*/
writeFeatureText(feature, opt_options) {}
/**
* Not implemented.
* @inheritDoc
*/
writeFeaturesText(features, opt_options) {}
/**
* Not implemented.
* @inheritDoc
*/
writeGeometryText(geometry, opt_options) {}
/**
* Not implemented.
* @inheritDoc
*/
readGeometryFromText(text, opt_options) {}
}
inherits(IGC, TextFeature);
@@ -100,82 +213,6 @@ const NEWLINE_RE = /\r\n|\r|\n/;
IGC.prototype.readFeature;
/**
* @inheritDoc
*/
IGC.prototype.readFeatureFromText = function(text, opt_options) {
const altitudeMode = this.altitudeMode_;
const lines = text.split(NEWLINE_RE);
/** @type {Object.<string, string>} */
const properties = {};
const flatCoordinates = [];
let year = 2000;
let month = 0;
let day = 1;
let lastDateTime = -1;
let i, ii;
for (i = 0, ii = lines.length; i < ii; ++i) {
const line = lines[i];
let m;
if (line.charAt(0) == 'B') {
m = B_RECORD_RE.exec(line);
if (m) {
const hour = parseInt(m[1], 10);
const minute = parseInt(m[2], 10);
const second = parseInt(m[3], 10);
let y = parseInt(m[4], 10) + parseInt(m[5], 10) / 60000;
if (m[6] == 'S') {
y = -y;
}
let x = parseInt(m[7], 10) + parseInt(m[8], 10) / 60000;
if (m[9] == 'W') {
x = -x;
}
flatCoordinates.push(x, y);
if (altitudeMode != IGCZ.NONE) {
let z;
if (altitudeMode == IGCZ.GPS) {
z = parseInt(m[11], 10);
} else if (altitudeMode == IGCZ.BAROMETRIC) {
z = parseInt(m[12], 10);
} else {
z = 0;
}
flatCoordinates.push(z);
}
let dateTime = Date.UTC(year, month, day, hour, minute, second);
// Detect UTC midnight wrap around.
if (dateTime < lastDateTime) {
dateTime = Date.UTC(year, month, day + 1, hour, minute, second);
}
flatCoordinates.push(dateTime / 1000);
lastDateTime = dateTime;
}
} else if (line.charAt(0) == 'H') {
m = HFDTE_RECORD_RE.exec(line);
if (m) {
day = parseInt(m[1], 10);
month = parseInt(m[2], 10) - 1;
year = 2000 + parseInt(m[3], 10);
} else {
m = H_RECORD_RE.exec(line);
if (m) {
properties[m[1]] = m[2].trim();
}
}
}
}
if (flatCoordinates.length === 0) {
return null;
}
const layout = altitudeMode == IGCZ.NONE ? GeometryLayout.XYM : GeometryLayout.XYZM;
const lineString = new LineString(flatCoordinates, layout);
const feature = new Feature(transformWithOptions(lineString, false, opt_options));
feature.setProperties(properties);
return feature;
};
/**
* Read the feature from the source. As IGC sources contain a single
* feature, this will return the feature in an array.
@@ -189,19 +226,6 @@ IGC.prototype.readFeatureFromText = function(text, opt_options) {
IGC.prototype.readFeatures;
/**
* @inheritDoc
*/
IGC.prototype.readFeaturesFromText = function(text, opt_options) {
const feature = this.readFeatureFromText(text, opt_options);
if (feature) {
return [feature];
} else {
return [];
}
};
/**
* Read the projection from the IGC source.
*
@@ -213,30 +237,4 @@ IGC.prototype.readFeaturesFromText = function(text, opt_options) {
IGC.prototype.readProjection;
/**
* Not implemented.
* @inheritDoc
*/
IGC.prototype.writeFeatureText = function(feature, opt_options) {};
/**
* Not implemented.
* @inheritDoc
*/
IGC.prototype.writeFeaturesText = function(features, opt_options) {};
/**
* Not implemented.
* @inheritDoc
*/
IGC.prototype.writeGeometryText = function(geometry, opt_options) {};
/**
* Not implemented.
* @inheritDoc
*/
IGC.prototype.readGeometryFromText = function(text, opt_options) {};
export default IGC;

View File

@@ -15,9 +15,129 @@ import FormatType from '../format/FormatType.js';
* @abstract
* @extends {module:ol/format/Feature}
*/
const JSONFeature = function() {
FeatureFormat.call(this);
};
class JSONFeature {
constructor() {
FeatureFormat.call(this);
}
/**
* @inheritDoc
*/
getType() {
return FormatType.JSON;
}
/**
* @inheritDoc
*/
readFeature(source, opt_options) {
return this.readFeatureFromObject(
getObject(source), this.getReadOptions(source, opt_options));
}
/**
* @inheritDoc
*/
readFeatures(source, opt_options) {
return this.readFeaturesFromObject(
getObject(source), this.getReadOptions(source, opt_options));
}
/**
* @abstract
* @param {Object} object Object.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @protected
* @return {module:ol/Feature} Feature.
*/
readFeatureFromObject(object, opt_options) {}
/**
* @abstract
* @param {Object} object Object.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @protected
* @return {Array.<module:ol/Feature>} Features.
*/
readFeaturesFromObject(object, opt_options) {}
/**
* @inheritDoc
*/
readGeometry(source, opt_options) {
return this.readGeometryFromObject(
getObject(source), this.getReadOptions(source, opt_options));
}
/**
* @abstract
* @param {Object} object Object.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @protected
* @return {module:ol/geom/Geometry} Geometry.
*/
readGeometryFromObject(object, opt_options) {}
/**
* @inheritDoc
*/
readProjection(source) {
return this.readProjectionFromObject(getObject(source));
}
/**
* @abstract
* @param {Object} object Object.
* @protected
* @return {module:ol/proj/Projection} Projection.
*/
readProjectionFromObject(object) {}
/**
* @inheritDoc
*/
writeFeature(feature, opt_options) {
return JSON.stringify(this.writeFeatureObject(feature, opt_options));
}
/**
* @abstract
* @param {module:ol/Feature} feature Feature.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {Object} Object.
*/
writeFeatureObject(feature, opt_options) {}
/**
* @inheritDoc
*/
writeFeatures(features, opt_options) {
return JSON.stringify(this.writeFeaturesObject(features, opt_options));
}
/**
* @abstract
* @param {Array.<module:ol/Feature>} features Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {Object} Object.
*/
writeFeaturesObject(features, opt_options) {}
/**
* @inheritDoc
*/
writeGeometry(geometry, opt_options) {
return JSON.stringify(this.writeGeometryObject(geometry, opt_options));
}
/**
* @abstract
* @param {module:ol/geom/Geometry} geometry Geometry.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {Object} Object.
*/
writeGeometryObject(geometry, opt_options) {}
}
inherits(JSONFeature, FeatureFormat);
@@ -38,135 +158,4 @@ function getObject(source) {
}
/**
* @inheritDoc
*/
JSONFeature.prototype.getType = function() {
return FormatType.JSON;
};
/**
* @inheritDoc
*/
JSONFeature.prototype.readFeature = function(source, opt_options) {
return this.readFeatureFromObject(
getObject(source), this.getReadOptions(source, opt_options));
};
/**
* @inheritDoc
*/
JSONFeature.prototype.readFeatures = function(source, opt_options) {
return this.readFeaturesFromObject(
getObject(source), this.getReadOptions(source, opt_options));
};
/**
* @abstract
* @param {Object} object Object.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @protected
* @return {module:ol/Feature} Feature.
*/
JSONFeature.prototype.readFeatureFromObject = function(object, opt_options) {};
/**
* @abstract
* @param {Object} object Object.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @protected
* @return {Array.<module:ol/Feature>} Features.
*/
JSONFeature.prototype.readFeaturesFromObject = function(object, opt_options) {};
/**
* @inheritDoc
*/
JSONFeature.prototype.readGeometry = function(source, opt_options) {
return this.readGeometryFromObject(
getObject(source), this.getReadOptions(source, opt_options));
};
/**
* @abstract
* @param {Object} object Object.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @protected
* @return {module:ol/geom/Geometry} Geometry.
*/
JSONFeature.prototype.readGeometryFromObject = function(object, opt_options) {};
/**
* @inheritDoc
*/
JSONFeature.prototype.readProjection = function(source) {
return this.readProjectionFromObject(getObject(source));
};
/**
* @abstract
* @param {Object} object Object.
* @protected
* @return {module:ol/proj/Projection} Projection.
*/
JSONFeature.prototype.readProjectionFromObject = function(object) {};
/**
* @inheritDoc
*/
JSONFeature.prototype.writeFeature = function(feature, opt_options) {
return JSON.stringify(this.writeFeatureObject(feature, opt_options));
};
/**
* @abstract
* @param {module:ol/Feature} feature Feature.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {Object} Object.
*/
JSONFeature.prototype.writeFeatureObject = function(feature, opt_options) {};
/**
* @inheritDoc
*/
JSONFeature.prototype.writeFeatures = function(features, opt_options) {
return JSON.stringify(this.writeFeaturesObject(features, opt_options));
};
/**
* @abstract
* @param {Array.<module:ol/Feature>} features Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {Object} Object.
*/
JSONFeature.prototype.writeFeaturesObject = function(features, opt_options) {};
/**
* @inheritDoc
*/
JSONFeature.prototype.writeGeometry = function(geometry, opt_options) {
return JSON.stringify(this.writeGeometryObject(geometry, opt_options));
};
/**
* @abstract
* @param {module:ol/geom/Geometry} geometry Geometry.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @return {Object} Object.
*/
JSONFeature.prototype.writeGeometryObject = function(geometry, opt_options) {};
export default JSONFeature;

View File

@@ -259,56 +259,456 @@ function createStyleDefaults() {
* @param {module:ol/format/KML~Options=} opt_options Options.
* @api
*/
const KML = function(opt_options) {
class KML {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
XMLFeature.call(this);
XMLFeature.call(this);
if (!DEFAULT_STYLE_ARRAY) {
createStyleDefaults();
if (!DEFAULT_STYLE_ARRAY) {
createStyleDefaults();
}
/**
* @inheritDoc
*/
this.dataProjection = getProjection('EPSG:4326');
/**
* @private
* @type {Array.<module:ol/style/Style>}
*/
this.defaultStyle_ = options.defaultStyle ?
options.defaultStyle : DEFAULT_STYLE_ARRAY;
/**
* @private
* @type {boolean}
*/
this.extractStyles_ = options.extractStyles !== undefined ?
options.extractStyles : true;
/**
* @private
* @type {boolean}
*/
this.writeStyles_ = options.writeStyles !== undefined ?
options.writeStyles : true;
/**
* @private
* @type {!Object.<string, (Array.<module:ol/style/Style>|string)>}
*/
this.sharedStyles_ = {};
/**
* @private
* @type {boolean}
*/
this.showPointNames_ = options.showPointNames !== undefined ?
options.showPointNames : true;
}
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
* @return {Array.<module:ol/Feature>|undefined} Features.
*/
readDocumentOrFolder_(node, objectStack) {
// FIXME use scope somehow
const parsersNS = makeStructureNS(
NAMESPACE_URIS, {
'Document': makeArrayExtender(this.readDocumentOrFolder_, this),
'Folder': makeArrayExtender(this.readDocumentOrFolder_, this),
'Placemark': makeArrayPusher(this.readPlacemark_, this),
'Style': this.readSharedStyle_.bind(this),
'StyleMap': this.readSharedStyleMap_.bind(this)
});
/** @type {Array.<module:ol/Feature>} */
const features = pushParseAndPop([], parsersNS, node, objectStack, this);
if (features) {
return features;
} else {
return undefined;
}
}
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
* @return {module:ol/Feature|undefined} Feature.
*/
readPlacemark_(node, objectStack) {
const object = pushParseAndPop({'geometry': null},
PLACEMARK_PARSERS, node, objectStack);
if (!object) {
return undefined;
}
const feature = new Feature();
const id = node.getAttribute('id');
if (id !== null) {
feature.setId(id);
}
const options = /** @type {module:ol/format/Feature~ReadOptions} */ (objectStack[0]);
const geometry = object['geometry'];
if (geometry) {
transformWithOptions(geometry, false, options);
}
feature.setGeometry(geometry);
delete object['geometry'];
if (this.extractStyles_) {
const style = object['Style'];
const styleUrl = object['styleUrl'];
const styleFunction = createFeatureStyleFunction(
style, styleUrl, this.defaultStyle_, this.sharedStyles_,
this.showPointNames_);
feature.setStyle(styleFunction);
}
delete object['Style'];
// we do not remove the styleUrl property from the object, so it
// gets stored on feature when setProperties is called
feature.setProperties(object);
return feature;
}
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
*/
readSharedStyle_(node, objectStack) {
const id = node.getAttribute('id');
if (id !== null) {
const style = readStyle(node, objectStack);
if (style) {
let styleUri;
let baseURI = node.baseURI;
if (!baseURI || baseURI == 'about:blank') {
baseURI = window.location.href;
}
if (baseURI) {
const url = new URL('#' + id, baseURI);
styleUri = url.href;
} else {
styleUri = '#' + id;
}
this.sharedStyles_[styleUri] = style;
}
}
}
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
*/
readSharedStyleMap_(node, objectStack) {
const id = node.getAttribute('id');
if (id === null) {
return;
}
const styleMapValue = readStyleMapValue(node, objectStack);
if (!styleMapValue) {
return;
}
let styleUri;
let baseURI = node.baseURI;
if (!baseURI || baseURI == 'about:blank') {
baseURI = window.location.href;
}
if (baseURI) {
const url = new URL('#' + id, baseURI);
styleUri = url.href;
} else {
styleUri = '#' + id;
}
this.sharedStyles_[styleUri] = styleMapValue;
}
/**
* @inheritDoc
*/
this.dataProjection = getProjection('EPSG:4326');
readFeatureFromNode(node, opt_options) {
if (!includes(NAMESPACE_URIS, node.namespaceURI)) {
return null;
}
const feature = this.readPlacemark_(
node, [this.getReadOptions(node, opt_options)]);
if (feature) {
return feature;
} else {
return null;
}
}
/**
* @private
* @type {Array.<module:ol/style/Style>}
* @inheritDoc
*/
this.defaultStyle_ = options.defaultStyle ?
options.defaultStyle : DEFAULT_STYLE_ARRAY;
readFeaturesFromNode(node, opt_options) {
if (!includes(NAMESPACE_URIS, node.namespaceURI)) {
return [];
}
let features;
const localName = node.localName;
if (localName == 'Document' || localName == 'Folder') {
features = this.readDocumentOrFolder_(
node, [this.getReadOptions(node, opt_options)]);
if (features) {
return features;
} else {
return [];
}
} else if (localName == 'Placemark') {
const feature = this.readPlacemark_(
node, [this.getReadOptions(node, opt_options)]);
if (feature) {
return [feature];
} else {
return [];
}
} else if (localName == 'kml') {
features = [];
for (let n = node.firstElementChild; n; n = n.nextElementSibling) {
const fs = this.readFeaturesFromNode(n, opt_options);
if (fs) {
extend(features, fs);
}
}
return features;
} else {
return [];
}
}
/**
* @private
* @type {boolean}
* Read the name of the KML.
*
* @param {Document|Node|string} source Source.
* @return {string|undefined} Name.
* @api
*/
this.extractStyles_ = options.extractStyles !== undefined ?
options.extractStyles : true;
readName(source) {
if (isDocument(source)) {
return this.readNameFromDocument(/** @type {Document} */ (source));
} else if (isNode(source)) {
return this.readNameFromNode(/** @type {Node} */ (source));
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readNameFromDocument(doc);
} else {
return undefined;
}
}
/**
* @private
* @type {boolean}
* @param {Document} doc Document.
* @return {string|undefined} Name.
*/
this.writeStyles_ = options.writeStyles !== undefined ?
options.writeStyles : true;
readNameFromDocument(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
const name = this.readNameFromNode(n);
if (name) {
return name;
}
}
}
return undefined;
}
/**
* @private
* @type {!Object.<string, (Array.<module:ol/style/Style>|string)>}
* @param {Node} node Node.
* @return {string|undefined} Name.
*/
this.sharedStyles_ = {};
readNameFromNode(node) {
for (let n = node.firstElementChild; n; n = n.nextElementSibling) {
if (includes(NAMESPACE_URIS, n.namespaceURI) &&
n.localName == 'name') {
return readString(n);
}
}
for (let n = node.firstElementChild; n; n = n.nextElementSibling) {
const localName = n.localName;
if (includes(NAMESPACE_URIS, n.namespaceURI) &&
(localName == 'Document' ||
localName == 'Folder' ||
localName == 'Placemark' ||
localName == 'kml')) {
const name = this.readNameFromNode(n);
if (name) {
return name;
}
}
}
return undefined;
}
/**
* @private
* @type {boolean}
* Read the network links of the KML.
*
* @param {Document|Node|string} source Source.
* @return {Array.<Object>} Network links.
* @api
*/
this.showPointNames_ = options.showPointNames !== undefined ?
options.showPointNames : true;
readNetworkLinks(source) {
const networkLinks = [];
if (isDocument(source)) {
extend(networkLinks, this.readNetworkLinksFromDocument(
/** @type {Document} */ (source)));
} else if (isNode(source)) {
extend(networkLinks, this.readNetworkLinksFromNode(
/** @type {Node} */ (source)));
} else if (typeof source === 'string') {
const doc = parse(source);
extend(networkLinks, this.readNetworkLinksFromDocument(doc));
}
return networkLinks;
}
};
/**
* @param {Document} doc Document.
* @return {Array.<Object>} Network links.
*/
readNetworkLinksFromDocument(doc) {
const networkLinks = [];
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
extend(networkLinks, this.readNetworkLinksFromNode(n));
}
}
return networkLinks;
}
/**
* @param {Node} node Node.
* @return {Array.<Object>} Network links.
*/
readNetworkLinksFromNode(node) {
const networkLinks = [];
for (let n = node.firstElementChild; n; n = n.nextElementSibling) {
if (includes(NAMESPACE_URIS, n.namespaceURI) &&
n.localName == 'NetworkLink') {
const obj = pushParseAndPop({}, NETWORK_LINK_PARSERS,
n, []);
networkLinks.push(obj);
}
}
for (let n = node.firstElementChild; n; n = n.nextElementSibling) {
const localName = n.localName;
if (includes(NAMESPACE_URIS, n.namespaceURI) &&
(localName == 'Document' ||
localName == 'Folder' ||
localName == 'kml')) {
extend(networkLinks, this.readNetworkLinksFromNode(n));
}
}
return networkLinks;
}
/**
* Read the regions of the KML.
*
* @param {Document|Node|string} source Source.
* @return {Array.<Object>} Regions.
* @api
*/
readRegion(source) {
const regions = [];
if (isDocument(source)) {
extend(regions, this.readRegionFromDocument(
/** @type {Document} */ (source)));
} else if (isNode(source)) {
extend(regions, this.readRegionFromNode(
/** @type {Node} */ (source)));
} else if (typeof source === 'string') {
const doc = parse(source);
extend(regions, this.readRegionFromDocument(doc));
}
return regions;
}
/**
* @param {Document} doc Document.
* @return {Array.<Object>} Region.
*/
readRegionFromDocument(doc) {
const regions = [];
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
extend(regions, this.readRegionFromNode(n));
}
}
return regions;
}
/**
* @param {Node} node Node.
* @return {Array.<Object>} Region.
* @api
*/
readRegionFromNode(node) {
const regions = [];
for (let n = node.firstElementChild; n; n = n.nextElementSibling) {
if (includes(NAMESPACE_URIS, n.namespaceURI) &&
n.localName == 'Region') {
const obj = pushParseAndPop({}, REGION_PARSERS,
n, []);
regions.push(obj);
}
}
for (let n = node.firstElementChild; n; n = n.nextElementSibling) {
const localName = n.localName;
if (includes(NAMESPACE_URIS, n.namespaceURI) &&
(localName == 'Document' ||
localName == 'Folder' ||
localName == 'kml')) {
extend(regions, this.readRegionFromNode(n));
}
}
return regions;
}
/**
* Encode an array of features in the KML format as an XML node. GeometryCollections,
* MultiPoints, MultiLineStrings, and MultiPolygons are output as MultiGeometries.
*
* @param {Array.<module:ol/Feature>} features Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Options.
* @return {Node} Node.
* @override
* @api
*/
writeFeaturesNode(features, opt_options) {
opt_options = this.adaptOptions(opt_options);
const kml = createElementNS(NAMESPACE_URIS[4], 'kml');
const xmlnsUri = 'http://www.w3.org/2000/xmlns/';
kml.setAttributeNS(xmlnsUri, 'xmlns:gx', GX_NAMESPACE_URIS[0]);
kml.setAttributeNS(xmlnsUri, 'xmlns:xsi', XML_SCHEMA_INSTANCE_URI);
kml.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', SCHEMA_LOCATION);
const /** @type {module:ol/xml~NodeStackItem} */ context = {node: kml};
const properties = {};
if (features.length > 1) {
properties['Document'] = features;
} else if (features.length == 1) {
properties['Placemark'] = features[0];
}
const orderedKeys = KML_SEQUENCE[kml.namespaceURI];
const values = makeSequence(properties, orderedKeys);
pushSerializeAndPop(context, KML_SERIALIZERS,
OBJECT_PROPERTY_NODE_FACTORY, values, [opt_options], orderedKeys,
this);
return kml;
}
}
inherits(KML, XMLFeature);
@@ -1643,132 +2043,6 @@ const PLACEMARK_PARSERS = makeStructureNS(
));
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
* @return {Array.<module:ol/Feature>|undefined} Features.
*/
KML.prototype.readDocumentOrFolder_ = function(node, objectStack) {
// FIXME use scope somehow
const parsersNS = makeStructureNS(
NAMESPACE_URIS, {
'Document': makeArrayExtender(this.readDocumentOrFolder_, this),
'Folder': makeArrayExtender(this.readDocumentOrFolder_, this),
'Placemark': makeArrayPusher(this.readPlacemark_, this),
'Style': this.readSharedStyle_.bind(this),
'StyleMap': this.readSharedStyleMap_.bind(this)
});
/** @type {Array.<module:ol/Feature>} */
const features = pushParseAndPop([], parsersNS, node, objectStack, this);
if (features) {
return features;
} else {
return undefined;
}
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
* @return {module:ol/Feature|undefined} Feature.
*/
KML.prototype.readPlacemark_ = function(node, objectStack) {
const object = pushParseAndPop({'geometry': null},
PLACEMARK_PARSERS, node, objectStack);
if (!object) {
return undefined;
}
const feature = new Feature();
const id = node.getAttribute('id');
if (id !== null) {
feature.setId(id);
}
const options = /** @type {module:ol/format/Feature~ReadOptions} */ (objectStack[0]);
const geometry = object['geometry'];
if (geometry) {
transformWithOptions(geometry, false, options);
}
feature.setGeometry(geometry);
delete object['geometry'];
if (this.extractStyles_) {
const style = object['Style'];
const styleUrl = object['styleUrl'];
const styleFunction = createFeatureStyleFunction(
style, styleUrl, this.defaultStyle_, this.sharedStyles_,
this.showPointNames_);
feature.setStyle(styleFunction);
}
delete object['Style'];
// we do not remove the styleUrl property from the object, so it
// gets stored on feature when setProperties is called
feature.setProperties(object);
return feature;
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
*/
KML.prototype.readSharedStyle_ = function(node, objectStack) {
const id = node.getAttribute('id');
if (id !== null) {
const style = readStyle(node, objectStack);
if (style) {
let styleUri;
let baseURI = node.baseURI;
if (!baseURI || baseURI == 'about:blank') {
baseURI = window.location.href;
}
if (baseURI) {
const url = new URL('#' + id, baseURI);
styleUri = url.href;
} else {
styleUri = '#' + id;
}
this.sharedStyles_[styleUri] = style;
}
}
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @private
*/
KML.prototype.readSharedStyleMap_ = function(node, objectStack) {
const id = node.getAttribute('id');
if (id === null) {
return;
}
const styleMapValue = readStyleMapValue(node, objectStack);
if (!styleMapValue) {
return;
}
let styleUri;
let baseURI = node.baseURI;
if (!baseURI || baseURI == 'about:blank') {
baseURI = window.location.href;
}
if (baseURI) {
const url = new URL('#' + id, baseURI);
styleUri = url.href;
} else {
styleUri = '#' + id;
}
this.sharedStyles_[styleUri] = styleMapValue;
};
/**
* Read the first feature from a KML source. MultiGeometries are converted into
* GeometryCollections if they are a mix of geometry types, and into MultiPoint/
@@ -1783,23 +2057,6 @@ KML.prototype.readSharedStyleMap_ = function(node, objectStack) {
KML.prototype.readFeature;
/**
* @inheritDoc
*/
KML.prototype.readFeatureFromNode = function(node, opt_options) {
if (!includes(NAMESPACE_URIS, node.namespaceURI)) {
return null;
}
const feature = this.readPlacemark_(
node, [this.getReadOptions(node, opt_options)]);
if (feature) {
return feature;
} else {
return null;
}
};
/**
* Read all features from a KML source. MultiGeometries are converted into
* GeometryCollections if they are a mix of geometry types, and into MultiPoint/
@@ -1814,243 +2071,6 @@ KML.prototype.readFeatureFromNode = function(node, opt_options) {
KML.prototype.readFeatures;
/**
* @inheritDoc
*/
KML.prototype.readFeaturesFromNode = function(node, opt_options) {
if (!includes(NAMESPACE_URIS, node.namespaceURI)) {
return [];
}
let features;
const localName = node.localName;
if (localName == 'Document' || localName == 'Folder') {
features = this.readDocumentOrFolder_(
node, [this.getReadOptions(node, opt_options)]);
if (features) {
return features;
} else {
return [];
}
} else if (localName == 'Placemark') {
const feature = this.readPlacemark_(
node, [this.getReadOptions(node, opt_options)]);
if (feature) {
return [feature];
} else {
return [];
}
} else if (localName == 'kml') {
features = [];
for (let n = node.firstElementChild; n; n = n.nextElementSibling) {
const fs = this.readFeaturesFromNode(n, opt_options);
if (fs) {
extend(features, fs);
}
}
return features;
} else {
return [];
}
};
/**
* Read the name of the KML.
*
* @param {Document|Node|string} source Source.
* @return {string|undefined} Name.
* @api
*/
KML.prototype.readName = function(source) {
if (isDocument(source)) {
return this.readNameFromDocument(/** @type {Document} */ (source));
} else if (isNode(source)) {
return this.readNameFromNode(/** @type {Node} */ (source));
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readNameFromDocument(doc);
} else {
return undefined;
}
};
/**
* @param {Document} doc Document.
* @return {string|undefined} Name.
*/
KML.prototype.readNameFromDocument = function(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
const name = this.readNameFromNode(n);
if (name) {
return name;
}
}
}
return undefined;
};
/**
* @param {Node} node Node.
* @return {string|undefined} Name.
*/
KML.prototype.readNameFromNode = function(node) {
for (let n = node.firstElementChild; n; n = n.nextElementSibling) {
if (includes(NAMESPACE_URIS, n.namespaceURI) &&
n.localName == 'name') {
return readString(n);
}
}
for (let n = node.firstElementChild; n; n = n.nextElementSibling) {
const localName = n.localName;
if (includes(NAMESPACE_URIS, n.namespaceURI) &&
(localName == 'Document' ||
localName == 'Folder' ||
localName == 'Placemark' ||
localName == 'kml')) {
const name = this.readNameFromNode(n);
if (name) {
return name;
}
}
}
return undefined;
};
/**
* Read the network links of the KML.
*
* @param {Document|Node|string} source Source.
* @return {Array.<Object>} Network links.
* @api
*/
KML.prototype.readNetworkLinks = function(source) {
const networkLinks = [];
if (isDocument(source)) {
extend(networkLinks, this.readNetworkLinksFromDocument(
/** @type {Document} */ (source)));
} else if (isNode(source)) {
extend(networkLinks, this.readNetworkLinksFromNode(
/** @type {Node} */ (source)));
} else if (typeof source === 'string') {
const doc = parse(source);
extend(networkLinks, this.readNetworkLinksFromDocument(doc));
}
return networkLinks;
};
/**
* @param {Document} doc Document.
* @return {Array.<Object>} Network links.
*/
KML.prototype.readNetworkLinksFromDocument = function(doc) {
const networkLinks = [];
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
extend(networkLinks, this.readNetworkLinksFromNode(n));
}
}
return networkLinks;
};
/**
* @param {Node} node Node.
* @return {Array.<Object>} Network links.
*/
KML.prototype.readNetworkLinksFromNode = function(node) {
const networkLinks = [];
for (let n = node.firstElementChild; n; n = n.nextElementSibling) {
if (includes(NAMESPACE_URIS, n.namespaceURI) &&
n.localName == 'NetworkLink') {
const obj = pushParseAndPop({}, NETWORK_LINK_PARSERS,
n, []);
networkLinks.push(obj);
}
}
for (let n = node.firstElementChild; n; n = n.nextElementSibling) {
const localName = n.localName;
if (includes(NAMESPACE_URIS, n.namespaceURI) &&
(localName == 'Document' ||
localName == 'Folder' ||
localName == 'kml')) {
extend(networkLinks, this.readNetworkLinksFromNode(n));
}
}
return networkLinks;
};
/**
* Read the regions of the KML.
*
* @param {Document|Node|string} source Source.
* @return {Array.<Object>} Regions.
* @api
*/
KML.prototype.readRegion = function(source) {
const regions = [];
if (isDocument(source)) {
extend(regions, this.readRegionFromDocument(
/** @type {Document} */ (source)));
} else if (isNode(source)) {
extend(regions, this.readRegionFromNode(
/** @type {Node} */ (source)));
} else if (typeof source === 'string') {
const doc = parse(source);
extend(regions, this.readRegionFromDocument(doc));
}
return regions;
};
/**
* @param {Document} doc Document.
* @return {Array.<Object>} Region.
*/
KML.prototype.readRegionFromDocument = function(doc) {
const regions = [];
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
extend(regions, this.readRegionFromNode(n));
}
}
return regions;
};
/**
* @param {Node} node Node.
* @return {Array.<Object>} Region.
* @api
*/
KML.prototype.readRegionFromNode = function(node) {
const regions = [];
for (let n = node.firstElementChild; n; n = n.nextElementSibling) {
if (includes(NAMESPACE_URIS, n.namespaceURI) &&
n.localName == 'Region') {
const obj = pushParseAndPop({}, REGION_PARSERS,
n, []);
regions.push(obj);
}
}
for (let n = node.firstElementChild; n; n = n.nextElementSibling) {
const localName = n.localName;
if (includes(NAMESPACE_URIS, n.namespaceURI) &&
(localName == 'Document' ||
localName == 'Folder' ||
localName == 'kml')) {
extend(regions, this.readRegionFromNode(n));
}
}
return regions;
};
/**
* Read the projection from a KML source.
*
@@ -2955,37 +2975,4 @@ const KML_SERIALIZERS = makeStructureNS(
KML.prototype.writeFeatures;
/**
* Encode an array of features in the KML format as an XML node. GeometryCollections,
* MultiPoints, MultiLineStrings, and MultiPolygons are output as MultiGeometries.
*
* @param {Array.<module:ol/Feature>} features Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Options.
* @return {Node} Node.
* @override
* @api
*/
KML.prototype.writeFeaturesNode = function(features, opt_options) {
opt_options = this.adaptOptions(opt_options);
const kml = createElementNS(NAMESPACE_URIS[4], 'kml');
const xmlnsUri = 'http://www.w3.org/2000/xmlns/';
kml.setAttributeNS(xmlnsUri, 'xmlns:gx', GX_NAMESPACE_URIS[0]);
kml.setAttributeNS(xmlnsUri, 'xmlns:xsi', XML_SCHEMA_INSTANCE_URI);
kml.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', SCHEMA_LOCATION);
const /** @type {module:ol/xml~NodeStackItem} */ context = {node: kml};
const properties = {};
if (features.length > 1) {
properties['Document'] = features;
} else if (features.length == 1) {
properties['Placemark'] = features[0];
}
const orderedKeys = KML_SEQUENCE[kml.namespaceURI];
const values = makeSequence(properties, orderedKeys);
pushSerializeAndPop(context, KML_SERIALIZERS,
OBJECT_PROPERTY_NODE_FACTORY, values, [opt_options], orderedKeys,
this);
return kml;
};
export default KML;

View File

@@ -47,54 +47,276 @@ import RenderFeature from '../render/Feature.js';
* @param {module:ol/format/MVT~Options=} opt_options Options.
* @api
*/
const MVT = function(opt_options) {
class MVT {
constructor(opt_options) {
FeatureFormat.call(this);
FeatureFormat.call(this);
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
/**
* @type {module:ol/proj/Projection}
*/
this.dataProjection = new Projection({
code: '',
units: Units.TILE_PIXELS
});
/**
* @private
* @type {function((module:ol/geom/Geometry|Object.<string,*>)=)|
* function(module:ol/geom/GeometryType,Array.<number>,
* (Array.<number>|Array.<Array.<number>>),Object.<string,*>,number)}
*/
this.featureClass_ = options.featureClass ?
options.featureClass : RenderFeature;
/**
* @private
* @type {string|undefined}
*/
this.geometryName_ = options.geometryName;
/**
* @private
* @type {string}
*/
this.layerName_ = options.layerName ? options.layerName : 'layer';
/**
* @private
* @type {Array.<string>}
*/
this.layers_ = options.layers ? options.layers : null;
/**
* @private
* @type {module:ol/extent~Extent}
*/
this.extent_ = null;
}
/**
* @type {module:ol/proj/Projection}
* Read the raw geometry from the pbf offset stored in a raw feature's geometry
* property.
* @suppress {missingProperties}
* @param {Object} pbf PBF.
* @param {Object} feature Raw feature.
* @param {Array.<number>} flatCoordinates Array to store flat coordinates in.
* @param {Array.<number>} ends Array to store ends in.
* @private
*/
this.dataProjection = new Projection({
code: '',
units: Units.TILE_PIXELS
});
readRawGeometry_(pbf, feature, flatCoordinates, ends) {
pbf.pos = feature.geometry;
const end = pbf.readVarint() + pbf.pos;
let cmd = 1;
let length = 0;
let x = 0;
let y = 0;
let coordsLen = 0;
let currentEnd = 0;
while (pbf.pos < end) {
if (!length) {
const cmdLen = pbf.readVarint();
cmd = cmdLen & 0x7;
length = cmdLen >> 3;
}
length--;
if (cmd === 1 || cmd === 2) {
x += pbf.readSVarint();
y += pbf.readSVarint();
if (cmd === 1) { // moveTo
if (coordsLen > currentEnd) {
ends.push(coordsLen);
currentEnd = coordsLen;
}
}
flatCoordinates.push(x, y);
coordsLen += 2;
} else if (cmd === 7) {
if (coordsLen > currentEnd) {
// close polygon
flatCoordinates.push(
flatCoordinates[currentEnd], flatCoordinates[currentEnd + 1]);
coordsLen += 2;
}
} else {
assert(false, 59); // Invalid command found in the PBF
}
}
if (coordsLen > currentEnd) {
ends.push(coordsLen);
currentEnd = coordsLen;
}
}
/**
* @private
* @type {function((module:ol/geom/Geometry|Object.<string,*>)=)|
* function(module:ol/geom/GeometryType,Array.<number>,
* (Array.<number>|Array.<Array.<number>>),Object.<string,*>,number)}
* @param {Object} pbf PBF
* @param {Object} rawFeature Raw Mapbox feature.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @return {module:ol/Feature|module:ol/render/Feature} Feature.
*/
this.featureClass_ = options.featureClass ?
options.featureClass : RenderFeature;
createFeature_(pbf, rawFeature, opt_options) {
const type = rawFeature.type;
if (type === 0) {
return null;
}
let feature;
const id = rawFeature.id;
const values = rawFeature.properties;
values[this.layerName_] = rawFeature.layer.name;
const flatCoordinates = [];
const ends = [];
this.readRawGeometry_(pbf, rawFeature, flatCoordinates, ends);
const geometryType = getGeometryType(type, ends.length);
if (this.featureClass_ === RenderFeature) {
feature = new this.featureClass_(geometryType, flatCoordinates, ends, values, id);
} else {
let geom;
if (geometryType == GeometryType.POLYGON) {
const endss = [];
let offset = 0;
let prevEndIndex = 0;
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
if (!linearRingIsClockwise(flatCoordinates, offset, end, 2)) {
endss.push(ends.slice(prevEndIndex, i));
prevEndIndex = i;
}
offset = end;
}
if (endss.length > 1) {
geom = new MultiPolygon(flatCoordinates, GeometryLayout.XY, endss);
} else {
geom = new Polygon(flatCoordinates, GeometryLayout.XY, ends);
}
} else {
geom = geometryType === GeometryType.POINT ? new Point(flatCoordinates, GeometryLayout.XY) :
geometryType === GeometryType.LINE_STRING ? new LineString(flatCoordinates, GeometryLayout.XY) :
geometryType === GeometryType.POLYGON ? new Polygon(flatCoordinates, GeometryLayout.XY, ends) :
geometryType === GeometryType.MULTI_POINT ? new MultiPoint(flatCoordinates, GeometryLayout.XY) :
geometryType === GeometryType.MULTI_LINE_STRING ? new MultiLineString(flatCoordinates, GeometryLayout.XY, ends) :
null;
}
feature = new this.featureClass_();
if (this.geometryName_) {
feature.setGeometryName(this.geometryName_);
}
const geometry = transformWithOptions(geom, false, this.adaptOptions(opt_options));
feature.setGeometry(geometry);
feature.setId(id);
feature.setProperties(values);
}
return feature;
}
/**
* @private
* @type {string|undefined}
* @inheritDoc
* @api
*/
this.geometryName_ = options.geometryName;
getLastExtent() {
return this.extent_;
}
/**
* @private
* @type {string}
* @inheritDoc
*/
this.layerName_ = options.layerName ? options.layerName : 'layer';
getType() {
return FormatType.ARRAY_BUFFER;
}
/**
* @private
* @type {Array.<string>}
* @inheritDoc
* @api
*/
this.layers_ = options.layers ? options.layers : null;
readFeatures(source, opt_options) {
const layers = this.layers_;
const pbf = new PBF(/** @type {ArrayBuffer} */ (source));
const pbfLayers = pbf.readFields(layersPBFReader, {});
/** @type {Array.<module:ol/Feature|module:ol/render/Feature>} */
const features = [];
for (const name in pbfLayers) {
if (layers && layers.indexOf(name) == -1) {
continue;
}
const pbfLayer = pbfLayers[name];
for (let i = 0, ii = pbfLayer.length; i < ii; ++i) {
const rawFeature = readRawFeature(pbf, pbfLayer, i);
features.push(this.createFeature_(pbf, rawFeature));
}
this.extent_ = pbfLayer ? [0, 0, pbfLayer.extent, pbfLayer.extent] : null;
}
return features;
}
/**
* @private
* @type {module:ol/extent~Extent}
* @inheritDoc
* @api
*/
this.extent_ = null;
readProjection(source) {
return this.dataProjection;
}
};
/**
* Sets the layers that features will be read from.
* @param {Array.<string>} layers Layers.
* @api
*/
setLayers(layers) {
this.layers_ = layers;
}
/**
* Not implemented.
* @override
*/
readFeature() {}
/**
* Not implemented.
* @override
*/
readGeometry() {}
/**
* Not implemented.
* @override
*/
writeFeature() {}
/**
* Not implemented.
* @override
*/
writeGeometry() {}
/**
* Not implemented.
* @override
*/
writeFeatures() {}
}
inherits(MVT, FeatureFormat);
@@ -201,72 +423,6 @@ function readRawFeature(pbf, layer, i) {
}
/**
* Read the raw geometry from the pbf offset stored in a raw feature's geometry
* property.
* @suppress {missingProperties}
* @param {Object} pbf PBF.
* @param {Object} feature Raw feature.
* @param {Array.<number>} flatCoordinates Array to store flat coordinates in.
* @param {Array.<number>} ends Array to store ends in.
* @private
*/
MVT.prototype.readRawGeometry_ = function(pbf, feature, flatCoordinates, ends) {
pbf.pos = feature.geometry;
const end = pbf.readVarint() + pbf.pos;
let cmd = 1;
let length = 0;
let x = 0;
let y = 0;
let coordsLen = 0;
let currentEnd = 0;
while (pbf.pos < end) {
if (!length) {
const cmdLen = pbf.readVarint();
cmd = cmdLen & 0x7;
length = cmdLen >> 3;
}
length--;
if (cmd === 1 || cmd === 2) {
x += pbf.readSVarint();
y += pbf.readSVarint();
if (cmd === 1) { // moveTo
if (coordsLen > currentEnd) {
ends.push(coordsLen);
currentEnd = coordsLen;
}
}
flatCoordinates.push(x, y);
coordsLen += 2;
} else if (cmd === 7) {
if (coordsLen > currentEnd) {
// close polygon
flatCoordinates.push(
flatCoordinates[currentEnd], flatCoordinates[currentEnd + 1]);
coordsLen += 2;
}
} else {
assert(false, 59); // Invalid command found in the PBF
}
}
if (coordsLen > currentEnd) {
ends.push(coordsLen);
currentEnd = coordsLen;
}
};
/**
* @suppress {missingProperties}
* @param {number} type The raw feature's geometry type
@@ -292,168 +448,4 @@ function getGeometryType(type, numEnds) {
return geometryType;
}
/**
* @private
* @param {Object} pbf PBF
* @param {Object} rawFeature Raw Mapbox feature.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @return {module:ol/Feature|module:ol/render/Feature} Feature.
*/
MVT.prototype.createFeature_ = function(pbf, rawFeature, opt_options) {
const type = rawFeature.type;
if (type === 0) {
return null;
}
let feature;
const id = rawFeature.id;
const values = rawFeature.properties;
values[this.layerName_] = rawFeature.layer.name;
const flatCoordinates = [];
const ends = [];
this.readRawGeometry_(pbf, rawFeature, flatCoordinates, ends);
const geometryType = getGeometryType(type, ends.length);
if (this.featureClass_ === RenderFeature) {
feature = new this.featureClass_(geometryType, flatCoordinates, ends, values, id);
} else {
let geom;
if (geometryType == GeometryType.POLYGON) {
const endss = [];
let offset = 0;
let prevEndIndex = 0;
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
if (!linearRingIsClockwise(flatCoordinates, offset, end, 2)) {
endss.push(ends.slice(prevEndIndex, i));
prevEndIndex = i;
}
offset = end;
}
if (endss.length > 1) {
geom = new MultiPolygon(flatCoordinates, GeometryLayout.XY, endss);
} else {
geom = new Polygon(flatCoordinates, GeometryLayout.XY, ends);
}
} else {
geom = geometryType === GeometryType.POINT ? new Point(flatCoordinates, GeometryLayout.XY) :
geometryType === GeometryType.LINE_STRING ? new LineString(flatCoordinates, GeometryLayout.XY) :
geometryType === GeometryType.POLYGON ? new Polygon(flatCoordinates, GeometryLayout.XY, ends) :
geometryType === GeometryType.MULTI_POINT ? new MultiPoint(flatCoordinates, GeometryLayout.XY) :
geometryType === GeometryType.MULTI_LINE_STRING ? new MultiLineString(flatCoordinates, GeometryLayout.XY, ends) :
null;
}
feature = new this.featureClass_();
if (this.geometryName_) {
feature.setGeometryName(this.geometryName_);
}
const geometry = transformWithOptions(geom, false, this.adaptOptions(opt_options));
feature.setGeometry(geometry);
feature.setId(id);
feature.setProperties(values);
}
return feature;
};
/**
* @inheritDoc
* @api
*/
MVT.prototype.getLastExtent = function() {
return this.extent_;
};
/**
* @inheritDoc
*/
MVT.prototype.getType = function() {
return FormatType.ARRAY_BUFFER;
};
/**
* @inheritDoc
* @api
*/
MVT.prototype.readFeatures = function(source, opt_options) {
const layers = this.layers_;
const pbf = new PBF(/** @type {ArrayBuffer} */ (source));
const pbfLayers = pbf.readFields(layersPBFReader, {});
/** @type {Array.<module:ol/Feature|module:ol/render/Feature>} */
const features = [];
for (const name in pbfLayers) {
if (layers && layers.indexOf(name) == -1) {
continue;
}
const pbfLayer = pbfLayers[name];
for (let i = 0, ii = pbfLayer.length; i < ii; ++i) {
const rawFeature = readRawFeature(pbf, pbfLayer, i);
features.push(this.createFeature_(pbf, rawFeature));
}
this.extent_ = pbfLayer ? [0, 0, pbfLayer.extent, pbfLayer.extent] : null;
}
return features;
};
/**
* @inheritDoc
* @api
*/
MVT.prototype.readProjection = function(source) {
return this.dataProjection;
};
/**
* Sets the layers that features will be read from.
* @param {Array.<string>} layers Layers.
* @api
*/
MVT.prototype.setLayers = function(layers) {
this.layers_ = layers;
};
/**
* Not implemented.
* @override
*/
MVT.prototype.readFeature = function() {};
/**
* Not implemented.
* @override
*/
MVT.prototype.readGeometry = function() {};
/**
* Not implemented.
* @override
*/
MVT.prototype.writeFeature = function() {};
/**
* Not implemented.
* @override
*/
MVT.prototype.writeGeometry = function() {};
/**
* Not implemented.
* @override
*/
MVT.prototype.writeFeatures = function() {};
export default MVT;

View File

@@ -24,14 +24,74 @@ import {pushParseAndPop, makeStructureNS} from '../xml.js';
* @extends {module:ol/format/XMLFeature}
* @api
*/
const OSMXML = function() {
XMLFeature.call(this);
class OSMXML {
constructor() {
XMLFeature.call(this);
/**
* @inheritDoc
*/
this.dataProjection = getProjection('EPSG:4326');
}
/**
* @inheritDoc
*/
this.dataProjection = getProjection('EPSG:4326');
};
readFeaturesFromNode(node, opt_options) {
const options = this.getReadOptions(node, opt_options);
if (node.localName == 'osm') {
const state = pushParseAndPop({
nodes: {},
ways: [],
features: []
}, PARSERS, node, [options]);
// parse nodes in ways
for (let j = 0; j < state.ways.length; j++) {
const values = /** @type {Object} */ (state.ways[j]);
/** @type {Array.<number>} */
const flatCoordinates = [];
for (let i = 0, ii = values.ndrefs.length; i < ii; i++) {
const point = state.nodes[values.ndrefs[i]];
extend(flatCoordinates, point);
}
let geometry;
if (values.ndrefs[0] == values.ndrefs[values.ndrefs.length - 1]) {
// closed way
geometry = new Polygon(flatCoordinates, GeometryLayout.XY, [flatCoordinates.length]);
} else {
geometry = new LineString(flatCoordinates, GeometryLayout.XY);
}
transformWithOptions(geometry, false, options);
const feature = new Feature(geometry);
feature.setId(values.id);
feature.setProperties(values.tags);
state.features.push(feature);
}
if (state.features) {
return state.features;
}
}
return [];
}
/**
* Not implemented.
* @inheritDoc
*/
writeFeatureNode(feature, opt_options) {}
/**
* Not implemented.
* @inheritDoc
*/
writeFeaturesNode(features, opt_options) {}
/**
* Not implemented.
* @inheritDoc
*/
writeGeometryNode(geometry, opt_options) {}
}
inherits(OSMXML, XMLFeature);
@@ -152,47 +212,6 @@ function readTag(node, objectStack) {
OSMXML.prototype.readFeatures;
/**
* @inheritDoc
*/
OSMXML.prototype.readFeaturesFromNode = function(node, opt_options) {
const options = this.getReadOptions(node, opt_options);
if (node.localName == 'osm') {
const state = pushParseAndPop({
nodes: {},
ways: [],
features: []
}, PARSERS, node, [options]);
// parse nodes in ways
for (let j = 0; j < state.ways.length; j++) {
const values = /** @type {Object} */ (state.ways[j]);
/** @type {Array.<number>} */
const flatCoordinates = [];
for (let i = 0, ii = values.ndrefs.length; i < ii; i++) {
const point = state.nodes[values.ndrefs[i]];
extend(flatCoordinates, point);
}
let geometry;
if (values.ndrefs[0] == values.ndrefs[values.ndrefs.length - 1]) {
// closed way
geometry = new Polygon(flatCoordinates, GeometryLayout.XY, [flatCoordinates.length]);
} else {
geometry = new LineString(flatCoordinates, GeometryLayout.XY);
}
transformWithOptions(geometry, false, options);
const feature = new Feature(geometry);
feature.setId(values.id);
feature.setProperties(values.tags);
state.features.push(feature);
}
if (state.features) {
return state.features;
}
}
return [];
};
/**
* Read the projection from an OSM source.
*
@@ -204,23 +223,4 @@ OSMXML.prototype.readFeaturesFromNode = function(node, opt_options) {
OSMXML.prototype.readProjection;
/**
* Not implemented.
* @inheritDoc
*/
OSMXML.prototype.writeFeatureNode = function(feature, opt_options) {};
/**
* Not implemented.
* @inheritDoc
*/
OSMXML.prototype.writeFeaturesNode = function(features, opt_options) {};
/**
* Not implemented.
* @inheritDoc
*/
OSMXML.prototype.writeGeometryNode = function(geometry, opt_options) {};
export default OSMXML;

View File

@@ -11,9 +11,32 @@ import {makeObjectPropertyPusher, makeObjectPropertySetter, makeStructureNS, pus
* @constructor
* @extends {module:ol/format/XML}
*/
const OWS = function() {
XML.call(this);
};
class OWS {
constructor() {
XML.call(this);
}
/**
* @inheritDoc
*/
readFromDocument(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
return this.readFromNode(n);
}
}
return null;
}
/**
* @inheritDoc
*/
readFromNode(node) {
const owsObject = pushParseAndPop({},
PARSERS, node, []);
return owsObject ? owsObject : null;
}
}
inherits(OWS, XML);
@@ -187,29 +210,6 @@ const SERVICE_PROVIDER_PARSERS =
});
/**
* @inheritDoc
*/
OWS.prototype.readFromDocument = function(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
return this.readFromNode(n);
}
}
return null;
};
/**
* @inheritDoc
*/
OWS.prototype.readFromNode = function(node) {
const owsObject = pushParseAndPop({},
PARSERS, node, []);
return owsObject ? owsObject : null;
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.

View File

@@ -32,30 +32,98 @@ import {get as getProjection} from '../proj.js';
* @param {module:ol/format/Polyline~Options=} opt_options Optional configuration object.
* @api
*/
const Polyline = function(opt_options) {
class Polyline {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
TextFeature.call(this);
TextFeature.call(this);
/**
* @inheritDoc
*/
this.dataProjection = getProjection('EPSG:4326');
/**
* @private
* @type {number}
*/
this.factor_ = options.factor ? options.factor : 1e5;
/**
* @private
* @type {module:ol/geom/GeometryLayout}
*/
this.geometryLayout_ = options.geometryLayout ?
options.geometryLayout : GeometryLayout.XY;
}
/**
* @inheritDoc
*/
this.dataProjection = getProjection('EPSG:4326');
readFeatureFromText(text, opt_options) {
const geometry = this.readGeometryFromText(text, opt_options);
return new Feature(geometry);
}
/**
* @private
* @type {number}
* @inheritDoc
*/
this.factor_ = options.factor ? options.factor : 1e5;
readFeaturesFromText(text, opt_options) {
const feature = this.readFeatureFromText(text, opt_options);
return [feature];
}
/**
* @private
* @type {module:ol/geom/GeometryLayout}
* @inheritDoc
*/
this.geometryLayout_ = options.geometryLayout ?
options.geometryLayout : GeometryLayout.XY;
};
readGeometryFromText(text, opt_options) {
const stride = getStrideForLayout(this.geometryLayout_);
const flatCoordinates = decodeDeltas(text, stride, this.factor_);
flipXY(flatCoordinates, 0, flatCoordinates.length, stride, flatCoordinates);
const coordinates = inflateCoordinates(flatCoordinates, 0, flatCoordinates.length, stride);
return (
/** @type {module:ol/geom/Geometry} */ (transformWithOptions(
new LineString(coordinates, this.geometryLayout_),
false,
this.adaptOptions(opt_options)
))
);
}
/**
* @inheritDoc
*/
writeFeatureText(feature, opt_options) {
const geometry = feature.getGeometry();
if (geometry) {
return this.writeGeometryText(geometry, opt_options);
} else {
assert(false, 40); // Expected `feature` to have a geometry
return '';
}
}
/**
* @inheritDoc
*/
writeFeaturesText(features, opt_options) {
return this.writeFeatureText(features[0], opt_options);
}
/**
* @inheritDoc
*/
writeGeometryText(geometry, opt_options) {
geometry = /** @type {module:ol/geom/LineString} */
(transformWithOptions(geometry, true, this.adaptOptions(opt_options)));
const flatCoordinates = geometry.getFlatCoordinates();
const stride = geometry.getStride();
flipXY(flatCoordinates, 0, flatCoordinates.length, stride, flatCoordinates);
return encodeDeltas(flatCoordinates, stride, this.factor_);
}
}
inherits(Polyline, TextFeature);
@@ -277,15 +345,6 @@ export function encodeUnsignedInteger(num) {
Polyline.prototype.readFeature;
/**
* @inheritDoc
*/
Polyline.prototype.readFeatureFromText = function(text, opt_options) {
const geometry = this.readGeometryFromText(text, opt_options);
return new Feature(geometry);
};
/**
* Read the feature from the source. As Polyline sources contain a single
* feature, this will return the feature in an array.
@@ -299,15 +358,6 @@ Polyline.prototype.readFeatureFromText = function(text, opt_options) {
Polyline.prototype.readFeatures;
/**
* @inheritDoc
*/
Polyline.prototype.readFeaturesFromText = function(text, opt_options) {
const feature = this.readFeatureFromText(text, opt_options);
return [feature];
};
/**
* Read the geometry from the source.
*
@@ -320,25 +370,6 @@ Polyline.prototype.readFeaturesFromText = function(text, opt_options) {
Polyline.prototype.readGeometry;
/**
* @inheritDoc
*/
Polyline.prototype.readGeometryFromText = function(text, opt_options) {
const stride = getStrideForLayout(this.geometryLayout_);
const flatCoordinates = decodeDeltas(text, stride, this.factor_);
flipXY(flatCoordinates, 0, flatCoordinates.length, stride, flatCoordinates);
const coordinates = inflateCoordinates(flatCoordinates, 0, flatCoordinates.length, stride);
return (
/** @type {module:ol/geom/Geometry} */ (transformWithOptions(
new LineString(coordinates, this.geometryLayout_),
false,
this.adaptOptions(opt_options)
))
);
};
/**
* Read the projection from a Polyline source.
*
@@ -350,28 +381,6 @@ Polyline.prototype.readGeometryFromText = function(text, opt_options) {
Polyline.prototype.readProjection;
/**
* @inheritDoc
*/
Polyline.prototype.writeFeatureText = function(feature, opt_options) {
const geometry = feature.getGeometry();
if (geometry) {
return this.writeGeometryText(geometry, opt_options);
} else {
assert(false, 40); // Expected `feature` to have a geometry
return '';
}
};
/**
* @inheritDoc
*/
Polyline.prototype.writeFeaturesText = function(features, opt_options) {
return this.writeFeatureText(features[0], opt_options);
};
/**
* Write a single geometry in Polyline format.
*
@@ -384,15 +393,4 @@ Polyline.prototype.writeFeaturesText = function(features, opt_options) {
Polyline.prototype.writeGeometry;
/**
* @inheritDoc
*/
Polyline.prototype.writeGeometryText = function(geometry, opt_options) {
geometry = /** @type {module:ol/geom/LineString} */
(transformWithOptions(geometry, true, this.adaptOptions(opt_options)));
const flatCoordinates = geometry.getFlatCoordinates();
const stride = geometry.getStride();
flipXY(flatCoordinates, 0, flatCoordinates.length, stride, flatCoordinates);
return encodeDeltas(flatCoordinates, stride, this.factor_);
};
export default Polyline;

View File

@@ -15,9 +15,130 @@ import FormatType from '../format/FormatType.js';
* @abstract
* @extends {module:ol/format/Feature}
*/
const TextFeature = function() {
FeatureFormat.call(this);
};
class TextFeature {
constructor() {
FeatureFormat.call(this);
}
/**
* @inheritDoc
*/
getType() {
return FormatType.TEXT;
}
/**
* @inheritDoc
*/
readFeature(source, opt_options) {
return this.readFeatureFromText(getText(source), this.adaptOptions(opt_options));
}
/**
* @abstract
* @param {string} text Text.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @protected
* @return {module:ol/Feature} Feature.
*/
readFeatureFromText(text, opt_options) {}
/**
* @inheritDoc
*/
readFeatures(source, opt_options) {
return this.readFeaturesFromText(getText(source), this.adaptOptions(opt_options));
}
/**
* @abstract
* @param {string} text Text.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @protected
* @return {Array.<module:ol/Feature>} Features.
*/
readFeaturesFromText(text, opt_options) {}
/**
* @inheritDoc
*/
readGeometry(source, opt_options) {
return this.readGeometryFromText(getText(source), this.adaptOptions(opt_options));
}
/**
* @abstract
* @param {string} text Text.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @protected
* @return {module:ol/geom/Geometry} Geometry.
*/
readGeometryFromText(text, opt_options) {}
/**
* @inheritDoc
*/
readProjection(source) {
return this.readProjectionFromText(getText(source));
}
/**
* @param {string} text Text.
* @protected
* @return {module:ol/proj/Projection} Projection.
*/
readProjectionFromText(text) {
return this.dataProjection;
}
/**
* @inheritDoc
*/
writeFeature(feature, opt_options) {
return this.writeFeatureText(feature, this.adaptOptions(opt_options));
}
/**
* @abstract
* @param {module:ol/Feature} feature Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @protected
* @return {string} Text.
*/
writeFeatureText(feature, opt_options) {}
/**
* @inheritDoc
*/
writeFeatures(features, opt_options) {
return this.writeFeaturesText(features, this.adaptOptions(opt_options));
}
/**
* @abstract
* @param {Array.<module:ol/Feature>} features Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @protected
* @return {string} Text.
*/
writeFeaturesText(features, opt_options) {}
/**
* @inheritDoc
*/
writeGeometry(geometry, opt_options) {
return this.writeGeometryText(geometry, this.adaptOptions(opt_options));
}
/**
* @abstract
* @param {module:ol/geom/Geometry} geometry Geometry.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @protected
* @return {string} Text.
*/
writeGeometryText(geometry, opt_options) {}
}
inherits(TextFeature, FeatureFormat);
@@ -35,136 +156,4 @@ function getText(source) {
}
/**
* @inheritDoc
*/
TextFeature.prototype.getType = function() {
return FormatType.TEXT;
};
/**
* @inheritDoc
*/
TextFeature.prototype.readFeature = function(source, opt_options) {
return this.readFeatureFromText(getText(source), this.adaptOptions(opt_options));
};
/**
* @abstract
* @param {string} text Text.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @protected
* @return {module:ol/Feature} Feature.
*/
TextFeature.prototype.readFeatureFromText = function(text, opt_options) {};
/**
* @inheritDoc
*/
TextFeature.prototype.readFeatures = function(source, opt_options) {
return this.readFeaturesFromText(getText(source), this.adaptOptions(opt_options));
};
/**
* @abstract
* @param {string} text Text.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @protected
* @return {Array.<module:ol/Feature>} Features.
*/
TextFeature.prototype.readFeaturesFromText = function(text, opt_options) {};
/**
* @inheritDoc
*/
TextFeature.prototype.readGeometry = function(source, opt_options) {
return this.readGeometryFromText(getText(source), this.adaptOptions(opt_options));
};
/**
* @abstract
* @param {string} text Text.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Read options.
* @protected
* @return {module:ol/geom/Geometry} Geometry.
*/
TextFeature.prototype.readGeometryFromText = function(text, opt_options) {};
/**
* @inheritDoc
*/
TextFeature.prototype.readProjection = function(source) {
return this.readProjectionFromText(getText(source));
};
/**
* @param {string} text Text.
* @protected
* @return {module:ol/proj/Projection} Projection.
*/
TextFeature.prototype.readProjectionFromText = function(text) {
return this.dataProjection;
};
/**
* @inheritDoc
*/
TextFeature.prototype.writeFeature = function(feature, opt_options) {
return this.writeFeatureText(feature, this.adaptOptions(opt_options));
};
/**
* @abstract
* @param {module:ol/Feature} feature Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @protected
* @return {string} Text.
*/
TextFeature.prototype.writeFeatureText = function(feature, opt_options) {};
/**
* @inheritDoc
*/
TextFeature.prototype.writeFeatures = function(features, opt_options) {
return this.writeFeaturesText(features, this.adaptOptions(opt_options));
};
/**
* @abstract
* @param {Array.<module:ol/Feature>} features Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @protected
* @return {string} Text.
*/
TextFeature.prototype.writeFeaturesText = function(features, opt_options) {};
/**
* @inheritDoc
*/
TextFeature.prototype.writeGeometry = function(geometry, opt_options) {
return this.writeGeometryText(geometry, this.adaptOptions(opt_options));
};
/**
* @abstract
* @param {module:ol/geom/Geometry} geometry Geometry.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Write options.
* @protected
* @return {string} Text.
*/
TextFeature.prototype.writeGeometryText = function(geometry, opt_options) {};
export default TextFeature;

View File

@@ -48,32 +48,112 @@ import {get as getProjection} from '../proj.js';
* @param {module:ol/format/TopoJSON~Options=} opt_options Options.
* @api
*/
const TopoJSON = function(opt_options) {
class TopoJSON {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
JSONFeature.call(this);
JSONFeature.call(this);
/**
* @private
* @type {string|undefined}
*/
this.layerName_ = options.layerName;
/**
* @private
* @type {string|undefined}
*/
this.layerName_ = options.layerName;
/**
* @private
* @type {Array.<string>}
*/
this.layers_ = options.layers ? options.layers : null;
/**
* @private
* @type {Array.<string>}
*/
this.layers_ = options.layers ? options.layers : null;
/**
* @inheritDoc
*/
this.dataProjection = getProjection(
options.dataProjection ?
options.dataProjection : 'EPSG:4326');
}
/**
* @inheritDoc
*/
this.dataProjection = getProjection(
options.dataProjection ?
options.dataProjection : 'EPSG:4326');
readFeaturesFromObject(object, opt_options) {
if (object.type == 'Topology') {
const topoJSONTopology = /** @type {TopoJSONTopology} */ (object);
let transform, scale = null, translate = null;
if (topoJSONTopology.transform) {
transform = topoJSONTopology.transform;
scale = transform.scale;
translate = transform.translate;
}
const arcs = topoJSONTopology.arcs;
if (transform) {
transformArcs(arcs, scale, translate);
}
/** @type {Array.<module:ol/Feature>} */
const features = [];
const topoJSONFeatures = topoJSONTopology.objects;
const property = this.layerName_;
let feature;
for (const objectName in topoJSONFeatures) {
if (this.layers_ && this.layers_.indexOf(objectName) == -1) {
continue;
}
if (topoJSONFeatures[objectName].type === 'GeometryCollection') {
feature = /** @type {TopoJSONGeometryCollection} */ (topoJSONFeatures[objectName]);
features.push.apply(features, readFeaturesFromGeometryCollection(
feature, arcs, scale, translate, property, objectName, opt_options));
} else {
feature = /** @type {TopoJSONGeometry} */ (topoJSONFeatures[objectName]);
features.push(readFeatureFromGeometry(
feature, arcs, scale, translate, property, objectName, opt_options));
}
}
return features;
} else {
return [];
}
}
};
/**
* @inheritDoc
*/
readProjectionFromObject(object) {
return this.dataProjection;
}
/**
* Not implemented.
* @inheritDoc
*/
writeFeatureObject(feature, opt_options) {}
/**
* Not implemented.
* @inheritDoc
*/
writeFeaturesObject(features, opt_options) {}
/**
* Not implemented.
* @inheritDoc
*/
writeGeometryObject(geometry, opt_options) {}
/**
* Not implemented.
* @override
*/
readGeometryFromObject() {}
/**
* Not implemented.
* @override
*/
readFeatureFromObject() {}
}
inherits(TopoJSON, JSONFeature);
@@ -309,48 +389,6 @@ function readFeatureFromGeometry(object, arcs, scale, translate, property, name,
TopoJSON.prototype.readFeatures;
/**
* @inheritDoc
*/
TopoJSON.prototype.readFeaturesFromObject = function(object, opt_options) {
if (object.type == 'Topology') {
const topoJSONTopology = /** @type {TopoJSONTopology} */ (object);
let transform, scale = null, translate = null;
if (topoJSONTopology.transform) {
transform = topoJSONTopology.transform;
scale = transform.scale;
translate = transform.translate;
}
const arcs = topoJSONTopology.arcs;
if (transform) {
transformArcs(arcs, scale, translate);
}
/** @type {Array.<module:ol/Feature>} */
const features = [];
const topoJSONFeatures = topoJSONTopology.objects;
const property = this.layerName_;
let feature;
for (const objectName in topoJSONFeatures) {
if (this.layers_ && this.layers_.indexOf(objectName) == -1) {
continue;
}
if (topoJSONFeatures[objectName].type === 'GeometryCollection') {
feature = /** @type {TopoJSONGeometryCollection} */ (topoJSONFeatures[objectName]);
features.push.apply(features, readFeaturesFromGeometryCollection(
feature, arcs, scale, translate, property, objectName, opt_options));
} else {
feature = /** @type {TopoJSONGeometry} */ (topoJSONFeatures[objectName]);
features.push(readFeatureFromGeometry(
feature, arcs, scale, translate, property, objectName, opt_options));
}
}
return features;
} else {
return [];
}
};
/**
* Apply a linear transform to array of arcs. The provided array of arcs is
* modified in place.
@@ -412,45 +450,4 @@ function transformVertex(vertex, scale, translate) {
TopoJSON.prototype.readProjection;
/**
* @inheritDoc
*/
TopoJSON.prototype.readProjectionFromObject = function(object) {
return this.dataProjection;
};
/**
* Not implemented.
* @inheritDoc
*/
TopoJSON.prototype.writeFeatureObject = function(feature, opt_options) {};
/**
* Not implemented.
* @inheritDoc
*/
TopoJSON.prototype.writeFeaturesObject = function(features, opt_options) {};
/**
* Not implemented.
* @inheritDoc
*/
TopoJSON.prototype.writeGeometryObject = function(geometry, opt_options) {};
/**
* Not implemented.
* @override
*/
TopoJSON.prototype.readGeometryFromObject = function() {};
/**
* Not implemented.
* @override
*/
TopoJSON.prototype.readFeatureFromObject = function() {};
export default TopoJSON;

View File

@@ -143,57 +143,338 @@ const DEFAULT_VERSION = '1.1.0';
* @extends {module:ol/format/XMLFeature}
* @api
*/
const WFS = function(opt_options) {
const options = opt_options ? opt_options : {};
class WFS {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
/**
* @private
* @type {Array.<string>|string|undefined}
*/
this.featureType_ = options.featureType;
/**
* @private
* @type {Object.<string, string>|string|undefined}
*/
this.featureNS_ = options.featureNS;
/**
* @private
* @type {module:ol/format/GMLBase}
*/
this.gmlFormat_ = options.gmlFormat ?
options.gmlFormat : new GML3();
/**
* @private
* @type {string}
*/
this.schemaLocation_ = options.schemaLocation ?
options.schemaLocation : SCHEMA_LOCATIONS[DEFAULT_VERSION];
XMLFeature.call(this);
}
/**
* @private
* @type {Array.<string>|string|undefined}
* @return {Array.<string>|string|undefined} featureType
*/
this.featureType_ = options.featureType;
getFeatureType() {
return this.featureType_;
}
/**
* @private
* @type {Object.<string, string>|string|undefined}
* @param {Array.<string>|string|undefined} featureType Feature type(s) to parse.
*/
this.featureNS_ = options.featureNS;
setFeatureType(featureType) {
this.featureType_ = featureType;
}
/**
* @private
* @type {module:ol/format/GMLBase}
* @inheritDoc
*/
this.gmlFormat_ = options.gmlFormat ?
options.gmlFormat : new GML3();
readFeaturesFromNode(node, opt_options) {
const context = /** @type {module:ol/xml~NodeStackItem} */ ({
'featureType': this.featureType_,
'featureNS': this.featureNS_
});
assign(context, this.getReadOptions(node, opt_options ? opt_options : {}));
const objectStack = [context];
this.gmlFormat_.FEATURE_COLLECTION_PARSERS[GMLNS][
'featureMember'] =
makeArrayPusher(GMLBase.prototype.readFeaturesInternal);
let features = pushParseAndPop([],
this.gmlFormat_.FEATURE_COLLECTION_PARSERS, node,
objectStack, this.gmlFormat_);
if (!features) {
features = [];
}
return features;
}
/**
* @private
* @type {string}
* Read transaction response of the source.
*
* @param {Document|Node|Object|string} source Source.
* @return {module:ol/format/WFS~TransactionResponse|undefined} Transaction response.
* @api
*/
this.schemaLocation_ = options.schemaLocation ?
options.schemaLocation : SCHEMA_LOCATIONS[DEFAULT_VERSION];
readTransactionResponse(source) {
if (isDocument(source)) {
return this.readTransactionResponseFromDocument(
/** @type {Document} */ (source));
} else if (isNode(source)) {
return this.readTransactionResponseFromNode(/** @type {Node} */ (source));
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readTransactionResponseFromDocument(doc);
} else {
return undefined;
}
}
XMLFeature.call(this);
};
/**
* Read feature collection metadata of the source.
*
* @param {Document|Node|Object|string} source Source.
* @return {module:ol/format/WFS~FeatureCollectionMetadata|undefined}
* FeatureCollection metadata.
* @api
*/
readFeatureCollectionMetadata(source) {
if (isDocument(source)) {
return this.readFeatureCollectionMetadataFromDocument(
/** @type {Document} */ (source));
} else if (isNode(source)) {
return this.readFeatureCollectionMetadataFromNode(
/** @type {Node} */ (source));
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readFeatureCollectionMetadataFromDocument(doc);
} else {
return undefined;
}
}
/**
* @param {Document} doc Document.
* @return {module:ol/format/WFS~FeatureCollectionMetadata|undefined}
* FeatureCollection metadata.
*/
readFeatureCollectionMetadataFromDocument(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
return this.readFeatureCollectionMetadataFromNode(n);
}
}
return undefined;
}
/**
* @param {Node} node Node.
* @return {module:ol/format/WFS~FeatureCollectionMetadata|undefined}
* FeatureCollection metadata.
*/
readFeatureCollectionMetadataFromNode(node) {
const result = {};
const value = readNonNegativeIntegerString(
node.getAttribute('numberOfFeatures'));
result['numberOfFeatures'] = value;
return pushParseAndPop(
/** @type {module:ol/format/WFS~FeatureCollectionMetadata} */ (result),
FEATURE_COLLECTION_PARSERS, node, [], this.gmlFormat_);
}
/**
* @param {Document} doc Document.
* @return {module:ol/format/WFS~TransactionResponse|undefined} Transaction response.
*/
readTransactionResponseFromDocument(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
return this.readTransactionResponseFromNode(n);
}
}
return undefined;
}
/**
* @param {Node} node Node.
* @return {module:ol/format/WFS~TransactionResponse|undefined} Transaction response.
*/
readTransactionResponseFromNode(node) {
return pushParseAndPop(
/** @type {module:ol/format/WFS~TransactionResponse} */({}),
TRANSACTION_RESPONSE_PARSERS, node, []);
}
/**
* Encode format as WFS `GetFeature` and return the Node.
*
* @param {module:ol/format/WFS~WriteGetFeatureOptions} options Options.
* @return {Node} Result.
* @api
*/
writeGetFeature(options) {
const node = createElementNS(WFSNS, 'GetFeature');
node.setAttribute('service', 'WFS');
node.setAttribute('version', '1.1.0');
let filter;
if (options) {
if (options.handle) {
node.setAttribute('handle', options.handle);
}
if (options.outputFormat) {
node.setAttribute('outputFormat', options.outputFormat);
}
if (options.maxFeatures !== undefined) {
node.setAttribute('maxFeatures', options.maxFeatures);
}
if (options.resultType) {
node.setAttribute('resultType', options.resultType);
}
if (options.startIndex !== undefined) {
node.setAttribute('startIndex', options.startIndex);
}
if (options.count !== undefined) {
node.setAttribute('count', options.count);
}
filter = options.filter;
if (options.bbox) {
assert(options.geometryName,
12); // `options.geometryName` must also be provided when `options.bbox` is set
const bbox = bboxFilter(
/** @type {string} */ (options.geometryName), options.bbox, options.srsName);
if (filter) {
// if bbox and filter are both set, combine the two into a single filter
filter = andFilter(filter, bbox);
} else {
filter = bbox;
}
}
}
node.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', this.schemaLocation_);
/** @type {module:ol/xml~NodeStackItem} */
const context = {
node: node,
'srsName': options.srsName,
'featureNS': options.featureNS ? options.featureNS : this.featureNS_,
'featurePrefix': options.featurePrefix,
'geometryName': options.geometryName,
'filter': filter,
'propertyNames': options.propertyNames ? options.propertyNames : []
};
assert(Array.isArray(options.featureTypes),
11); // `options.featureTypes` should be an Array
writeGetFeature(node, /** @type {!Array.<string>} */ (options.featureTypes), [context]);
return node;
}
/**
* Encode format as WFS `Transaction` and return the Node.
*
* @param {Array.<module:ol/Feature>} inserts The features to insert.
* @param {Array.<module:ol/Feature>} updates The features to update.
* @param {Array.<module:ol/Feature>} deletes The features to delete.
* @param {module:ol/format/WFS~WriteTransactionOptions} options Write options.
* @return {Node} Result.
* @api
*/
writeTransaction(inserts, updates, deletes, options) {
const objectStack = [];
const node = createElementNS(WFSNS, 'Transaction');
const version = options.version ? options.version : DEFAULT_VERSION;
const gmlVersion = version === '1.0.0' ? 2 : 3;
node.setAttribute('service', 'WFS');
node.setAttribute('version', version);
let baseObj;
/** @type {module:ol/xml~NodeStackItem} */
let obj;
if (options) {
baseObj = options.gmlOptions ? options.gmlOptions : {};
if (options.handle) {
node.setAttribute('handle', options.handle);
}
}
const schemaLocation = SCHEMA_LOCATIONS[version];
node.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', schemaLocation);
const featurePrefix = options.featurePrefix ? options.featurePrefix : FEATURE_PREFIX;
if (inserts) {
obj = {node: node, 'featureNS': options.featureNS,
'featureType': options.featureType, 'featurePrefix': featurePrefix,
'gmlVersion': gmlVersion, 'hasZ': options.hasZ, 'srsName': options.srsName};
assign(obj, baseObj);
pushSerializeAndPop(obj,
TRANSACTION_SERIALIZERS,
makeSimpleNodeFactory('Insert'), inserts,
objectStack);
}
if (updates) {
obj = {node: node, 'featureNS': options.featureNS,
'featureType': options.featureType, 'featurePrefix': featurePrefix,
'gmlVersion': gmlVersion, 'hasZ': options.hasZ, 'srsName': options.srsName};
assign(obj, baseObj);
pushSerializeAndPop(obj,
TRANSACTION_SERIALIZERS,
makeSimpleNodeFactory('Update'), updates,
objectStack);
}
if (deletes) {
pushSerializeAndPop({node: node, 'featureNS': options.featureNS,
'featureType': options.featureType, 'featurePrefix': featurePrefix,
'gmlVersion': gmlVersion, 'srsName': options.srsName},
TRANSACTION_SERIALIZERS,
makeSimpleNodeFactory('Delete'), deletes,
objectStack);
}
if (options.nativeElements) {
pushSerializeAndPop({node: node, 'featureNS': options.featureNS,
'featureType': options.featureType, 'featurePrefix': featurePrefix,
'gmlVersion': gmlVersion, 'srsName': options.srsName},
TRANSACTION_SERIALIZERS,
makeSimpleNodeFactory('Native'), options.nativeElements,
objectStack);
}
return node;
}
/**
* @inheritDoc
*/
readProjectionFromDocument(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
return this.readProjectionFromNode(n);
}
}
return null;
}
/**
* @inheritDoc
*/
readProjectionFromNode(node) {
if (node.firstElementChild &&
node.firstElementChild.firstElementChild) {
node = node.firstElementChild.firstElementChild;
for (let n = node.firstElementChild; n; n = n.nextElementSibling) {
if (!(n.childNodes.length === 0 ||
(n.childNodes.length === 1 &&
n.firstChild.nodeType === 3))) {
const objectStack = [{}];
this.gmlFormat_.readGeometryElement(n, objectStack);
return getProjection(objectStack.pop().srsName);
}
}
}
return null;
}
}
inherits(WFS, XMLFeature);
/**
* @return {Array.<string>|string|undefined} featureType
*/
WFS.prototype.getFeatureType = function() {
return this.featureType_;
};
/**
* @param {Array.<string>|string|undefined} featureType Feature type(s) to parse.
*/
WFS.prototype.setFeatureType = function(featureType) {
this.featureType_ = featureType;
};
/**
* Read all features from a WFS FeatureCollection.
*
@@ -206,90 +487,6 @@ WFS.prototype.setFeatureType = function(featureType) {
WFS.prototype.readFeatures;
/**
* @inheritDoc
*/
WFS.prototype.readFeaturesFromNode = function(node, opt_options) {
const context = /** @type {module:ol/xml~NodeStackItem} */ ({
'featureType': this.featureType_,
'featureNS': this.featureNS_
});
assign(context, this.getReadOptions(node, opt_options ? opt_options : {}));
const objectStack = [context];
this.gmlFormat_.FEATURE_COLLECTION_PARSERS[GMLNS][
'featureMember'] =
makeArrayPusher(GMLBase.prototype.readFeaturesInternal);
let features = pushParseAndPop([],
this.gmlFormat_.FEATURE_COLLECTION_PARSERS, node,
objectStack, this.gmlFormat_);
if (!features) {
features = [];
}
return features;
};
/**
* Read transaction response of the source.
*
* @param {Document|Node|Object|string} source Source.
* @return {module:ol/format/WFS~TransactionResponse|undefined} Transaction response.
* @api
*/
WFS.prototype.readTransactionResponse = function(source) {
if (isDocument(source)) {
return this.readTransactionResponseFromDocument(
/** @type {Document} */ (source));
} else if (isNode(source)) {
return this.readTransactionResponseFromNode(/** @type {Node} */ (source));
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readTransactionResponseFromDocument(doc);
} else {
return undefined;
}
};
/**
* Read feature collection metadata of the source.
*
* @param {Document|Node|Object|string} source Source.
* @return {module:ol/format/WFS~FeatureCollectionMetadata|undefined}
* FeatureCollection metadata.
* @api
*/
WFS.prototype.readFeatureCollectionMetadata = function(source) {
if (isDocument(source)) {
return this.readFeatureCollectionMetadataFromDocument(
/** @type {Document} */ (source));
} else if (isNode(source)) {
return this.readFeatureCollectionMetadataFromNode(
/** @type {Node} */ (source));
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readFeatureCollectionMetadataFromDocument(doc);
} else {
return undefined;
}
};
/**
* @param {Document} doc Document.
* @return {module:ol/format/WFS~FeatureCollectionMetadata|undefined}
* FeatureCollection metadata.
*/
WFS.prototype.readFeatureCollectionMetadataFromDocument = function(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
return this.readFeatureCollectionMetadataFromNode(n);
}
}
return undefined;
};
/**
* @const
* @type {Object.<string, Object.<string, module:ol/xml~Parser>>}
@@ -302,22 +499,6 @@ const FEATURE_COLLECTION_PARSERS = {
};
/**
* @param {Node} node Node.
* @return {module:ol/format/WFS~FeatureCollectionMetadata|undefined}
* FeatureCollection metadata.
*/
WFS.prototype.readFeatureCollectionMetadataFromNode = function(node) {
const result = {};
const value = readNonNegativeIntegerString(
node.getAttribute('numberOfFeatures'));
result['numberOfFeatures'] = value;
return pushParseAndPop(
/** @type {module:ol/format/WFS~FeatureCollectionMetadata} */ (result),
FEATURE_COLLECTION_PARSERS, node, [], this.gmlFormat_);
};
/**
* @const
* @type {Object.<string, Object.<string, module:ol/xml~Parser>>}
@@ -400,31 +581,6 @@ const TRANSACTION_RESPONSE_PARSERS = {
};
/**
* @param {Document} doc Document.
* @return {module:ol/format/WFS~TransactionResponse|undefined} Transaction response.
*/
WFS.prototype.readTransactionResponseFromDocument = function(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
return this.readTransactionResponseFromNode(n);
}
}
return undefined;
};
/**
* @param {Node} node Node.
* @return {module:ol/format/WFS~TransactionResponse|undefined} Transaction response.
*/
WFS.prototype.readTransactionResponseFromNode = function(node) {
return pushParseAndPop(
/** @type {module:ol/format/WFS~TransactionResponse} */({}),
TRANSACTION_RESPONSE_PARSERS, node, []);
};
/**
* @type {Object.<string, Object.<string, module:ol/xml~Serializer>>}
*/
@@ -942,138 +1098,6 @@ function writeGetFeature(node, featureTypes, objectStack) {
}
/**
* Encode format as WFS `GetFeature` and return the Node.
*
* @param {module:ol/format/WFS~WriteGetFeatureOptions} options Options.
* @return {Node} Result.
* @api
*/
WFS.prototype.writeGetFeature = function(options) {
const node = createElementNS(WFSNS, 'GetFeature');
node.setAttribute('service', 'WFS');
node.setAttribute('version', '1.1.0');
let filter;
if (options) {
if (options.handle) {
node.setAttribute('handle', options.handle);
}
if (options.outputFormat) {
node.setAttribute('outputFormat', options.outputFormat);
}
if (options.maxFeatures !== undefined) {
node.setAttribute('maxFeatures', options.maxFeatures);
}
if (options.resultType) {
node.setAttribute('resultType', options.resultType);
}
if (options.startIndex !== undefined) {
node.setAttribute('startIndex', options.startIndex);
}
if (options.count !== undefined) {
node.setAttribute('count', options.count);
}
filter = options.filter;
if (options.bbox) {
assert(options.geometryName,
12); // `options.geometryName` must also be provided when `options.bbox` is set
const bbox = bboxFilter(
/** @type {string} */ (options.geometryName), options.bbox, options.srsName);
if (filter) {
// if bbox and filter are both set, combine the two into a single filter
filter = andFilter(filter, bbox);
} else {
filter = bbox;
}
}
}
node.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', this.schemaLocation_);
/** @type {module:ol/xml~NodeStackItem} */
const context = {
node: node,
'srsName': options.srsName,
'featureNS': options.featureNS ? options.featureNS : this.featureNS_,
'featurePrefix': options.featurePrefix,
'geometryName': options.geometryName,
'filter': filter,
'propertyNames': options.propertyNames ? options.propertyNames : []
};
assert(Array.isArray(options.featureTypes),
11); // `options.featureTypes` should be an Array
writeGetFeature(node, /** @type {!Array.<string>} */ (options.featureTypes), [context]);
return node;
};
/**
* Encode format as WFS `Transaction` and return the Node.
*
* @param {Array.<module:ol/Feature>} inserts The features to insert.
* @param {Array.<module:ol/Feature>} updates The features to update.
* @param {Array.<module:ol/Feature>} deletes The features to delete.
* @param {module:ol/format/WFS~WriteTransactionOptions} options Write options.
* @return {Node} Result.
* @api
*/
WFS.prototype.writeTransaction = function(inserts, updates, deletes, options) {
const objectStack = [];
const node = createElementNS(WFSNS, 'Transaction');
const version = options.version ? options.version : DEFAULT_VERSION;
const gmlVersion = version === '1.0.0' ? 2 : 3;
node.setAttribute('service', 'WFS');
node.setAttribute('version', version);
let baseObj;
/** @type {module:ol/xml~NodeStackItem} */
let obj;
if (options) {
baseObj = options.gmlOptions ? options.gmlOptions : {};
if (options.handle) {
node.setAttribute('handle', options.handle);
}
}
const schemaLocation = SCHEMA_LOCATIONS[version];
node.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', schemaLocation);
const featurePrefix = options.featurePrefix ? options.featurePrefix : FEATURE_PREFIX;
if (inserts) {
obj = {node: node, 'featureNS': options.featureNS,
'featureType': options.featureType, 'featurePrefix': featurePrefix,
'gmlVersion': gmlVersion, 'hasZ': options.hasZ, 'srsName': options.srsName};
assign(obj, baseObj);
pushSerializeAndPop(obj,
TRANSACTION_SERIALIZERS,
makeSimpleNodeFactory('Insert'), inserts,
objectStack);
}
if (updates) {
obj = {node: node, 'featureNS': options.featureNS,
'featureType': options.featureType, 'featurePrefix': featurePrefix,
'gmlVersion': gmlVersion, 'hasZ': options.hasZ, 'srsName': options.srsName};
assign(obj, baseObj);
pushSerializeAndPop(obj,
TRANSACTION_SERIALIZERS,
makeSimpleNodeFactory('Update'), updates,
objectStack);
}
if (deletes) {
pushSerializeAndPop({node: node, 'featureNS': options.featureNS,
'featureType': options.featureType, 'featurePrefix': featurePrefix,
'gmlVersion': gmlVersion, 'srsName': options.srsName},
TRANSACTION_SERIALIZERS,
makeSimpleNodeFactory('Delete'), deletes,
objectStack);
}
if (options.nativeElements) {
pushSerializeAndPop({node: node, 'featureNS': options.featureNS,
'featureType': options.featureType, 'featurePrefix': featurePrefix,
'gmlVersion': gmlVersion, 'srsName': options.srsName},
TRANSACTION_SERIALIZERS,
makeSimpleNodeFactory('Native'), options.nativeElements,
objectStack);
}
return node;
};
/**
* Read the projection from a WFS source.
*
@@ -1085,37 +1109,4 @@ WFS.prototype.writeTransaction = function(inserts, updates, deletes, options) {
WFS.prototype.readProjection;
/**
* @inheritDoc
*/
WFS.prototype.readProjectionFromDocument = function(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
return this.readProjectionFromNode(n);
}
}
return null;
};
/**
* @inheritDoc
*/
WFS.prototype.readProjectionFromNode = function(node) {
if (node.firstElementChild &&
node.firstElementChild.firstElementChild) {
node = node.firstElementChild.firstElementChild;
for (let n = node.firstElementChild; n; n = n.nextElementSibling) {
if (!(n.childNodes.length === 0 ||
(n.childNodes.length === 1 &&
n.firstChild.nodeType === 3))) {
const objectStack = [{}];
this.gmlFormat_.readGeometryElement(n, objectStack);
return getProjection(objectStack.pop().srsName);
}
}
}
return null;
};
export default WFS;

File diff suppressed because it is too large Load Diff

View File

@@ -17,15 +17,40 @@ import {makeArrayPusher, makeObjectPropertyPusher, makeObjectPropertySetter,
* @extends {module:ol/format/XML}
* @api
*/
const WMSCapabilities = function() {
class WMSCapabilities {
constructor() {
XML.call(this);
XML.call(this);
/**
* @type {string|undefined}
*/
this.version = undefined;
}
/**
* @type {string|undefined}
* @inheritDoc
*/
this.version = undefined;
};
readFromDocument(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
return this.readFromNode(n);
}
}
return null;
}
/**
* @inheritDoc
*/
readFromNode(node) {
this.version = node.getAttribute('version').trim();
const wmsCapabilityObject = pushParseAndPop({
'version': this.version
}, PARSERS, node, []);
return wmsCapabilityObject ? wmsCapabilityObject : null;
}
}
inherits(WMSCapabilities, XML);
@@ -277,31 +302,6 @@ const KEYWORDLIST_PARSERS = makeStructureNS(
WMSCapabilities.prototype.read;
/**
* @inheritDoc
*/
WMSCapabilities.prototype.readFromDocument = function(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
return this.readFromNode(n);
}
}
return null;
};
/**
* @inheritDoc
*/
WMSCapabilities.prototype.readFromNode = function(node) {
this.version = node.getAttribute('version').trim();
const wmsCapabilityObject = pushParseAndPop({
'version': this.version
}, PARSERS, node, []);
return wmsCapabilityObject ? wmsCapabilityObject : null;
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.

View File

@@ -25,32 +25,136 @@ import {makeArrayPusher, makeStructureNS, pushParseAndPop} from '../xml.js';
* @param {module:ol/format/WMSGetFeatureInfo~Options=} opt_options Options.
* @api
*/
const WMSGetFeatureInfo = function(opt_options) {
class WMSGetFeatureInfo {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
/**
* @private
* @type {string}
*/
this.featureNS_ = 'http://mapserver.gis.umn.edu/mapserver';
/**
* @private
* @type {module:ol/format/GML2}
*/
this.gmlFormat_ = new GML2();
/**
* @private
* @type {Array.<string>}
*/
this.layers_ = options.layers ? options.layers : null;
XMLFeature.call(this);
}
/**
* @private
* @type {string}
* @return {Array.<string>} layers
*/
this.featureNS_ = 'http://mapserver.gis.umn.edu/mapserver';
getLayers() {
return this.layers_;
}
/**
* @private
* @type {module:ol/format/GML2}
* @param {Array.<string>} layers Layers to parse.
*/
this.gmlFormat_ = new GML2();
setLayers(layers) {
this.layers_ = layers;
}
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {Array.<module:ol/Feature>} Features.
* @private
* @type {Array.<string>}
*/
this.layers_ = options.layers ? options.layers : null;
readFeatures_(node, objectStack) {
node.setAttribute('namespaceURI', this.featureNS_);
const localName = node.localName;
/** @type {Array.<module:ol/Feature>} */
let features = [];
if (node.childNodes.length === 0) {
return features;
}
if (localName == 'msGMLOutput') {
for (let i = 0, ii = node.childNodes.length; i < ii; i++) {
const layer = node.childNodes[i];
if (layer.nodeType !== Node.ELEMENT_NODE) {
continue;
}
const context = objectStack[0];
XMLFeature.call(this);
};
const toRemove = layerIdentifier;
const layerName = layer.localName.replace(toRemove, '');
if (this.layers_ && !includes(this.layers_, layerName)) {
continue;
}
const featureType = layerName +
featureIdentifier;
context['featureType'] = featureType;
context['featureNS'] = this.featureNS_;
const parsers = {};
parsers[featureType] = makeArrayPusher(
this.gmlFormat_.readFeatureElement, this.gmlFormat_);
const parsersNS = makeStructureNS(
[context['featureNS'], null], parsers);
layer.setAttribute('namespaceURI', this.featureNS_);
const layerFeatures = pushParseAndPop(
[], parsersNS, layer, objectStack, this.gmlFormat_);
if (layerFeatures) {
extend(features, layerFeatures);
}
}
}
if (localName == 'FeatureCollection') {
const gmlFeatures = pushParseAndPop([],
this.gmlFormat_.FEATURE_COLLECTION_PARSERS, node,
[{}], this.gmlFormat_);
if (gmlFeatures) {
features = gmlFeatures;
}
}
return features;
}
/**
* @inheritDoc
*/
readFeaturesFromNode(node, opt_options) {
const options = {};
if (opt_options) {
assign(options, this.getReadOptions(node, opt_options));
}
return this.readFeatures_(node, [options]);
}
/**
* Not implemented.
* @inheritDoc
*/
writeFeatureNode(feature, opt_options) {}
/**
* Not implemented.
* @inheritDoc
*/
writeFeaturesNode(features, opt_options) {}
/**
* Not implemented.
* @inheritDoc
*/
writeGeometryNode(geometry, opt_options) {}
}
inherits(WMSGetFeatureInfo, XMLFeature);
@@ -69,82 +173,6 @@ const featureIdentifier = '_feature';
const layerIdentifier = '_layer';
/**
* @return {Array.<string>} layers
*/
WMSGetFeatureInfo.prototype.getLayers = function() {
return this.layers_;
};
/**
* @param {Array.<string>} layers Layers to parse.
*/
WMSGetFeatureInfo.prototype.setLayers = function(layers) {
this.layers_ = layers;
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.
* @return {Array.<module:ol/Feature>} Features.
* @private
*/
WMSGetFeatureInfo.prototype.readFeatures_ = function(node, objectStack) {
node.setAttribute('namespaceURI', this.featureNS_);
const localName = node.localName;
/** @type {Array.<module:ol/Feature>} */
let features = [];
if (node.childNodes.length === 0) {
return features;
}
if (localName == 'msGMLOutput') {
for (let i = 0, ii = node.childNodes.length; i < ii; i++) {
const layer = node.childNodes[i];
if (layer.nodeType !== Node.ELEMENT_NODE) {
continue;
}
const context = objectStack[0];
const toRemove = layerIdentifier;
const layerName = layer.localName.replace(toRemove, '');
if (this.layers_ && !includes(this.layers_, layerName)) {
continue;
}
const featureType = layerName +
featureIdentifier;
context['featureType'] = featureType;
context['featureNS'] = this.featureNS_;
const parsers = {};
parsers[featureType] = makeArrayPusher(
this.gmlFormat_.readFeatureElement, this.gmlFormat_);
const parsersNS = makeStructureNS(
[context['featureNS'], null], parsers);
layer.setAttribute('namespaceURI', this.featureNS_);
const layerFeatures = pushParseAndPop(
[], parsersNS, layer, objectStack, this.gmlFormat_);
if (layerFeatures) {
extend(features, layerFeatures);
}
}
}
if (localName == 'FeatureCollection') {
const gmlFeatures = pushParseAndPop([],
this.gmlFormat_.FEATURE_COLLECTION_PARSERS, node,
[{}], this.gmlFormat_);
if (gmlFeatures) {
features = gmlFeatures;
}
}
return features;
};
/**
* Read all features from a WMSGetFeatureInfo response.
*
@@ -157,35 +185,4 @@ WMSGetFeatureInfo.prototype.readFeatures_ = function(node, objectStack) {
WMSGetFeatureInfo.prototype.readFeatures;
/**
* @inheritDoc
*/
WMSGetFeatureInfo.prototype.readFeaturesFromNode = function(node, opt_options) {
const options = {};
if (opt_options) {
assign(options, this.getReadOptions(node, opt_options));
}
return this.readFeatures_(node, [options]);
};
/**
* Not implemented.
* @inheritDoc
*/
WMSGetFeatureInfo.prototype.writeFeatureNode = function(feature, opt_options) {};
/**
* Not implemented.
* @inheritDoc
*/
WMSGetFeatureInfo.prototype.writeFeaturesNode = function(features, opt_options) {};
/**
* Not implemented.
* @inheritDoc
*/
WMSGetFeatureInfo.prototype.writeGeometryNode = function(geometry, opt_options) {};
export default WMSGetFeatureInfo;

View File

@@ -18,15 +18,43 @@ import {pushParseAndPop, makeStructureNS,
* @extends {module:ol/format/XML}
* @api
*/
const WMTSCapabilities = function() {
XML.call(this);
class WMTSCapabilities {
constructor() {
XML.call(this);
/**
* @type {module:ol/format/OWS}
* @private
*/
this.owsParser_ = new OWS();
}
/**
* @type {module:ol/format/OWS}
* @private
* @inheritDoc
*/
this.owsParser_ = new OWS();
};
readFromDocument(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
return this.readFromNode(n);
}
}
return null;
}
/**
* @inheritDoc
*/
readFromNode(node) {
const version = node.getAttribute('version').trim();
let WMTSCapabilityObject = this.owsParser_.readFromNode(node);
if (!WMTSCapabilityObject) {
return null;
}
WMTSCapabilityObject['version'] = version;
WMTSCapabilityObject = pushParseAndPop(WMTSCapabilityObject, PARSERS, node, []);
return WMTSCapabilityObject ? WMTSCapabilityObject : null;
}
}
inherits(WMTSCapabilities, XML);
@@ -204,34 +232,6 @@ const TM_PARSERS = makeStructureNS(
WMTSCapabilities.prototype.read;
/**
* @inheritDoc
*/
WMTSCapabilities.prototype.readFromDocument = function(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
return this.readFromNode(n);
}
}
return null;
};
/**
* @inheritDoc
*/
WMTSCapabilities.prototype.readFromNode = function(node) {
const version = node.getAttribute('version').trim();
let WMTSCapabilityObject = this.owsParser_.readFromNode(node);
if (!WMTSCapabilityObject) {
return null;
}
WMTSCapabilityObject['version'] = version;
WMTSCapabilityObject = pushParseAndPop(WMTSCapabilityObject, PARSERS, node, []);
return WMTSCapabilityObject ? WMTSCapabilityObject : null;
};
/**
* @param {Node} node Node.
* @param {Array.<*>} objectStack Object stack.

View File

@@ -11,40 +11,37 @@ import {isDocument, isNode, parse} from '../xml.js';
* @abstract
* @struct
*/
const XML = function() {
};
class XML {
/**
* @param {Document|Node|string} source Source.
* @return {Object} The parsed result.
*/
read(source) {
if (isDocument(source)) {
return this.readFromDocument(/** @type {Document} */ (source));
} else if (isNode(source)) {
return this.readFromNode(/** @type {Node} */ (source));
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readFromDocument(doc);
} else {
return null;
}
}
/**
* @abstract
* @param {Document} doc Document.
* @return {Object} Object
*/
readFromDocument(doc) {}
/**
* @param {Document|Node|string} source Source.
* @return {Object} The parsed result.
*/
XML.prototype.read = function(source) {
if (isDocument(source)) {
return this.readFromDocument(/** @type {Document} */ (source));
} else if (isNode(source)) {
return this.readFromNode(/** @type {Node} */ (source));
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readFromDocument(doc);
} else {
return null;
}
};
/**
* @abstract
* @param {Node} node Node.
* @return {Object} Object
*/
readFromNode(node) {}
}
/**
* @abstract
* @param {Document} doc Document.
* @return {Object} Object
*/
XML.prototype.readFromDocument = function(doc) {};
/**
* @abstract
* @param {Node} node Node.
* @return {Object} Object
*/
XML.prototype.readFromNode = function(node) {};
export default XML;

View File

@@ -17,247 +17,232 @@ import {isDocument, isNode, parse} from '../xml.js';
* @abstract
* @extends {module:ol/format/Feature}
*/
const XMLFeature = function() {
class XMLFeature {
constructor() {
/**
* @type {XMLSerializer}
* @private
*/
this.xmlSerializer_ = new XMLSerializer();
FeatureFormat.call(this);
}
/**
* @type {XMLSerializer}
* @private
* @inheritDoc
*/
this.xmlSerializer_ = new XMLSerializer();
getType() {
return FormatType.XML;
}
FeatureFormat.call(this);
};
/**
* @inheritDoc
*/
readFeature(source, opt_options) {
if (isDocument(source)) {
return this.readFeatureFromDocument(/** @type {Document} */ (source), opt_options);
} else if (isNode(source)) {
return this.readFeatureFromNode(/** @type {Node} */ (source), opt_options);
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readFeatureFromDocument(doc, opt_options);
} else {
return null;
}
}
/**
* @param {Document} doc Document.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Options.
* @return {module:ol/Feature} Feature.
*/
readFeatureFromDocument(doc, opt_options) {
const features = this.readFeaturesFromDocument(doc, opt_options);
if (features.length > 0) {
return features[0];
} else {
return null;
}
}
/**
* @param {Node} node Node.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Options.
* @return {module:ol/Feature} Feature.
*/
readFeatureFromNode(node, opt_options) {
return null; // not implemented
}
/**
* @inheritDoc
*/
readFeatures(source, opt_options) {
if (isDocument(source)) {
return this.readFeaturesFromDocument(
/** @type {Document} */ (source), opt_options);
} else if (isNode(source)) {
return this.readFeaturesFromNode(/** @type {Node} */ (source), opt_options);
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readFeaturesFromDocument(doc, opt_options);
} else {
return [];
}
}
/**
* @param {Document} doc Document.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Options.
* @protected
* @return {Array.<module:ol/Feature>} Features.
*/
readFeaturesFromDocument(doc, opt_options) {
/** @type {Array.<module:ol/Feature>} */
const features = [];
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
extend(features, this.readFeaturesFromNode(n, opt_options));
}
}
return features;
}
/**
* @abstract
* @param {Node} node Node.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Options.
* @protected
* @return {Array.<module:ol/Feature>} Features.
*/
readFeaturesFromNode(node, opt_options) {}
/**
* @inheritDoc
*/
readGeometry(source, opt_options) {
if (isDocument(source)) {
return this.readGeometryFromDocument(
/** @type {Document} */ (source), opt_options);
} else if (isNode(source)) {
return this.readGeometryFromNode(/** @type {Node} */ (source), opt_options);
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readGeometryFromDocument(doc, opt_options);
} else {
return null;
}
}
/**
* @param {Document} doc Document.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Options.
* @protected
* @return {module:ol/geom/Geometry} Geometry.
*/
readGeometryFromDocument(doc, opt_options) {
return null; // not implemented
}
/**
* @param {Node} node Node.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Options.
* @protected
* @return {module:ol/geom/Geometry} Geometry.
*/
readGeometryFromNode(node, opt_options) {
return null; // not implemented
}
/**
* @inheritDoc
*/
readProjection(source) {
if (isDocument(source)) {
return this.readProjectionFromDocument(/** @type {Document} */ (source));
} else if (isNode(source)) {
return this.readProjectionFromNode(/** @type {Node} */ (source));
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readProjectionFromDocument(doc);
} else {
return null;
}
}
/**
* @param {Document} doc Document.
* @protected
* @return {module:ol/proj/Projection} Projection.
*/
readProjectionFromDocument(doc) {
return this.dataProjection;
}
/**
* @param {Node} node Node.
* @protected
* @return {module:ol/proj/Projection} Projection.
*/
readProjectionFromNode(node) {
return this.dataProjection;
}
/**
* @inheritDoc
*/
writeFeature(feature, opt_options) {
const node = this.writeFeatureNode(feature, opt_options);
return this.xmlSerializer_.serializeToString(node);
}
/**
* @param {module:ol/Feature} feature Feature.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Options.
* @protected
* @return {Node} Node.
*/
writeFeatureNode(feature, opt_options) {
return null; // not implemented
}
/**
* @inheritDoc
*/
writeFeatures(features, opt_options) {
const node = this.writeFeaturesNode(features, opt_options);
return this.xmlSerializer_.serializeToString(node);
}
/**
* @param {Array.<module:ol/Feature>} features Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Options.
* @return {Node} Node.
*/
writeFeaturesNode(features, opt_options) {
return null; // not implemented
}
/**
* @inheritDoc
*/
writeGeometry(geometry, opt_options) {
const node = this.writeGeometryNode(geometry, opt_options);
return this.xmlSerializer_.serializeToString(node);
}
/**
* @param {module:ol/geom/Geometry} geometry Geometry.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Options.
* @return {Node} Node.
*/
writeGeometryNode(geometry, opt_options) {
return null; // not implemented
}
}
inherits(XMLFeature, FeatureFormat);
/**
* @inheritDoc
*/
XMLFeature.prototype.getType = function() {
return FormatType.XML;
};
/**
* @inheritDoc
*/
XMLFeature.prototype.readFeature = function(source, opt_options) {
if (isDocument(source)) {
return this.readFeatureFromDocument(/** @type {Document} */ (source), opt_options);
} else if (isNode(source)) {
return this.readFeatureFromNode(/** @type {Node} */ (source), opt_options);
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readFeatureFromDocument(doc, opt_options);
} else {
return null;
}
};
/**
* @param {Document} doc Document.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Options.
* @return {module:ol/Feature} Feature.
*/
XMLFeature.prototype.readFeatureFromDocument = function(doc, opt_options) {
const features = this.readFeaturesFromDocument(doc, opt_options);
if (features.length > 0) {
return features[0];
} else {
return null;
}
};
/**
* @param {Node} node Node.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Options.
* @return {module:ol/Feature} Feature.
*/
XMLFeature.prototype.readFeatureFromNode = function(node, opt_options) {
return null; // not implemented
};
/**
* @inheritDoc
*/
XMLFeature.prototype.readFeatures = function(source, opt_options) {
if (isDocument(source)) {
return this.readFeaturesFromDocument(
/** @type {Document} */ (source), opt_options);
} else if (isNode(source)) {
return this.readFeaturesFromNode(/** @type {Node} */ (source), opt_options);
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readFeaturesFromDocument(doc, opt_options);
} else {
return [];
}
};
/**
* @param {Document} doc Document.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Options.
* @protected
* @return {Array.<module:ol/Feature>} Features.
*/
XMLFeature.prototype.readFeaturesFromDocument = function(doc, opt_options) {
/** @type {Array.<module:ol/Feature>} */
const features = [];
for (let n = doc.firstChild; n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
extend(features, this.readFeaturesFromNode(n, opt_options));
}
}
return features;
};
/**
* @abstract
* @param {Node} node Node.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Options.
* @protected
* @return {Array.<module:ol/Feature>} Features.
*/
XMLFeature.prototype.readFeaturesFromNode = function(node, opt_options) {};
/**
* @inheritDoc
*/
XMLFeature.prototype.readGeometry = function(source, opt_options) {
if (isDocument(source)) {
return this.readGeometryFromDocument(
/** @type {Document} */ (source), opt_options);
} else if (isNode(source)) {
return this.readGeometryFromNode(/** @type {Node} */ (source), opt_options);
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readGeometryFromDocument(doc, opt_options);
} else {
return null;
}
};
/**
* @param {Document} doc Document.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Options.
* @protected
* @return {module:ol/geom/Geometry} Geometry.
*/
XMLFeature.prototype.readGeometryFromDocument = function(doc, opt_options) {
return null; // not implemented
};
/**
* @param {Node} node Node.
* @param {module:ol/format/Feature~ReadOptions=} opt_options Options.
* @protected
* @return {module:ol/geom/Geometry} Geometry.
*/
XMLFeature.prototype.readGeometryFromNode = function(node, opt_options) {
return null; // not implemented
};
/**
* @inheritDoc
*/
XMLFeature.prototype.readProjection = function(source) {
if (isDocument(source)) {
return this.readProjectionFromDocument(/** @type {Document} */ (source));
} else if (isNode(source)) {
return this.readProjectionFromNode(/** @type {Node} */ (source));
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readProjectionFromDocument(doc);
} else {
return null;
}
};
/**
* @param {Document} doc Document.
* @protected
* @return {module:ol/proj/Projection} Projection.
*/
XMLFeature.prototype.readProjectionFromDocument = function(doc) {
return this.dataProjection;
};
/**
* @param {Node} node Node.
* @protected
* @return {module:ol/proj/Projection} Projection.
*/
XMLFeature.prototype.readProjectionFromNode = function(node) {
return this.dataProjection;
};
/**
* @inheritDoc
*/
XMLFeature.prototype.writeFeature = function(feature, opt_options) {
const node = this.writeFeatureNode(feature, opt_options);
return this.xmlSerializer_.serializeToString(node);
};
/**
* @param {module:ol/Feature} feature Feature.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Options.
* @protected
* @return {Node} Node.
*/
XMLFeature.prototype.writeFeatureNode = function(feature, opt_options) {
return null; // not implemented
};
/**
* @inheritDoc
*/
XMLFeature.prototype.writeFeatures = function(features, opt_options) {
const node = this.writeFeaturesNode(features, opt_options);
return this.xmlSerializer_.serializeToString(node);
};
/**
* @param {Array.<module:ol/Feature>} features Features.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Options.
* @return {Node} Node.
*/
XMLFeature.prototype.writeFeaturesNode = function(features, opt_options) {
return null; // not implemented
};
/**
* @inheritDoc
*/
XMLFeature.prototype.writeGeometry = function(geometry, opt_options) {
const node = this.writeGeometryNode(geometry, opt_options);
return this.xmlSerializer_.serializeToString(node);
};
/**
* @param {module:ol/geom/Geometry} geometry Geometry.
* @param {module:ol/format/Feature~WriteOptions=} opt_options Options.
* @return {Node} Node.
*/
XMLFeature.prototype.writeGeometryNode = function(geometry, opt_options) {
return null; // not implemented
};
export default XMLFeature;

View File

@@ -13,21 +13,23 @@
* @param {!string} tagName The XML tag name for this filter.
* @struct
*/
const Filter = function(tagName) {
class Filter {
constructor(tagName) {
/**
* @private
* @type {!string}
*/
this.tagName_ = tagName;
};
/**
* @private
* @type {!string}
*/
this.tagName_ = tagName;
}
/**
* The XML tag name for a filter.
* @returns {!string} Name.
*/
Filter.prototype.getTagName = function() {
return this.tagName_;
};
/**
* The XML tag name for a filter.
* @returns {!string} Name.
*/
getTagName() {
return this.tagName_;
}
}
export default Filter;

View File

@@ -20,213 +20,201 @@ import {deflateCoordinate} from '../geom/flat/deflate.js';
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @api
*/
const Circle = function(center, opt_radius, opt_layout) {
SimpleGeometry.call(this);
if (opt_layout !== undefined && opt_radius === undefined) {
this.setFlatCoordinates(opt_layout, center);
} else {
const radius = opt_radius ? opt_radius : 0;
this.setCenterAndRadius(center, radius, opt_layout);
class Circle {
constructor(center, opt_radius, opt_layout) {
SimpleGeometry.call(this);
if (opt_layout !== undefined && opt_radius === undefined) {
this.setFlatCoordinates(opt_layout, center);
} else {
const radius = opt_radius ? opt_radius : 0;
this.setCenterAndRadius(center, radius, opt_layout);
}
}
};
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/Circle} Clone.
* @override
* @api
*/
clone() {
return new Circle(this.flatCoordinates.slice(), undefined, this.layout);
}
/**
* @inheritDoc
*/
closestPointXY(x, y, closestPoint, minSquaredDistance) {
const flatCoordinates = this.flatCoordinates;
const dx = x - flatCoordinates[0];
const dy = y - flatCoordinates[1];
const squaredDistance = dx * dx + dy * dy;
if (squaredDistance < minSquaredDistance) {
if (squaredDistance === 0) {
for (let i = 0; i < this.stride; ++i) {
closestPoint[i] = flatCoordinates[i];
}
} else {
const delta = this.getRadius() / Math.sqrt(squaredDistance);
closestPoint[0] = flatCoordinates[0] + delta * dx;
closestPoint[1] = flatCoordinates[1] + delta * dy;
for (let i = 2; i < this.stride; ++i) {
closestPoint[i] = flatCoordinates[i];
}
}
closestPoint.length = this.stride;
return squaredDistance;
} else {
return minSquaredDistance;
}
}
/**
* @inheritDoc
*/
containsXY(x, y) {
const flatCoordinates = this.flatCoordinates;
const dx = x - flatCoordinates[0];
const dy = y - flatCoordinates[1];
return dx * dx + dy * dy <= this.getRadiusSquared_();
}
/**
* Return the center of the circle as {@link module:ol/coordinate~Coordinate coordinate}.
* @return {module:ol/coordinate~Coordinate} Center.
* @api
*/
getCenter() {
return this.flatCoordinates.slice(0, this.stride);
}
/**
* @inheritDoc
*/
computeExtent(extent) {
const flatCoordinates = this.flatCoordinates;
const radius = flatCoordinates[this.stride] - flatCoordinates[0];
return createOrUpdate(
flatCoordinates[0] - radius, flatCoordinates[1] - radius,
flatCoordinates[0] + radius, flatCoordinates[1] + radius,
extent);
}
/**
* Return the radius of the circle.
* @return {number} Radius.
* @api
*/
getRadius() {
return Math.sqrt(this.getRadiusSquared_());
}
/**
* @private
* @return {number} Radius squared.
*/
getRadiusSquared_() {
const dx = this.flatCoordinates[this.stride] - this.flatCoordinates[0];
const dy = this.flatCoordinates[this.stride + 1] - this.flatCoordinates[1];
return dx * dx + dy * dy;
}
/**
* @inheritDoc
* @api
*/
getType() {
return GeometryType.CIRCLE;
}
/**
* @inheritDoc
* @api
*/
intersectsExtent(extent) {
const circleExtent = this.getExtent();
if (intersects(extent, circleExtent)) {
const center = this.getCenter();
if (extent[0] <= center[0] && extent[2] >= center[0]) {
return true;
}
if (extent[1] <= center[1] && extent[3] >= center[1]) {
return true;
}
return forEachCorner(extent, this.intersectsCoordinate, this);
}
return false;
}
/**
* Set the center of the circle as {@link module:ol/coordinate~Coordinate coordinate}.
* @param {module:ol/coordinate~Coordinate} center Center.
* @api
*/
setCenter(center) {
const stride = this.stride;
const radius = this.flatCoordinates[stride] - this.flatCoordinates[0];
const flatCoordinates = center.slice();
flatCoordinates[stride] = flatCoordinates[0] + radius;
for (let i = 1; i < stride; ++i) {
flatCoordinates[stride + i] = center[i];
}
this.setFlatCoordinates(this.layout, flatCoordinates);
this.changed();
}
/**
* Set the center (as {@link module:ol/coordinate~Coordinate coordinate}) and the radius (as
* number) of the circle.
* @param {!module:ol/coordinate~Coordinate} center Center.
* @param {number} radius Radius.
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @api
*/
setCenterAndRadius(center, radius, opt_layout) {
this.setLayout(opt_layout, center, 0);
if (!this.flatCoordinates) {
this.flatCoordinates = [];
}
/** @type {Array.<number>} */
const flatCoordinates = this.flatCoordinates;
let offset = deflateCoordinate(
flatCoordinates, 0, center, this.stride);
flatCoordinates[offset++] = flatCoordinates[0] + radius;
for (let i = 1, ii = this.stride; i < ii; ++i) {
flatCoordinates[offset++] = flatCoordinates[i];
}
flatCoordinates.length = offset;
this.changed();
}
/**
* @inheritDoc
*/
getCoordinates() {}
/**
* @inheritDoc
*/
setCoordinates(coordinates, opt_layout) {}
/**
* Set the radius of the circle. The radius is in the units of the projection.
* @param {number} radius Radius.
* @api
*/
setRadius(radius) {
this.flatCoordinates[this.stride] = this.flatCoordinates[0] + radius;
this.changed();
}
}
inherits(Circle, SimpleGeometry);
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/Circle} Clone.
* @override
* @api
*/
Circle.prototype.clone = function() {
return new Circle(this.flatCoordinates.slice(), undefined, this.layout);
};
/**
* @inheritDoc
*/
Circle.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
const flatCoordinates = this.flatCoordinates;
const dx = x - flatCoordinates[0];
const dy = y - flatCoordinates[1];
const squaredDistance = dx * dx + dy * dy;
if (squaredDistance < minSquaredDistance) {
if (squaredDistance === 0) {
for (let i = 0; i < this.stride; ++i) {
closestPoint[i] = flatCoordinates[i];
}
} else {
const delta = this.getRadius() / Math.sqrt(squaredDistance);
closestPoint[0] = flatCoordinates[0] + delta * dx;
closestPoint[1] = flatCoordinates[1] + delta * dy;
for (let i = 2; i < this.stride; ++i) {
closestPoint[i] = flatCoordinates[i];
}
}
closestPoint.length = this.stride;
return squaredDistance;
} else {
return minSquaredDistance;
}
};
/**
* @inheritDoc
*/
Circle.prototype.containsXY = function(x, y) {
const flatCoordinates = this.flatCoordinates;
const dx = x - flatCoordinates[0];
const dy = y - flatCoordinates[1];
return dx * dx + dy * dy <= this.getRadiusSquared_();
};
/**
* Return the center of the circle as {@link module:ol/coordinate~Coordinate coordinate}.
* @return {module:ol/coordinate~Coordinate} Center.
* @api
*/
Circle.prototype.getCenter = function() {
return this.flatCoordinates.slice(0, this.stride);
};
/**
* @inheritDoc
*/
Circle.prototype.computeExtent = function(extent) {
const flatCoordinates = this.flatCoordinates;
const radius = flatCoordinates[this.stride] - flatCoordinates[0];
return createOrUpdate(
flatCoordinates[0] - radius, flatCoordinates[1] - radius,
flatCoordinates[0] + radius, flatCoordinates[1] + radius,
extent);
};
/**
* Return the radius of the circle.
* @return {number} Radius.
* @api
*/
Circle.prototype.getRadius = function() {
return Math.sqrt(this.getRadiusSquared_());
};
/**
* @private
* @return {number} Radius squared.
*/
Circle.prototype.getRadiusSquared_ = function() {
const dx = this.flatCoordinates[this.stride] - this.flatCoordinates[0];
const dy = this.flatCoordinates[this.stride + 1] - this.flatCoordinates[1];
return dx * dx + dy * dy;
};
/**
* @inheritDoc
* @api
*/
Circle.prototype.getType = function() {
return GeometryType.CIRCLE;
};
/**
* @inheritDoc
* @api
*/
Circle.prototype.intersectsExtent = function(extent) {
const circleExtent = this.getExtent();
if (intersects(extent, circleExtent)) {
const center = this.getCenter();
if (extent[0] <= center[0] && extent[2] >= center[0]) {
return true;
}
if (extent[1] <= center[1] && extent[3] >= center[1]) {
return true;
}
return forEachCorner(extent, this.intersectsCoordinate, this);
}
return false;
};
/**
* Set the center of the circle as {@link module:ol/coordinate~Coordinate coordinate}.
* @param {module:ol/coordinate~Coordinate} center Center.
* @api
*/
Circle.prototype.setCenter = function(center) {
const stride = this.stride;
const radius = this.flatCoordinates[stride] - this.flatCoordinates[0];
const flatCoordinates = center.slice();
flatCoordinates[stride] = flatCoordinates[0] + radius;
for (let i = 1; i < stride; ++i) {
flatCoordinates[stride + i] = center[i];
}
this.setFlatCoordinates(this.layout, flatCoordinates);
this.changed();
};
/**
* Set the center (as {@link module:ol/coordinate~Coordinate coordinate}) and the radius (as
* number) of the circle.
* @param {!module:ol/coordinate~Coordinate} center Center.
* @param {number} radius Radius.
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @api
*/
Circle.prototype.setCenterAndRadius = function(center, radius, opt_layout) {
this.setLayout(opt_layout, center, 0);
if (!this.flatCoordinates) {
this.flatCoordinates = [];
}
/** @type {Array.<number>} */
const flatCoordinates = this.flatCoordinates;
let offset = deflateCoordinate(
flatCoordinates, 0, center, this.stride);
flatCoordinates[offset++] = flatCoordinates[0] + radius;
for (let i = 1, ii = this.stride; i < ii; ++i) {
flatCoordinates[offset++] = flatCoordinates[i];
}
flatCoordinates.length = offset;
this.changed();
};
/**
* @inheritDoc
*/
Circle.prototype.getCoordinates = function() {};
/**
* @inheritDoc
*/
Circle.prototype.setCoordinates = function(coordinates, opt_layout) {};
/**
* Set the radius of the circle. The radius is in the units of the projection.
* @param {number} radius Radius.
* @api
*/
Circle.prototype.setRadius = function(radius) {
this.flatCoordinates[this.stride] = this.flatCoordinates[0] + radius;
this.changed();
};
/**
* Transform each coordinate of the circle from one coordinate reference system
* to another. The geometry is modified in place.

View File

@@ -25,41 +25,225 @@ import {create as createTransform, compose as composeTransform} from '../transfo
* @extends {module:ol/Object}
* @api
*/
const Geometry = function() {
class Geometry {
constructor() {
BaseObject.call(this);
BaseObject.call(this);
/**
* @private
* @type {module:ol/extent~Extent}
*/
this.extent_ = createEmpty();
/**
* @private
* @type {module:ol/extent~Extent}
*/
this.extent_ = createEmpty();
/**
* @private
* @type {number}
*/
this.extentRevision_ = -1;
/**
* @private
* @type {number}
*/
this.extentRevision_ = -1;
/**
* @protected
* @type {Object.<string, module:ol/geom/Geometry>}
*/
this.simplifiedGeometryCache = {};
/**
* @protected
* @type {Object.<string, module:ol/geom/Geometry>}
*/
this.simplifiedGeometryCache = {};
/**
* @protected
* @type {number}
*/
this.simplifiedGeometryMaxMinSquaredTolerance = 0;
/**
* @protected
* @type {number}
*/
this.simplifiedGeometryMaxMinSquaredTolerance = 0;
/**
* @protected
* @type {number}
*/
this.simplifiedGeometryRevision = 0;
/**
* @protected
* @type {number}
*/
this.simplifiedGeometryRevision = 0;
};
}
/**
* Make a complete copy of the geometry.
* @abstract
* @return {!module:ol/geom/Geometry} Clone.
*/
clone() {}
/**
* @abstract
* @param {number} x X.
* @param {number} y Y.
* @param {module:ol/coordinate~Coordinate} closestPoint Closest point.
* @param {number} minSquaredDistance Minimum squared distance.
* @return {number} Minimum squared distance.
*/
closestPointXY(x, y, closestPoint, minSquaredDistance) {}
/**
* Return the closest point of the geometry to the passed point as
* {@link module:ol/coordinate~Coordinate coordinate}.
* @param {module:ol/coordinate~Coordinate} point Point.
* @param {module:ol/coordinate~Coordinate=} opt_closestPoint Closest point.
* @return {module:ol/coordinate~Coordinate} Closest point.
* @api
*/
getClosestPoint(point, opt_closestPoint) {
const closestPoint = opt_closestPoint ? opt_closestPoint : [NaN, NaN];
this.closestPointXY(point[0], point[1], closestPoint, Infinity);
return closestPoint;
}
/**
* Returns true if this geometry includes the specified coordinate. If the
* coordinate is on the boundary of the geometry, returns false.
* @param {module:ol/coordinate~Coordinate} coordinate Coordinate.
* @return {boolean} Contains coordinate.
* @api
*/
intersectsCoordinate(coordinate) {
return this.containsXY(coordinate[0], coordinate[1]);
}
/**
* @abstract
* @param {module:ol/extent~Extent} extent Extent.
* @protected
* @return {module:ol/extent~Extent} extent Extent.
*/
computeExtent(extent) {}
/**
* Get the extent of the geometry.
* @param {module:ol/extent~Extent=} opt_extent Extent.
* @return {module:ol/extent~Extent} extent Extent.
* @api
*/
getExtent(opt_extent) {
if (this.extentRevision_ != this.getRevision()) {
this.extent_ = this.computeExtent(this.extent_);
this.extentRevision_ = this.getRevision();
}
return returnOrUpdate(this.extent_, opt_extent);
}
/**
* Rotate the geometry around a given coordinate. This modifies the geometry
* coordinates in place.
* @abstract
* @param {number} angle Rotation angle in radians.
* @param {module:ol/coordinate~Coordinate} anchor The rotation center.
* @api
*/
rotate(angle, anchor) {}
/**
* Scale the geometry (with an optional origin). This modifies the geometry
* coordinates in place.
* @abstract
* @param {number} sx The scaling factor in the x-direction.
* @param {number=} opt_sy The scaling factor in the y-direction (defaults to
* sx).
* @param {module:ol/coordinate~Coordinate=} opt_anchor The scale origin (defaults to the center
* of the geometry extent).
* @api
*/
scale(sx, opt_sy, opt_anchor) {}
/**
* Create a simplified version of this geometry. For linestrings, this uses
* the the {@link
* https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
* Douglas Peucker} algorithm. For polygons, a quantization-based
* simplification is used to preserve topology.
* @function
* @param {number} tolerance The tolerance distance for simplification.
* @return {module:ol/geom/Geometry} A new, simplified version of the original
* geometry.
* @api
*/
simplify(tolerance) {
return this.getSimplifiedGeometry(tolerance * tolerance);
}
/**
* Create a simplified version of this geometry using the Douglas Peucker
* algorithm.
* @see https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
* @abstract
* @param {number} squaredTolerance Squared tolerance.
* @return {module:ol/geom/Geometry} Simplified geometry.
*/
getSimplifiedGeometry(squaredTolerance) {}
/**
* Get the type of this geometry.
* @abstract
* @return {module:ol/geom/GeometryType} Geometry type.
*/
getType() {}
/**
* Apply a transform function to each coordinate of the geometry.
* The geometry is modified in place.
* If you do not want the geometry modified in place, first `clone()` it and
* then use this function on the clone.
* @abstract
* @param {module:ol/proj~TransformFunction} transformFn Transform.
*/
applyTransform(transformFn) {}
/**
* Test if the geometry and the passed extent intersect.
* @abstract
* @param {module:ol/extent~Extent} extent Extent.
* @return {boolean} `true` if the geometry and the extent intersect.
*/
intersectsExtent(extent) {}
/**
* Translate the geometry. This modifies the geometry coordinates in place. If
* instead you want a new geometry, first `clone()` this geometry.
* @abstract
* @param {number} deltaX Delta X.
* @param {number} deltaY Delta Y.
*/
translate(deltaX, deltaY) {}
/**
* Transform each coordinate of the geometry from one coordinate reference
* system to another. The geometry is modified in place.
* For example, a line will be transformed to a line and a circle to a circle.
* If you do not want the geometry modified in place, first `clone()` it and
* then use this function on the clone.
*
* @param {module:ol/proj~ProjectionLike} source The current projection. Can be a
* string identifier or a {@link module:ol/proj/Projection~Projection} object.
* @param {module:ol/proj~ProjectionLike} destination The desired projection. Can be a
* string identifier or a {@link module:ol/proj/Projection~Projection} object.
* @return {module:ol/geom/Geometry} This geometry. Note that original geometry is
* modified in place.
* @api
*/
transform(source, destination) {
source = getProjection(source);
const transformFn = source.getUnits() == Units.TILE_PIXELS ?
function(inCoordinates, outCoordinates, stride) {
const pixelExtent = source.getExtent();
const projectedExtent = source.getWorldExtent();
const scale = getHeight(projectedExtent) / getHeight(pixelExtent);
composeTransform(tmpTransform,
projectedExtent[0], projectedExtent[3],
scale, -scale, 0,
0, 0);
transform2D(inCoordinates, 0, inCoordinates.length, stride,
tmpTransform, outCoordinates);
return getTransform(source, destination)(inCoordinates, outCoordinates, stride);
} :
getTransform(source, destination);
this.applyTransform(transformFn);
return this;
}
}
inherits(Geometry, BaseObject);
@@ -70,61 +254,6 @@ inherits(Geometry, BaseObject);
const tmpTransform = createTransform();
/**
* Make a complete copy of the geometry.
* @abstract
* @return {!module:ol/geom/Geometry} Clone.
*/
Geometry.prototype.clone = function() {};
/**
* @abstract
* @param {number} x X.
* @param {number} y Y.
* @param {module:ol/coordinate~Coordinate} closestPoint Closest point.
* @param {number} minSquaredDistance Minimum squared distance.
* @return {number} Minimum squared distance.
*/
Geometry.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {};
/**
* Return the closest point of the geometry to the passed point as
* {@link module:ol/coordinate~Coordinate coordinate}.
* @param {module:ol/coordinate~Coordinate} point Point.
* @param {module:ol/coordinate~Coordinate=} opt_closestPoint Closest point.
* @return {module:ol/coordinate~Coordinate} Closest point.
* @api
*/
Geometry.prototype.getClosestPoint = function(point, opt_closestPoint) {
const closestPoint = opt_closestPoint ? opt_closestPoint : [NaN, NaN];
this.closestPointXY(point[0], point[1], closestPoint, Infinity);
return closestPoint;
};
/**
* Returns true if this geometry includes the specified coordinate. If the
* coordinate is on the boundary of the geometry, returns false.
* @param {module:ol/coordinate~Coordinate} coordinate Coordinate.
* @return {boolean} Contains coordinate.
* @api
*/
Geometry.prototype.intersectsCoordinate = function(coordinate) {
return this.containsXY(coordinate[0], coordinate[1]);
};
/**
* @abstract
* @param {module:ol/extent~Extent} extent Extent.
* @protected
* @return {module:ol/extent~Extent} extent Extent.
*/
Geometry.prototype.computeExtent = function(extent) {};
/**
* @param {number} x X.
* @param {number} y Y.
@@ -133,144 +262,4 @@ Geometry.prototype.computeExtent = function(extent) {};
Geometry.prototype.containsXY = FALSE;
/**
* Get the extent of the geometry.
* @param {module:ol/extent~Extent=} opt_extent Extent.
* @return {module:ol/extent~Extent} extent Extent.
* @api
*/
Geometry.prototype.getExtent = function(opt_extent) {
if (this.extentRevision_ != this.getRevision()) {
this.extent_ = this.computeExtent(this.extent_);
this.extentRevision_ = this.getRevision();
}
return returnOrUpdate(this.extent_, opt_extent);
};
/**
* Rotate the geometry around a given coordinate. This modifies the geometry
* coordinates in place.
* @abstract
* @param {number} angle Rotation angle in radians.
* @param {module:ol/coordinate~Coordinate} anchor The rotation center.
* @api
*/
Geometry.prototype.rotate = function(angle, anchor) {};
/**
* Scale the geometry (with an optional origin). This modifies the geometry
* coordinates in place.
* @abstract
* @param {number} sx The scaling factor in the x-direction.
* @param {number=} opt_sy The scaling factor in the y-direction (defaults to
* sx).
* @param {module:ol/coordinate~Coordinate=} opt_anchor The scale origin (defaults to the center
* of the geometry extent).
* @api
*/
Geometry.prototype.scale = function(sx, opt_sy, opt_anchor) {};
/**
* Create a simplified version of this geometry. For linestrings, this uses
* the the {@link
* https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
* Douglas Peucker} algorithm. For polygons, a quantization-based
* simplification is used to preserve topology.
* @function
* @param {number} tolerance The tolerance distance for simplification.
* @return {module:ol/geom/Geometry} A new, simplified version of the original
* geometry.
* @api
*/
Geometry.prototype.simplify = function(tolerance) {
return this.getSimplifiedGeometry(tolerance * tolerance);
};
/**
* Create a simplified version of this geometry using the Douglas Peucker
* algorithm.
* @see https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
* @abstract
* @param {number} squaredTolerance Squared tolerance.
* @return {module:ol/geom/Geometry} Simplified geometry.
*/
Geometry.prototype.getSimplifiedGeometry = function(squaredTolerance) {};
/**
* Get the type of this geometry.
* @abstract
* @return {module:ol/geom/GeometryType} Geometry type.
*/
Geometry.prototype.getType = function() {};
/**
* Apply a transform function to each coordinate of the geometry.
* The geometry is modified in place.
* If you do not want the geometry modified in place, first `clone()` it and
* then use this function on the clone.
* @abstract
* @param {module:ol/proj~TransformFunction} transformFn Transform.
*/
Geometry.prototype.applyTransform = function(transformFn) {};
/**
* Test if the geometry and the passed extent intersect.
* @abstract
* @param {module:ol/extent~Extent} extent Extent.
* @return {boolean} `true` if the geometry and the extent intersect.
*/
Geometry.prototype.intersectsExtent = function(extent) {};
/**
* Translate the geometry. This modifies the geometry coordinates in place. If
* instead you want a new geometry, first `clone()` this geometry.
* @abstract
* @param {number} deltaX Delta X.
* @param {number} deltaY Delta Y.
*/
Geometry.prototype.translate = function(deltaX, deltaY) {};
/**
* Transform each coordinate of the geometry from one coordinate reference
* system to another. The geometry is modified in place.
* For example, a line will be transformed to a line and a circle to a circle.
* If you do not want the geometry modified in place, first `clone()` it and
* then use this function on the clone.
*
* @param {module:ol/proj~ProjectionLike} source The current projection. Can be a
* string identifier or a {@link module:ol/proj/Projection~Projection} object.
* @param {module:ol/proj~ProjectionLike} destination The desired projection. Can be a
* string identifier or a {@link module:ol/proj/Projection~Projection} object.
* @return {module:ol/geom/Geometry} This geometry. Note that original geometry is
* modified in place.
* @api
*/
Geometry.prototype.transform = function(source, destination) {
source = getProjection(source);
const transformFn = source.getUnits() == Units.TILE_PIXELS ?
function(inCoordinates, outCoordinates, stride) {
const pixelExtent = source.getExtent();
const projectedExtent = source.getWorldExtent();
const scale = getHeight(projectedExtent) / getHeight(pixelExtent);
composeTransform(tmpTransform,
projectedExtent[0], projectedExtent[3],
scale, -scale, 0,
0, 0);
transform2D(inCoordinates, 0, inCoordinates.length, stride,
tmpTransform, outCoordinates);
return getTransform(source, destination)(inCoordinates, outCoordinates, stride);
} :
getTransform(source, destination);
this.applyTransform(transformFn);
return this;
};
export default Geometry;

View File

@@ -18,18 +18,268 @@ import {clear} from '../obj.js';
* @param {Array.<module:ol/geom/Geometry>=} opt_geometries Geometries.
* @api
*/
const GeometryCollection = function(opt_geometries) {
class GeometryCollection {
constructor(opt_geometries) {
Geometry.call(this);
Geometry.call(this);
/**
* @private
* @type {Array.<module:ol/geom/Geometry>}
*/
this.geometries_ = opt_geometries ? opt_geometries : null;
this.listenGeometriesChange_();
}
/**
* @private
* @type {Array.<module:ol/geom/Geometry>}
*/
this.geometries_ = opt_geometries ? opt_geometries : null;
unlistenGeometriesChange_() {
if (!this.geometries_) {
return;
}
for (let i = 0, ii = this.geometries_.length; i < ii; ++i) {
unlisten(
this.geometries_[i], EventType.CHANGE,
this.changed, this);
}
}
this.listenGeometriesChange_();
};
/**
* @private
*/
listenGeometriesChange_() {
if (!this.geometries_) {
return;
}
for (let i = 0, ii = this.geometries_.length; i < ii; ++i) {
listen(
this.geometries_[i], EventType.CHANGE,
this.changed, this);
}
}
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/GeometryCollection} Clone.
* @override
* @api
*/
clone() {
const geometryCollection = new GeometryCollection(null);
geometryCollection.setGeometries(this.geometries_);
return geometryCollection;
}
/**
* @inheritDoc
*/
closestPointXY(x, y, closestPoint, minSquaredDistance) {
if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
return minSquaredDistance;
}
const geometries = this.geometries_;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
minSquaredDistance = geometries[i].closestPointXY(
x, y, closestPoint, minSquaredDistance);
}
return minSquaredDistance;
}
/**
* @inheritDoc
*/
containsXY(x, y) {
const geometries = this.geometries_;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
if (geometries[i].containsXY(x, y)) {
return true;
}
}
return false;
}
/**
* @inheritDoc
*/
computeExtent(extent) {
createOrUpdateEmpty(extent);
const geometries = this.geometries_;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
extend(extent, geometries[i].getExtent());
}
return extent;
}
/**
* Return the geometries that make up this geometry collection.
* @return {Array.<module:ol/geom/Geometry>} Geometries.
* @api
*/
getGeometries() {
return cloneGeometries(this.geometries_);
}
/**
* @return {Array.<module:ol/geom/Geometry>} Geometries.
*/
getGeometriesArray() {
return this.geometries_;
}
/**
* @inheritDoc
*/
getSimplifiedGeometry(squaredTolerance) {
if (this.simplifiedGeometryRevision != this.getRevision()) {
clear(this.simplifiedGeometryCache);
this.simplifiedGeometryMaxMinSquaredTolerance = 0;
this.simplifiedGeometryRevision = this.getRevision();
}
if (squaredTolerance < 0 ||
(this.simplifiedGeometryMaxMinSquaredTolerance !== 0 &&
squaredTolerance < this.simplifiedGeometryMaxMinSquaredTolerance)) {
return this;
}
const key = squaredTolerance.toString();
if (this.simplifiedGeometryCache.hasOwnProperty(key)) {
return this.simplifiedGeometryCache[key];
} else {
const simplifiedGeometries = [];
const geometries = this.geometries_;
let simplified = false;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
const geometry = geometries[i];
const simplifiedGeometry = geometry.getSimplifiedGeometry(squaredTolerance);
simplifiedGeometries.push(simplifiedGeometry);
if (simplifiedGeometry !== geometry) {
simplified = true;
}
}
if (simplified) {
const simplifiedGeometryCollection = new GeometryCollection(null);
simplifiedGeometryCollection.setGeometriesArray(simplifiedGeometries);
this.simplifiedGeometryCache[key] = simplifiedGeometryCollection;
return simplifiedGeometryCollection;
} else {
this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance;
return this;
}
}
}
/**
* @inheritDoc
* @api
*/
getType() {
return GeometryType.GEOMETRY_COLLECTION;
}
/**
* @inheritDoc
* @api
*/
intersectsExtent(extent) {
const geometries = this.geometries_;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
if (geometries[i].intersectsExtent(extent)) {
return true;
}
}
return false;
}
/**
* @return {boolean} Is empty.
*/
isEmpty() {
return this.geometries_.length === 0;
}
/**
* @inheritDoc
* @api
*/
rotate(angle, anchor) {
const geometries = this.geometries_;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
geometries[i].rotate(angle, anchor);
}
this.changed();
}
/**
* @inheritDoc
* @api
*/
scale(sx, opt_sy, opt_anchor) {
let anchor = opt_anchor;
if (!anchor) {
anchor = getCenter(this.getExtent());
}
const geometries = this.geometries_;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
geometries[i].scale(sx, opt_sy, anchor);
}
this.changed();
}
/**
* Set the geometries that make up this geometry collection.
* @param {Array.<module:ol/geom/Geometry>} geometries Geometries.
* @api
*/
setGeometries(geometries) {
this.setGeometriesArray(cloneGeometries(geometries));
}
/**
* @param {Array.<module:ol/geom/Geometry>} geometries Geometries.
*/
setGeometriesArray(geometries) {
this.unlistenGeometriesChange_();
this.geometries_ = geometries;
this.listenGeometriesChange_();
this.changed();
}
/**
* @inheritDoc
* @api
*/
applyTransform(transformFn) {
const geometries = this.geometries_;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
geometries[i].applyTransform(transformFn);
}
this.changed();
}
/**
* Translate the geometry.
* @param {number} deltaX Delta X.
* @param {number} deltaY Delta Y.
* @override
* @api
*/
translate(deltaX, deltaY) {
const geometries = this.geometries_;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
geometries[i].translate(deltaX, deltaY);
}
this.changed();
}
/**
* @inheritDoc
*/
disposeInternal() {
this.unlistenGeometriesChange_();
Geometry.prototype.disposeInternal.call(this);
}
}
inherits(GeometryCollection, Geometry);
@@ -47,269 +297,4 @@ function cloneGeometries(geometries) {
}
/**
* @private
*/
GeometryCollection.prototype.unlistenGeometriesChange_ = function() {
if (!this.geometries_) {
return;
}
for (let i = 0, ii = this.geometries_.length; i < ii; ++i) {
unlisten(
this.geometries_[i], EventType.CHANGE,
this.changed, this);
}
};
/**
* @private
*/
GeometryCollection.prototype.listenGeometriesChange_ = function() {
if (!this.geometries_) {
return;
}
for (let i = 0, ii = this.geometries_.length; i < ii; ++i) {
listen(
this.geometries_[i], EventType.CHANGE,
this.changed, this);
}
};
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/GeometryCollection} Clone.
* @override
* @api
*/
GeometryCollection.prototype.clone = function() {
const geometryCollection = new GeometryCollection(null);
geometryCollection.setGeometries(this.geometries_);
return geometryCollection;
};
/**
* @inheritDoc
*/
GeometryCollection.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
return minSquaredDistance;
}
const geometries = this.geometries_;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
minSquaredDistance = geometries[i].closestPointXY(
x, y, closestPoint, minSquaredDistance);
}
return minSquaredDistance;
};
/**
* @inheritDoc
*/
GeometryCollection.prototype.containsXY = function(x, y) {
const geometries = this.geometries_;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
if (geometries[i].containsXY(x, y)) {
return true;
}
}
return false;
};
/**
* @inheritDoc
*/
GeometryCollection.prototype.computeExtent = function(extent) {
createOrUpdateEmpty(extent);
const geometries = this.geometries_;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
extend(extent, geometries[i].getExtent());
}
return extent;
};
/**
* Return the geometries that make up this geometry collection.
* @return {Array.<module:ol/geom/Geometry>} Geometries.
* @api
*/
GeometryCollection.prototype.getGeometries = function() {
return cloneGeometries(this.geometries_);
};
/**
* @return {Array.<module:ol/geom/Geometry>} Geometries.
*/
GeometryCollection.prototype.getGeometriesArray = function() {
return this.geometries_;
};
/**
* @inheritDoc
*/
GeometryCollection.prototype.getSimplifiedGeometry = function(squaredTolerance) {
if (this.simplifiedGeometryRevision != this.getRevision()) {
clear(this.simplifiedGeometryCache);
this.simplifiedGeometryMaxMinSquaredTolerance = 0;
this.simplifiedGeometryRevision = this.getRevision();
}
if (squaredTolerance < 0 ||
(this.simplifiedGeometryMaxMinSquaredTolerance !== 0 &&
squaredTolerance < this.simplifiedGeometryMaxMinSquaredTolerance)) {
return this;
}
const key = squaredTolerance.toString();
if (this.simplifiedGeometryCache.hasOwnProperty(key)) {
return this.simplifiedGeometryCache[key];
} else {
const simplifiedGeometries = [];
const geometries = this.geometries_;
let simplified = false;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
const geometry = geometries[i];
const simplifiedGeometry = geometry.getSimplifiedGeometry(squaredTolerance);
simplifiedGeometries.push(simplifiedGeometry);
if (simplifiedGeometry !== geometry) {
simplified = true;
}
}
if (simplified) {
const simplifiedGeometryCollection = new GeometryCollection(null);
simplifiedGeometryCollection.setGeometriesArray(simplifiedGeometries);
this.simplifiedGeometryCache[key] = simplifiedGeometryCollection;
return simplifiedGeometryCollection;
} else {
this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance;
return this;
}
}
};
/**
* @inheritDoc
* @api
*/
GeometryCollection.prototype.getType = function() {
return GeometryType.GEOMETRY_COLLECTION;
};
/**
* @inheritDoc
* @api
*/
GeometryCollection.prototype.intersectsExtent = function(extent) {
const geometries = this.geometries_;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
if (geometries[i].intersectsExtent(extent)) {
return true;
}
}
return false;
};
/**
* @return {boolean} Is empty.
*/
GeometryCollection.prototype.isEmpty = function() {
return this.geometries_.length === 0;
};
/**
* @inheritDoc
* @api
*/
GeometryCollection.prototype.rotate = function(angle, anchor) {
const geometries = this.geometries_;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
geometries[i].rotate(angle, anchor);
}
this.changed();
};
/**
* @inheritDoc
* @api
*/
GeometryCollection.prototype.scale = function(sx, opt_sy, opt_anchor) {
let anchor = opt_anchor;
if (!anchor) {
anchor = getCenter(this.getExtent());
}
const geometries = this.geometries_;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
geometries[i].scale(sx, opt_sy, anchor);
}
this.changed();
};
/**
* Set the geometries that make up this geometry collection.
* @param {Array.<module:ol/geom/Geometry>} geometries Geometries.
* @api
*/
GeometryCollection.prototype.setGeometries = function(geometries) {
this.setGeometriesArray(cloneGeometries(geometries));
};
/**
* @param {Array.<module:ol/geom/Geometry>} geometries Geometries.
*/
GeometryCollection.prototype.setGeometriesArray = function(geometries) {
this.unlistenGeometriesChange_();
this.geometries_ = geometries;
this.listenGeometriesChange_();
this.changed();
};
/**
* @inheritDoc
* @api
*/
GeometryCollection.prototype.applyTransform = function(transformFn) {
const geometries = this.geometries_;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
geometries[i].applyTransform(transformFn);
}
this.changed();
};
/**
* Translate the geometry.
* @param {number} deltaX Delta X.
* @param {number} deltaY Delta Y.
* @override
* @api
*/
GeometryCollection.prototype.translate = function(deltaX, deltaY) {
const geometries = this.geometries_;
for (let i = 0, ii = geometries.length; i < ii; ++i) {
geometries[i].translate(deltaX, deltaY);
}
this.changed();
};
/**
* @inheritDoc
*/
GeometryCollection.prototype.disposeInternal = function() {
this.unlistenGeometriesChange_();
Geometry.prototype.disposeInternal.call(this);
};
export default GeometryCollection;

View File

@@ -28,229 +28,219 @@ import {douglasPeucker} from '../geom/flat/simplify.js';
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @api
*/
const LineString = function(coordinates, opt_layout) {
class LineString {
constructor(coordinates, opt_layout) {
SimpleGeometry.call(this);
SimpleGeometry.call(this);
/**
* @private
* @type {module:ol/coordinate~Coordinate}
*/
this.flatMidpoint_ = null;
/**
* @private
* @type {module:ol/coordinate~Coordinate}
*/
this.flatMidpoint_ = null;
/**
* @private
* @type {number}
*/
this.flatMidpointRevision_ = -1;
/**
* @private
* @type {number}
*/
this.flatMidpointRevision_ = -1;
/**
* @private
* @type {number}
*/
this.maxDelta_ = -1;
/**
* @private
* @type {number}
*/
this.maxDelta_ = -1;
/**
* @private
* @type {number}
*/
this.maxDeltaRevision_ = -1;
/**
* @private
* @type {number}
*/
this.maxDeltaRevision_ = -1;
if (opt_layout !== undefined && !Array.isArray(coordinates[0])) {
this.setFlatCoordinates(opt_layout, coordinates);
} else {
this.setCoordinates(coordinates, opt_layout);
}
if (opt_layout !== undefined && !Array.isArray(coordinates[0])) {
this.setFlatCoordinates(opt_layout, coordinates);
} else {
this.setCoordinates(coordinates, opt_layout);
}
};
/**
* Append the passed coordinate to the coordinates of the linestring.
* @param {module:ol/coordinate~Coordinate} coordinate Coordinate.
* @api
*/
appendCoordinate(coordinate) {
if (!this.flatCoordinates) {
this.flatCoordinates = coordinate.slice();
} else {
extend(this.flatCoordinates, coordinate);
}
this.changed();
}
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/LineString} Clone.
* @override
* @api
*/
clone() {
return new LineString(this.flatCoordinates.slice(), this.layout);
}
/**
* @inheritDoc
*/
closestPointXY(x, y, closestPoint, minSquaredDistance) {
if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
return minSquaredDistance;
}
if (this.maxDeltaRevision_ != this.getRevision()) {
this.maxDelta_ = Math.sqrt(maxSquaredDelta(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0));
this.maxDeltaRevision_ = this.getRevision();
}
return assignClosestPoint(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
this.maxDelta_, false, x, y, closestPoint, minSquaredDistance);
}
/**
* Iterate over each segment, calling the provided callback.
* If the callback returns a truthy value the function returns that
* value immediately. Otherwise the function returns `false`.
*
* @param {function(this: S, module:ol/coordinate~Coordinate, module:ol/coordinate~Coordinate): T} callback Function
* called for each segment.
* @return {T|boolean} Value.
* @template T,S
* @api
*/
forEachSegment(callback) {
return forEachSegment(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, callback);
}
/**
* Returns the coordinate at `m` using linear interpolation, or `null` if no
* such coordinate exists.
*
* `opt_extrapolate` controls extrapolation beyond the range of Ms in the
* MultiLineString. If `opt_extrapolate` is `true` then Ms less than the first
* M will return the first coordinate and Ms greater than the last M will
* return the last coordinate.
*
* @param {number} m M.
* @param {boolean=} opt_extrapolate Extrapolate. Default is `false`.
* @return {module:ol/coordinate~Coordinate} Coordinate.
* @api
*/
getCoordinateAtM(m, opt_extrapolate) {
if (this.layout != GeometryLayout.XYM &&
this.layout != GeometryLayout.XYZM) {
return null;
}
const extrapolate = opt_extrapolate !== undefined ? opt_extrapolate : false;
return lineStringCoordinateAtM(this.flatCoordinates, 0,
this.flatCoordinates.length, this.stride, m, extrapolate);
}
/**
* Return the coordinates of the linestring.
* @return {Array.<module:ol/coordinate~Coordinate>} Coordinates.
* @override
* @api
*/
getCoordinates() {
return inflateCoordinates(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
}
/**
* Return the coordinate at the provided fraction along the linestring.
* The `fraction` is a number between 0 and 1, where 0 is the start of the
* linestring and 1 is the end.
* @param {number} fraction Fraction.
* @param {module:ol/coordinate~Coordinate=} opt_dest Optional coordinate whose values will
* be modified. If not provided, a new coordinate will be returned.
* @return {module:ol/coordinate~Coordinate} Coordinate of the interpolated point.
* @api
*/
getCoordinateAt(fraction, opt_dest) {
return interpolatePoint(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
fraction, opt_dest);
}
/**
* Return the length of the linestring on projected plane.
* @return {number} Length (on projected plane).
* @api
*/
getLength() {
return lineStringLength(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
}
/**
* @return {Array.<number>} Flat midpoint.
*/
getFlatMidpoint() {
if (this.flatMidpointRevision_ != this.getRevision()) {
this.flatMidpoint_ = this.getCoordinateAt(0.5, this.flatMidpoint_);
this.flatMidpointRevision_ = this.getRevision();
}
return this.flatMidpoint_;
}
/**
* @inheritDoc
*/
getSimplifiedGeometryInternal(squaredTolerance) {
const simplifiedFlatCoordinates = [];
simplifiedFlatCoordinates.length = douglasPeucker(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
squaredTolerance, simplifiedFlatCoordinates, 0);
return new LineString(simplifiedFlatCoordinates, GeometryLayout.XY);
}
/**
* @inheritDoc
* @api
*/
getType() {
return GeometryType.LINE_STRING;
}
/**
* @inheritDoc
* @api
*/
intersectsExtent(extent) {
return intersectsLineString(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
extent);
}
/**
* Set the coordinates of the linestring.
* @param {!Array.<module:ol/coordinate~Coordinate>} coordinates Coordinates.
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @override
* @api
*/
setCoordinates(coordinates, opt_layout) {
this.setLayout(opt_layout, coordinates, 1);
if (!this.flatCoordinates) {
this.flatCoordinates = [];
}
this.flatCoordinates.length = deflateCoordinates(
this.flatCoordinates, 0, coordinates, this.stride);
this.changed();
}
}
inherits(LineString, SimpleGeometry);
/**
* Append the passed coordinate to the coordinates of the linestring.
* @param {module:ol/coordinate~Coordinate} coordinate Coordinate.
* @api
*/
LineString.prototype.appendCoordinate = function(coordinate) {
if (!this.flatCoordinates) {
this.flatCoordinates = coordinate.slice();
} else {
extend(this.flatCoordinates, coordinate);
}
this.changed();
};
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/LineString} Clone.
* @override
* @api
*/
LineString.prototype.clone = function() {
return new LineString(this.flatCoordinates.slice(), this.layout);
};
/**
* @inheritDoc
*/
LineString.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
return minSquaredDistance;
}
if (this.maxDeltaRevision_ != this.getRevision()) {
this.maxDelta_ = Math.sqrt(maxSquaredDelta(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0));
this.maxDeltaRevision_ = this.getRevision();
}
return assignClosestPoint(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
this.maxDelta_, false, x, y, closestPoint, minSquaredDistance);
};
/**
* Iterate over each segment, calling the provided callback.
* If the callback returns a truthy value the function returns that
* value immediately. Otherwise the function returns `false`.
*
* @param {function(this: S, module:ol/coordinate~Coordinate, module:ol/coordinate~Coordinate): T} callback Function
* called for each segment.
* @return {T|boolean} Value.
* @template T,S
* @api
*/
LineString.prototype.forEachSegment = function(callback) {
return forEachSegment(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, callback);
};
/**
* Returns the coordinate at `m` using linear interpolation, or `null` if no
* such coordinate exists.
*
* `opt_extrapolate` controls extrapolation beyond the range of Ms in the
* MultiLineString. If `opt_extrapolate` is `true` then Ms less than the first
* M will return the first coordinate and Ms greater than the last M will
* return the last coordinate.
*
* @param {number} m M.
* @param {boolean=} opt_extrapolate Extrapolate. Default is `false`.
* @return {module:ol/coordinate~Coordinate} Coordinate.
* @api
*/
LineString.prototype.getCoordinateAtM = function(m, opt_extrapolate) {
if (this.layout != GeometryLayout.XYM &&
this.layout != GeometryLayout.XYZM) {
return null;
}
const extrapolate = opt_extrapolate !== undefined ? opt_extrapolate : false;
return lineStringCoordinateAtM(this.flatCoordinates, 0,
this.flatCoordinates.length, this.stride, m, extrapolate);
};
/**
* Return the coordinates of the linestring.
* @return {Array.<module:ol/coordinate~Coordinate>} Coordinates.
* @override
* @api
*/
LineString.prototype.getCoordinates = function() {
return inflateCoordinates(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
};
/**
* Return the coordinate at the provided fraction along the linestring.
* The `fraction` is a number between 0 and 1, where 0 is the start of the
* linestring and 1 is the end.
* @param {number} fraction Fraction.
* @param {module:ol/coordinate~Coordinate=} opt_dest Optional coordinate whose values will
* be modified. If not provided, a new coordinate will be returned.
* @return {module:ol/coordinate~Coordinate} Coordinate of the interpolated point.
* @api
*/
LineString.prototype.getCoordinateAt = function(fraction, opt_dest) {
return interpolatePoint(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
fraction, opt_dest);
};
/**
* Return the length of the linestring on projected plane.
* @return {number} Length (on projected plane).
* @api
*/
LineString.prototype.getLength = function() {
return lineStringLength(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
};
/**
* @return {Array.<number>} Flat midpoint.
*/
LineString.prototype.getFlatMidpoint = function() {
if (this.flatMidpointRevision_ != this.getRevision()) {
this.flatMidpoint_ = this.getCoordinateAt(0.5, this.flatMidpoint_);
this.flatMidpointRevision_ = this.getRevision();
}
return this.flatMidpoint_;
};
/**
* @inheritDoc
*/
LineString.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) {
const simplifiedFlatCoordinates = [];
simplifiedFlatCoordinates.length = douglasPeucker(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
squaredTolerance, simplifiedFlatCoordinates, 0);
return new LineString(simplifiedFlatCoordinates, GeometryLayout.XY);
};
/**
* @inheritDoc
* @api
*/
LineString.prototype.getType = function() {
return GeometryType.LINE_STRING;
};
/**
* @inheritDoc
* @api
*/
LineString.prototype.intersectsExtent = function(extent) {
return intersectsLineString(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
extent);
};
/**
* Set the coordinates of the linestring.
* @param {!Array.<module:ol/coordinate~Coordinate>} coordinates Coordinates.
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @override
* @api
*/
LineString.prototype.setCoordinates = function(coordinates, opt_layout) {
this.setLayout(opt_layout, coordinates, 1);
if (!this.flatCoordinates) {
this.flatCoordinates = [];
}
this.flatCoordinates.length = deflateCoordinates(
this.flatCoordinates, 0, coordinates, this.stride);
this.changed();
};
export default LineString;

View File

@@ -25,125 +25,121 @@ import {douglasPeucker} from '../geom/flat/simplify.js';
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @api
*/
const LinearRing = function(coordinates, opt_layout) {
class LinearRing {
constructor(coordinates, opt_layout) {
SimpleGeometry.call(this);
SimpleGeometry.call(this);
/**
* @private
* @type {number}
*/
this.maxDelta_ = -1;
/**
* @private
* @type {number}
*/
this.maxDelta_ = -1;
/**
* @private
* @type {number}
*/
this.maxDeltaRevision_ = -1;
/**
* @private
* @type {number}
*/
this.maxDeltaRevision_ = -1;
if (opt_layout !== undefined && !Array.isArray(coordinates[0])) {
this.setFlatCoordinates(opt_layout, coordinates);
} else {
this.setCoordinates(coordinates, opt_layout);
}
if (opt_layout !== undefined && !Array.isArray(coordinates[0])) {
this.setFlatCoordinates(opt_layout, coordinates);
} else {
this.setCoordinates(coordinates, opt_layout);
}
};
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/LinearRing} Clone.
* @override
* @api
*/
clone() {
return new LinearRing(this.flatCoordinates.slice(), this.layout);
}
/**
* @inheritDoc
*/
closestPointXY(x, y, closestPoint, minSquaredDistance) {
if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
return minSquaredDistance;
}
if (this.maxDeltaRevision_ != this.getRevision()) {
this.maxDelta_ = Math.sqrt(maxSquaredDelta(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0));
this.maxDeltaRevision_ = this.getRevision();
}
return assignClosestPoint(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
this.maxDelta_, true, x, y, closestPoint, minSquaredDistance);
}
/**
* Return the area of the linear ring on projected plane.
* @return {number} Area (on projected plane).
* @api
*/
getArea() {
return linearRingArea(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
}
/**
* Return the coordinates of the linear ring.
* @return {Array.<module:ol/coordinate~Coordinate>} Coordinates.
* @override
* @api
*/
getCoordinates() {
return inflateCoordinates(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
}
/**
* @inheritDoc
*/
getSimplifiedGeometryInternal(squaredTolerance) {
const simplifiedFlatCoordinates = [];
simplifiedFlatCoordinates.length = douglasPeucker(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
squaredTolerance, simplifiedFlatCoordinates, 0);
return new LinearRing(simplifiedFlatCoordinates, GeometryLayout.XY);
}
/**
* @inheritDoc
* @api
*/
getType() {
return GeometryType.LINEAR_RING;
}
/**
* @inheritDoc
*/
intersectsExtent(extent) {}
/**
* Set the coordinates of the linear ring.
* @param {!Array.<module:ol/coordinate~Coordinate>} coordinates Coordinates.
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @override
* @api
*/
setCoordinates(coordinates, opt_layout) {
this.setLayout(opt_layout, coordinates, 1);
if (!this.flatCoordinates) {
this.flatCoordinates = [];
}
this.flatCoordinates.length = deflateCoordinates(
this.flatCoordinates, 0, coordinates, this.stride);
this.changed();
}
}
inherits(LinearRing, SimpleGeometry);
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/LinearRing} Clone.
* @override
* @api
*/
LinearRing.prototype.clone = function() {
return new LinearRing(this.flatCoordinates.slice(), this.layout);
};
/**
* @inheritDoc
*/
LinearRing.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
return minSquaredDistance;
}
if (this.maxDeltaRevision_ != this.getRevision()) {
this.maxDelta_ = Math.sqrt(maxSquaredDelta(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0));
this.maxDeltaRevision_ = this.getRevision();
}
return assignClosestPoint(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
this.maxDelta_, true, x, y, closestPoint, minSquaredDistance);
};
/**
* Return the area of the linear ring on projected plane.
* @return {number} Area (on projected plane).
* @api
*/
LinearRing.prototype.getArea = function() {
return linearRingArea(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
};
/**
* Return the coordinates of the linear ring.
* @return {Array.<module:ol/coordinate~Coordinate>} Coordinates.
* @override
* @api
*/
LinearRing.prototype.getCoordinates = function() {
return inflateCoordinates(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
};
/**
* @inheritDoc
*/
LinearRing.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) {
const simplifiedFlatCoordinates = [];
simplifiedFlatCoordinates.length = douglasPeucker(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
squaredTolerance, simplifiedFlatCoordinates, 0);
return new LinearRing(simplifiedFlatCoordinates, GeometryLayout.XY);
};
/**
* @inheritDoc
* @api
*/
LinearRing.prototype.getType = function() {
return GeometryType.LINEAR_RING;
};
/**
* @inheritDoc
*/
LinearRing.prototype.intersectsExtent = function(extent) {};
/**
* Set the coordinates of the linear ring.
* @param {!Array.<module:ol/coordinate~Coordinate>} coordinates Coordinates.
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @override
* @api
*/
LinearRing.prototype.setCoordinates = function(coordinates, opt_layout) {
this.setLayout(opt_layout, coordinates, 1);
if (!this.flatCoordinates) {
this.flatCoordinates = [];
}
this.flatCoordinates.length = deflateCoordinates(
this.flatCoordinates, 0, coordinates, this.stride);
this.changed();
};
export default LinearRing;

View File

@@ -28,258 +28,249 @@ import {douglasPeuckerArray} from '../geom/flat/simplify.js';
* @param {Array.<number>} opt_ends Flat coordinate ends for internal use.
* @api
*/
const MultiLineString = function(coordinates, opt_layout, opt_ends) {
class MultiLineString {
constructor(coordinates, opt_layout, opt_ends) {
SimpleGeometry.call(this);
SimpleGeometry.call(this);
/**
* @type {Array.<number>}
* @private
*/
this.ends_ = [];
/**
* @type {Array.<number>}
* @private
*/
this.ends_ = [];
/**
* @private
* @type {number}
*/
this.maxDelta_ = -1;
/**
* @private
* @type {number}
*/
this.maxDelta_ = -1;
/**
* @private
* @type {number}
*/
this.maxDeltaRevision_ = -1;
/**
* @private
* @type {number}
*/
this.maxDeltaRevision_ = -1;
if (Array.isArray(coordinates[0])) {
this.setCoordinates(coordinates, opt_layout);
} else if (opt_layout !== undefined && opt_ends) {
this.setFlatCoordinates(opt_layout, coordinates);
this.ends_ = opt_ends;
} else {
let layout = this.getLayout();
const flatCoordinates = [];
const ends = [];
for (let i = 0, ii = coordinates.length; i < ii; ++i) {
const lineString = coordinates[i];
if (i === 0) {
layout = lineString.getLayout();
if (Array.isArray(coordinates[0])) {
this.setCoordinates(coordinates, opt_layout);
} else if (opt_layout !== undefined && opt_ends) {
this.setFlatCoordinates(opt_layout, coordinates);
this.ends_ = opt_ends;
} else {
let layout = this.getLayout();
const flatCoordinates = [];
const ends = [];
for (let i = 0, ii = coordinates.length; i < ii; ++i) {
const lineString = coordinates[i];
if (i === 0) {
layout = lineString.getLayout();
}
extend(flatCoordinates, lineString.getFlatCoordinates());
ends.push(flatCoordinates.length);
}
extend(flatCoordinates, lineString.getFlatCoordinates());
ends.push(flatCoordinates.length);
this.setFlatCoordinates(layout, flatCoordinates);
this.ends_ = ends;
}
this.setFlatCoordinates(layout, flatCoordinates);
this.ends_ = ends;
}
};
/**
* Append the passed linestring to the multilinestring.
* @param {module:ol/geom/LineString} lineString LineString.
* @api
*/
appendLineString(lineString) {
if (!this.flatCoordinates) {
this.flatCoordinates = lineString.getFlatCoordinates().slice();
} else {
extend(this.flatCoordinates, lineString.getFlatCoordinates().slice());
}
this.ends_.push(this.flatCoordinates.length);
this.changed();
}
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/MultiLineString} Clone.
* @override
* @api
*/
clone() {
return new MultiLineString(this.flatCoordinates.slice(), this.layout, this.ends_.slice());
}
/**
* @inheritDoc
*/
closestPointXY(x, y, closestPoint, minSquaredDistance) {
if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
return minSquaredDistance;
}
if (this.maxDeltaRevision_ != this.getRevision()) {
this.maxDelta_ = Math.sqrt(arrayMaxSquaredDelta(
this.flatCoordinates, 0, this.ends_, this.stride, 0));
this.maxDeltaRevision_ = this.getRevision();
}
return assignClosestArrayPoint(
this.flatCoordinates, 0, this.ends_, this.stride,
this.maxDelta_, false, x, y, closestPoint, minSquaredDistance);
}
/**
* Returns the coordinate at `m` using linear interpolation, or `null` if no
* such coordinate exists.
*
* `opt_extrapolate` controls extrapolation beyond the range of Ms in the
* MultiLineString. If `opt_extrapolate` is `true` then Ms less than the first
* M will return the first coordinate and Ms greater than the last M will
* return the last coordinate.
*
* `opt_interpolate` controls interpolation between consecutive LineStrings
* within the MultiLineString. If `opt_interpolate` is `true` the coordinates
* will be linearly interpolated between the last coordinate of one LineString
* and the first coordinate of the next LineString. If `opt_interpolate` is
* `false` then the function will return `null` for Ms falling between
* LineStrings.
*
* @param {number} m M.
* @param {boolean=} opt_extrapolate Extrapolate. Default is `false`.
* @param {boolean=} opt_interpolate Interpolate. Default is `false`.
* @return {module:ol/coordinate~Coordinate} Coordinate.
* @api
*/
getCoordinateAtM(m, opt_extrapolate, opt_interpolate) {
if ((this.layout != GeometryLayout.XYM &&
this.layout != GeometryLayout.XYZM) ||
this.flatCoordinates.length === 0) {
return null;
}
const extrapolate = opt_extrapolate !== undefined ? opt_extrapolate : false;
const interpolate = opt_interpolate !== undefined ? opt_interpolate : false;
return lineStringsCoordinateAtM(this.flatCoordinates, 0,
this.ends_, this.stride, m, extrapolate, interpolate);
}
/**
* Return the coordinates of the multilinestring.
* @return {Array.<Array.<module:ol/coordinate~Coordinate>>} Coordinates.
* @override
* @api
*/
getCoordinates() {
return inflateCoordinatesArray(
this.flatCoordinates, 0, this.ends_, this.stride);
}
/**
* @return {Array.<number>} Ends.
*/
getEnds() {
return this.ends_;
}
/**
* Return the linestring at the specified index.
* @param {number} index Index.
* @return {module:ol/geom/LineString} LineString.
* @api
*/
getLineString(index) {
if (index < 0 || this.ends_.length <= index) {
return null;
}
return new LineString(this.flatCoordinates.slice(
index === 0 ? 0 : this.ends_[index - 1], this.ends_[index]), this.layout);
}
/**
* Return the linestrings of this multilinestring.
* @return {Array.<module:ol/geom/LineString>} LineStrings.
* @api
*/
getLineStrings() {
const flatCoordinates = this.flatCoordinates;
const ends = this.ends_;
const layout = this.layout;
/** @type {Array.<module:ol/geom/LineString>} */
const lineStrings = [];
let offset = 0;
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
const lineString = new LineString(flatCoordinates.slice(offset, end), layout);
lineStrings.push(lineString);
offset = end;
}
return lineStrings;
}
/**
* @return {Array.<number>} Flat midpoints.
*/
getFlatMidpoints() {
const midpoints = [];
const flatCoordinates = this.flatCoordinates;
let offset = 0;
const ends = this.ends_;
const stride = this.stride;
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
const midpoint = interpolatePoint(
flatCoordinates, offset, end, stride, 0.5);
extend(midpoints, midpoint);
offset = end;
}
return midpoints;
}
/**
* @inheritDoc
*/
getSimplifiedGeometryInternal(squaredTolerance) {
const simplifiedFlatCoordinates = [];
const simplifiedEnds = [];
simplifiedFlatCoordinates.length = douglasPeuckerArray(
this.flatCoordinates, 0, this.ends_, this.stride, squaredTolerance,
simplifiedFlatCoordinates, 0, simplifiedEnds);
return new MultiLineString(simplifiedFlatCoordinates, GeometryLayout.XY, simplifiedEnds);
}
/**
* @inheritDoc
* @api
*/
getType() {
return GeometryType.MULTI_LINE_STRING;
}
/**
* @inheritDoc
* @api
*/
intersectsExtent(extent) {
return intersectsLineStringArray(
this.flatCoordinates, 0, this.ends_, this.stride, extent);
}
/**
* Set the coordinates of the multilinestring.
* @param {!Array.<Array.<module:ol/coordinate~Coordinate>>} coordinates Coordinates.
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @override
* @api
*/
setCoordinates(coordinates, opt_layout) {
this.setLayout(opt_layout, coordinates, 2);
if (!this.flatCoordinates) {
this.flatCoordinates = [];
}
const ends = deflateCoordinatesArray(
this.flatCoordinates, 0, coordinates, this.stride, this.ends_);
this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1];
this.changed();
}
}
inherits(MultiLineString, SimpleGeometry);
/**
* Append the passed linestring to the multilinestring.
* @param {module:ol/geom/LineString} lineString LineString.
* @api
*/
MultiLineString.prototype.appendLineString = function(lineString) {
if (!this.flatCoordinates) {
this.flatCoordinates = lineString.getFlatCoordinates().slice();
} else {
extend(this.flatCoordinates, lineString.getFlatCoordinates().slice());
}
this.ends_.push(this.flatCoordinates.length);
this.changed();
};
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/MultiLineString} Clone.
* @override
* @api
*/
MultiLineString.prototype.clone = function() {
return new MultiLineString(this.flatCoordinates.slice(), this.layout, this.ends_.slice());
};
/**
* @inheritDoc
*/
MultiLineString.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
return minSquaredDistance;
}
if (this.maxDeltaRevision_ != this.getRevision()) {
this.maxDelta_ = Math.sqrt(arrayMaxSquaredDelta(
this.flatCoordinates, 0, this.ends_, this.stride, 0));
this.maxDeltaRevision_ = this.getRevision();
}
return assignClosestArrayPoint(
this.flatCoordinates, 0, this.ends_, this.stride,
this.maxDelta_, false, x, y, closestPoint, minSquaredDistance);
};
/**
* Returns the coordinate at `m` using linear interpolation, or `null` if no
* such coordinate exists.
*
* `opt_extrapolate` controls extrapolation beyond the range of Ms in the
* MultiLineString. If `opt_extrapolate` is `true` then Ms less than the first
* M will return the first coordinate and Ms greater than the last M will
* return the last coordinate.
*
* `opt_interpolate` controls interpolation between consecutive LineStrings
* within the MultiLineString. If `opt_interpolate` is `true` the coordinates
* will be linearly interpolated between the last coordinate of one LineString
* and the first coordinate of the next LineString. If `opt_interpolate` is
* `false` then the function will return `null` for Ms falling between
* LineStrings.
*
* @param {number} m M.
* @param {boolean=} opt_extrapolate Extrapolate. Default is `false`.
* @param {boolean=} opt_interpolate Interpolate. Default is `false`.
* @return {module:ol/coordinate~Coordinate} Coordinate.
* @api
*/
MultiLineString.prototype.getCoordinateAtM = function(m, opt_extrapolate, opt_interpolate) {
if ((this.layout != GeometryLayout.XYM &&
this.layout != GeometryLayout.XYZM) ||
this.flatCoordinates.length === 0) {
return null;
}
const extrapolate = opt_extrapolate !== undefined ? opt_extrapolate : false;
const interpolate = opt_interpolate !== undefined ? opt_interpolate : false;
return lineStringsCoordinateAtM(this.flatCoordinates, 0,
this.ends_, this.stride, m, extrapolate, interpolate);
};
/**
* Return the coordinates of the multilinestring.
* @return {Array.<Array.<module:ol/coordinate~Coordinate>>} Coordinates.
* @override
* @api
*/
MultiLineString.prototype.getCoordinates = function() {
return inflateCoordinatesArray(
this.flatCoordinates, 0, this.ends_, this.stride);
};
/**
* @return {Array.<number>} Ends.
*/
MultiLineString.prototype.getEnds = function() {
return this.ends_;
};
/**
* Return the linestring at the specified index.
* @param {number} index Index.
* @return {module:ol/geom/LineString} LineString.
* @api
*/
MultiLineString.prototype.getLineString = function(index) {
if (index < 0 || this.ends_.length <= index) {
return null;
}
return new LineString(this.flatCoordinates.slice(
index === 0 ? 0 : this.ends_[index - 1], this.ends_[index]), this.layout);
};
/**
* Return the linestrings of this multilinestring.
* @return {Array.<module:ol/geom/LineString>} LineStrings.
* @api
*/
MultiLineString.prototype.getLineStrings = function() {
const flatCoordinates = this.flatCoordinates;
const ends = this.ends_;
const layout = this.layout;
/** @type {Array.<module:ol/geom/LineString>} */
const lineStrings = [];
let offset = 0;
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
const lineString = new LineString(flatCoordinates.slice(offset, end), layout);
lineStrings.push(lineString);
offset = end;
}
return lineStrings;
};
/**
* @return {Array.<number>} Flat midpoints.
*/
MultiLineString.prototype.getFlatMidpoints = function() {
const midpoints = [];
const flatCoordinates = this.flatCoordinates;
let offset = 0;
const ends = this.ends_;
const stride = this.stride;
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
const midpoint = interpolatePoint(
flatCoordinates, offset, end, stride, 0.5);
extend(midpoints, midpoint);
offset = end;
}
return midpoints;
};
/**
* @inheritDoc
*/
MultiLineString.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) {
const simplifiedFlatCoordinates = [];
const simplifiedEnds = [];
simplifiedFlatCoordinates.length = douglasPeuckerArray(
this.flatCoordinates, 0, this.ends_, this.stride, squaredTolerance,
simplifiedFlatCoordinates, 0, simplifiedEnds);
return new MultiLineString(simplifiedFlatCoordinates, GeometryLayout.XY, simplifiedEnds);
};
/**
* @inheritDoc
* @api
*/
MultiLineString.prototype.getType = function() {
return GeometryType.MULTI_LINE_STRING;
};
/**
* @inheritDoc
* @api
*/
MultiLineString.prototype.intersectsExtent = function(extent) {
return intersectsLineStringArray(
this.flatCoordinates, 0, this.ends_, this.stride, extent);
};
/**
* Set the coordinates of the multilinestring.
* @param {!Array.<Array.<module:ol/coordinate~Coordinate>>} coordinates Coordinates.
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @override
* @api
*/
MultiLineString.prototype.setCoordinates = function(coordinates, opt_layout) {
this.setLayout(opt_layout, coordinates, 2);
if (!this.flatCoordinates) {
this.flatCoordinates = [];
}
const ends = deflateCoordinatesArray(
this.flatCoordinates, 0, coordinates, this.stride, this.ends_);
this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1];
this.changed();
};
export default MultiLineString;

View File

@@ -23,157 +23,152 @@ import {squaredDistance as squaredDx} from '../math.js';
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @api
*/
const MultiPoint = function(coordinates, opt_layout) {
SimpleGeometry.call(this);
if (opt_layout && !Array.isArray(coordinates[0])) {
this.setFlatCoordinates(opt_layout, coordinates);
} else {
this.setCoordinates(coordinates, opt_layout);
class MultiPoint {
constructor(coordinates, opt_layout) {
SimpleGeometry.call(this);
if (opt_layout && !Array.isArray(coordinates[0])) {
this.setFlatCoordinates(opt_layout, coordinates);
} else {
this.setCoordinates(coordinates, opt_layout);
}
}
};
/**
* Append the passed point to this multipoint.
* @param {module:ol/geom/Point} point Point.
* @api
*/
appendPoint(point) {
if (!this.flatCoordinates) {
this.flatCoordinates = point.getFlatCoordinates().slice();
} else {
extend(this.flatCoordinates, point.getFlatCoordinates());
}
this.changed();
}
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/MultiPoint} Clone.
* @override
* @api
*/
clone() {
const multiPoint = new MultiPoint(this.flatCoordinates.slice(), this.layout);
return multiPoint;
}
/**
* @inheritDoc
*/
closestPointXY(x, y, closestPoint, minSquaredDistance) {
if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
return minSquaredDistance;
}
const flatCoordinates = this.flatCoordinates;
const stride = this.stride;
for (let i = 0, ii = flatCoordinates.length; i < ii; i += stride) {
const squaredDistance = squaredDx(
x, y, flatCoordinates[i], flatCoordinates[i + 1]);
if (squaredDistance < minSquaredDistance) {
minSquaredDistance = squaredDistance;
for (let j = 0; j < stride; ++j) {
closestPoint[j] = flatCoordinates[i + j];
}
closestPoint.length = stride;
}
}
return minSquaredDistance;
}
/**
* Return the coordinates of the multipoint.
* @return {Array.<module:ol/coordinate~Coordinate>} Coordinates.
* @override
* @api
*/
getCoordinates() {
return inflateCoordinates(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
}
/**
* Return the point at the specified index.
* @param {number} index Index.
* @return {module:ol/geom/Point} Point.
* @api
*/
getPoint(index) {
const n = !this.flatCoordinates ? 0 : this.flatCoordinates.length / this.stride;
if (index < 0 || n <= index) {
return null;
}
return new Point(this.flatCoordinates.slice(
index * this.stride, (index + 1) * this.stride), this.layout);
}
/**
* Return the points of this multipoint.
* @return {Array.<module:ol/geom/Point>} Points.
* @api
*/
getPoints() {
const flatCoordinates = this.flatCoordinates;
const layout = this.layout;
const stride = this.stride;
/** @type {Array.<module:ol/geom/Point>} */
const points = [];
for (let i = 0, ii = flatCoordinates.length; i < ii; i += stride) {
const point = new Point(flatCoordinates.slice(i, i + stride), layout);
points.push(point);
}
return points;
}
/**
* @inheritDoc
* @api
*/
getType() {
return GeometryType.MULTI_POINT;
}
/**
* @inheritDoc
* @api
*/
intersectsExtent(extent) {
const flatCoordinates = this.flatCoordinates;
const stride = this.stride;
for (let i = 0, ii = flatCoordinates.length; i < ii; i += stride) {
const x = flatCoordinates[i];
const y = flatCoordinates[i + 1];
if (containsXY(extent, x, y)) {
return true;
}
}
return false;
}
/**
* Set the coordinates of the multipoint.
* @param {!Array.<module:ol/coordinate~Coordinate>} coordinates Coordinates.
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @override
* @api
*/
setCoordinates(coordinates, opt_layout) {
this.setLayout(opt_layout, coordinates, 1);
if (!this.flatCoordinates) {
this.flatCoordinates = [];
}
this.flatCoordinates.length = deflateCoordinates(
this.flatCoordinates, 0, coordinates, this.stride);
this.changed();
}
}
inherits(MultiPoint, SimpleGeometry);
/**
* Append the passed point to this multipoint.
* @param {module:ol/geom/Point} point Point.
* @api
*/
MultiPoint.prototype.appendPoint = function(point) {
if (!this.flatCoordinates) {
this.flatCoordinates = point.getFlatCoordinates().slice();
} else {
extend(this.flatCoordinates, point.getFlatCoordinates());
}
this.changed();
};
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/MultiPoint} Clone.
* @override
* @api
*/
MultiPoint.prototype.clone = function() {
const multiPoint = new MultiPoint(this.flatCoordinates.slice(), this.layout);
return multiPoint;
};
/**
* @inheritDoc
*/
MultiPoint.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
return minSquaredDistance;
}
const flatCoordinates = this.flatCoordinates;
const stride = this.stride;
for (let i = 0, ii = flatCoordinates.length; i < ii; i += stride) {
const squaredDistance = squaredDx(
x, y, flatCoordinates[i], flatCoordinates[i + 1]);
if (squaredDistance < minSquaredDistance) {
minSquaredDistance = squaredDistance;
for (let j = 0; j < stride; ++j) {
closestPoint[j] = flatCoordinates[i + j];
}
closestPoint.length = stride;
}
}
return minSquaredDistance;
};
/**
* Return the coordinates of the multipoint.
* @return {Array.<module:ol/coordinate~Coordinate>} Coordinates.
* @override
* @api
*/
MultiPoint.prototype.getCoordinates = function() {
return inflateCoordinates(
this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
};
/**
* Return the point at the specified index.
* @param {number} index Index.
* @return {module:ol/geom/Point} Point.
* @api
*/
MultiPoint.prototype.getPoint = function(index) {
const n = !this.flatCoordinates ? 0 : this.flatCoordinates.length / this.stride;
if (index < 0 || n <= index) {
return null;
}
return new Point(this.flatCoordinates.slice(
index * this.stride, (index + 1) * this.stride), this.layout);
};
/**
* Return the points of this multipoint.
* @return {Array.<module:ol/geom/Point>} Points.
* @api
*/
MultiPoint.prototype.getPoints = function() {
const flatCoordinates = this.flatCoordinates;
const layout = this.layout;
const stride = this.stride;
/** @type {Array.<module:ol/geom/Point>} */
const points = [];
for (let i = 0, ii = flatCoordinates.length; i < ii; i += stride) {
const point = new Point(flatCoordinates.slice(i, i + stride), layout);
points.push(point);
}
return points;
};
/**
* @inheritDoc
* @api
*/
MultiPoint.prototype.getType = function() {
return GeometryType.MULTI_POINT;
};
/**
* @inheritDoc
* @api
*/
MultiPoint.prototype.intersectsExtent = function(extent) {
const flatCoordinates = this.flatCoordinates;
const stride = this.stride;
for (let i = 0, ii = flatCoordinates.length; i < ii; i += stride) {
const x = flatCoordinates[i];
const y = flatCoordinates[i + 1];
if (containsXY(extent, x, y)) {
return true;
}
}
return false;
};
/**
* Set the coordinates of the multipoint.
* @param {!Array.<module:ol/coordinate~Coordinate>} coordinates Coordinates.
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @override
* @api
*/
MultiPoint.prototype.setCoordinates = function(coordinates, opt_layout) {
this.setLayout(opt_layout, coordinates, 1);
if (!this.flatCoordinates) {
this.flatCoordinates = [];
}
this.flatCoordinates.length = deflateCoordinates(
this.flatCoordinates, 0, coordinates, this.stride);
this.changed();
};
export default MultiPoint;

View File

@@ -34,360 +34,346 @@ import {quantizeMultiArray} from '../geom/flat/simplify.js';
* coordinates.
* @api
*/
const MultiPolygon = function(coordinates, opt_layout, opt_endss) {
class MultiPolygon {
constructor(coordinates, opt_layout, opt_endss) {
SimpleGeometry.call(this);
SimpleGeometry.call(this);
/**
* @type {Array.<Array.<number>>}
* @private
*/
this.endss_ = [];
/**
* @type {Array.<Array.<number>>}
* @private
*/
this.endss_ = [];
/**
* @private
* @type {number}
*/
this.flatInteriorPointsRevision_ = -1;
/**
* @private
* @type {number}
*/
this.flatInteriorPointsRevision_ = -1;
/**
* @private
* @type {Array.<number>}
*/
this.flatInteriorPoints_ = null;
/**
* @private
* @type {Array.<number>}
*/
this.flatInteriorPoints_ = null;
/**
* @private
* @type {number}
*/
this.maxDelta_ = -1;
/**
* @private
* @type {number}
*/
this.maxDelta_ = -1;
/**
* @private
* @type {number}
*/
this.maxDeltaRevision_ = -1;
/**
* @private
* @type {number}
*/
this.maxDeltaRevision_ = -1;
/**
* @private
* @type {number}
*/
this.orientedRevision_ = -1;
/**
* @private
* @type {number}
*/
this.orientedRevision_ = -1;
/**
* @private
* @type {Array.<number>}
*/
this.orientedFlatCoordinates_ = null;
/**
* @private
* @type {Array.<number>}
*/
this.orientedFlatCoordinates_ = null;
if (!opt_endss && !Array.isArray(coordinates[0])) {
let layout = this.getLayout();
const flatCoordinates = [];
const endss = [];
for (let i = 0, ii = coordinates.length; i < ii; ++i) {
const polygon = coordinates[i];
if (i === 0) {
layout = polygon.getLayout();
if (!opt_endss && !Array.isArray(coordinates[0])) {
let layout = this.getLayout();
const flatCoordinates = [];
const endss = [];
for (let i = 0, ii = coordinates.length; i < ii; ++i) {
const polygon = coordinates[i];
if (i === 0) {
layout = polygon.getLayout();
}
const offset = flatCoordinates.length;
const ends = polygon.getEnds();
for (let j = 0, jj = ends.length; j < jj; ++j) {
ends[j] += offset;
}
extend(flatCoordinates, polygon.getFlatCoordinates());
endss.push(ends);
}
const offset = flatCoordinates.length;
const ends = polygon.getEnds();
for (let j = 0, jj = ends.length; j < jj; ++j) {
ends[j] += offset;
}
extend(flatCoordinates, polygon.getFlatCoordinates());
endss.push(ends);
opt_layout = layout;
coordinates = flatCoordinates;
opt_endss = endss;
}
opt_layout = layout;
coordinates = flatCoordinates;
opt_endss = endss;
}
if (opt_layout !== undefined && opt_endss) {
this.setFlatCoordinates(opt_layout, coordinates);
this.endss_ = opt_endss;
} else {
this.setCoordinates(coordinates, opt_layout);
if (opt_layout !== undefined && opt_endss) {
this.setFlatCoordinates(opt_layout, coordinates);
this.endss_ = opt_endss;
} else {
this.setCoordinates(coordinates, opt_layout);
}
}
};
/**
* Append the passed polygon to this multipolygon.
* @param {module:ol/geom/Polygon} polygon Polygon.
* @api
*/
appendPolygon(polygon) {
/** @type {Array.<number>} */
let ends;
if (!this.flatCoordinates) {
this.flatCoordinates = polygon.getFlatCoordinates().slice();
ends = polygon.getEnds().slice();
this.endss_.push();
} else {
const offset = this.flatCoordinates.length;
extend(this.flatCoordinates, polygon.getFlatCoordinates());
ends = polygon.getEnds().slice();
for (let i = 0, ii = ends.length; i < ii; ++i) {
ends[i] += offset;
}
}
this.endss_.push(ends);
this.changed();
}
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/MultiPolygon} Clone.
* @override
* @api
*/
clone() {
const len = this.endss_.length;
const newEndss = new Array(len);
for (let i = 0; i < len; ++i) {
newEndss[i] = this.endss_[i].slice();
}
return new MultiPolygon(
this.flatCoordinates.slice(), this.layout, newEndss);
}
/**
* @inheritDoc
*/
closestPointXY(x, y, closestPoint, minSquaredDistance) {
if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
return minSquaredDistance;
}
if (this.maxDeltaRevision_ != this.getRevision()) {
this.maxDelta_ = Math.sqrt(multiArrayMaxSquaredDelta(
this.flatCoordinates, 0, this.endss_, this.stride, 0));
this.maxDeltaRevision_ = this.getRevision();
}
return assignClosestMultiArrayPoint(
this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride,
this.maxDelta_, true, x, y, closestPoint, minSquaredDistance);
}
/**
* @inheritDoc
*/
containsXY(x, y) {
return linearRingssContainsXY(this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, x, y);
}
/**
* Return the area of the multipolygon on projected plane.
* @return {number} Area (on projected plane).
* @api
*/
getArea() {
return linearRingssArea(this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride);
}
/**
* Get the coordinate array for this geometry. This array has the structure
* of a GeoJSON coordinate array for multi-polygons.
*
* @param {boolean=} opt_right Orient coordinates according to the right-hand
* rule (counter-clockwise for exterior and clockwise for interior rings).
* If `false`, coordinates will be oriented according to the left-hand rule
* (clockwise for exterior and counter-clockwise for interior rings).
* By default, coordinate orientation will depend on how the geometry was
* constructed.
* @return {Array.<Array.<Array.<module:ol/coordinate~Coordinate>>>} Coordinates.
* @override
* @api
*/
getCoordinates(opt_right) {
let flatCoordinates;
if (opt_right !== undefined) {
flatCoordinates = this.getOrientedFlatCoordinates().slice();
orientLinearRingsArray(
flatCoordinates, 0, this.endss_, this.stride, opt_right);
} else {
flatCoordinates = this.flatCoordinates;
}
return inflateMultiCoordinatesArray(
flatCoordinates, 0, this.endss_, this.stride);
}
/**
* @return {Array.<Array.<number>>} Endss.
*/
getEndss() {
return this.endss_;
}
/**
* @return {Array.<number>} Flat interior points.
*/
getFlatInteriorPoints() {
if (this.flatInteriorPointsRevision_ != this.getRevision()) {
const flatCenters = linearRingssCenter(
this.flatCoordinates, 0, this.endss_, this.stride);
this.flatInteriorPoints_ = getInteriorPointsOfMultiArray(
this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride,
flatCenters);
this.flatInteriorPointsRevision_ = this.getRevision();
}
return this.flatInteriorPoints_;
}
/**
* Return the interior points as {@link module:ol/geom/MultiPoint multipoint}.
* @return {module:ol/geom/MultiPoint} Interior points as XYM coordinates, where M is
* the length of the horizontal intersection that the point belongs to.
* @api
*/
getInteriorPoints() {
return new MultiPoint(this.getFlatInteriorPoints().slice(), GeometryLayout.XYM);
}
/**
* @return {Array.<number>} Oriented flat coordinates.
*/
getOrientedFlatCoordinates() {
if (this.orientedRevision_ != this.getRevision()) {
const flatCoordinates = this.flatCoordinates;
if (linearRingsAreOriented(
flatCoordinates, 0, this.endss_, this.stride)) {
this.orientedFlatCoordinates_ = flatCoordinates;
} else {
this.orientedFlatCoordinates_ = flatCoordinates.slice();
this.orientedFlatCoordinates_.length =
orientLinearRingsArray(
this.orientedFlatCoordinates_, 0, this.endss_, this.stride);
}
this.orientedRevision_ = this.getRevision();
}
return this.orientedFlatCoordinates_;
}
/**
* @inheritDoc
*/
getSimplifiedGeometryInternal(squaredTolerance) {
const simplifiedFlatCoordinates = [];
const simplifiedEndss = [];
simplifiedFlatCoordinates.length = quantizeMultiArray(
this.flatCoordinates, 0, this.endss_, this.stride,
Math.sqrt(squaredTolerance),
simplifiedFlatCoordinates, 0, simplifiedEndss);
return new MultiPolygon(simplifiedFlatCoordinates, GeometryLayout.XY, simplifiedEndss);
}
/**
* Return the polygon at the specified index.
* @param {number} index Index.
* @return {module:ol/geom/Polygon} Polygon.
* @api
*/
getPolygon(index) {
if (index < 0 || this.endss_.length <= index) {
return null;
}
let offset;
if (index === 0) {
offset = 0;
} else {
const prevEnds = this.endss_[index - 1];
offset = prevEnds[prevEnds.length - 1];
}
const ends = this.endss_[index].slice();
const end = ends[ends.length - 1];
if (offset !== 0) {
for (let i = 0, ii = ends.length; i < ii; ++i) {
ends[i] -= offset;
}
}
return new Polygon(this.flatCoordinates.slice(offset, end), this.layout, ends);
}
/**
* Return the polygons of this multipolygon.
* @return {Array.<module:ol/geom/Polygon>} Polygons.
* @api
*/
getPolygons() {
const layout = this.layout;
const flatCoordinates = this.flatCoordinates;
const endss = this.endss_;
const polygons = [];
let offset = 0;
for (let i = 0, ii = endss.length; i < ii; ++i) {
const ends = endss[i].slice();
const end = ends[ends.length - 1];
if (offset !== 0) {
for (let j = 0, jj = ends.length; j < jj; ++j) {
ends[j] -= offset;
}
}
const polygon = new Polygon(flatCoordinates.slice(offset, end), layout, ends);
polygons.push(polygon);
offset = end;
}
return polygons;
}
/**
* @inheritDoc
* @api
*/
getType() {
return GeometryType.MULTI_POLYGON;
}
/**
* @inheritDoc
* @api
*/
intersectsExtent(extent) {
return intersectsLinearRingMultiArray(
this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, extent);
}
/**
* Set the coordinates of the multipolygon.
* @param {!Array.<Array.<Array.<module:ol/coordinate~Coordinate>>>} coordinates Coordinates.
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @override
* @api
*/
setCoordinates(coordinates, opt_layout) {
this.setLayout(opt_layout, coordinates, 3);
if (!this.flatCoordinates) {
this.flatCoordinates = [];
}
const endss = deflateMultiCoordinatesArray(
this.flatCoordinates, 0, coordinates, this.stride, this.endss_);
if (endss.length === 0) {
this.flatCoordinates.length = 0;
} else {
const lastEnds = endss[endss.length - 1];
this.flatCoordinates.length = lastEnds.length === 0 ?
0 : lastEnds[lastEnds.length - 1];
}
this.changed();
}
}
inherits(MultiPolygon, SimpleGeometry);
/**
* Append the passed polygon to this multipolygon.
* @param {module:ol/geom/Polygon} polygon Polygon.
* @api
*/
MultiPolygon.prototype.appendPolygon = function(polygon) {
/** @type {Array.<number>} */
let ends;
if (!this.flatCoordinates) {
this.flatCoordinates = polygon.getFlatCoordinates().slice();
ends = polygon.getEnds().slice();
this.endss_.push();
} else {
const offset = this.flatCoordinates.length;
extend(this.flatCoordinates, polygon.getFlatCoordinates());
ends = polygon.getEnds().slice();
for (let i = 0, ii = ends.length; i < ii; ++i) {
ends[i] += offset;
}
}
this.endss_.push(ends);
this.changed();
};
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/MultiPolygon} Clone.
* @override
* @api
*/
MultiPolygon.prototype.clone = function() {
const len = this.endss_.length;
const newEndss = new Array(len);
for (let i = 0; i < len; ++i) {
newEndss[i] = this.endss_[i].slice();
}
return new MultiPolygon(
this.flatCoordinates.slice(), this.layout, newEndss);
};
/**
* @inheritDoc
*/
MultiPolygon.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
return minSquaredDistance;
}
if (this.maxDeltaRevision_ != this.getRevision()) {
this.maxDelta_ = Math.sqrt(multiArrayMaxSquaredDelta(
this.flatCoordinates, 0, this.endss_, this.stride, 0));
this.maxDeltaRevision_ = this.getRevision();
}
return assignClosestMultiArrayPoint(
this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride,
this.maxDelta_, true, x, y, closestPoint, minSquaredDistance);
};
/**
* @inheritDoc
*/
MultiPolygon.prototype.containsXY = function(x, y) {
return linearRingssContainsXY(this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, x, y);
};
/**
* Return the area of the multipolygon on projected plane.
* @return {number} Area (on projected plane).
* @api
*/
MultiPolygon.prototype.getArea = function() {
return linearRingssArea(this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride);
};
/**
* Get the coordinate array for this geometry. This array has the structure
* of a GeoJSON coordinate array for multi-polygons.
*
* @param {boolean=} opt_right Orient coordinates according to the right-hand
* rule (counter-clockwise for exterior and clockwise for interior rings).
* If `false`, coordinates will be oriented according to the left-hand rule
* (clockwise for exterior and counter-clockwise for interior rings).
* By default, coordinate orientation will depend on how the geometry was
* constructed.
* @return {Array.<Array.<Array.<module:ol/coordinate~Coordinate>>>} Coordinates.
* @override
* @api
*/
MultiPolygon.prototype.getCoordinates = function(opt_right) {
let flatCoordinates;
if (opt_right !== undefined) {
flatCoordinates = this.getOrientedFlatCoordinates().slice();
orientLinearRingsArray(
flatCoordinates, 0, this.endss_, this.stride, opt_right);
} else {
flatCoordinates = this.flatCoordinates;
}
return inflateMultiCoordinatesArray(
flatCoordinates, 0, this.endss_, this.stride);
};
/**
* @return {Array.<Array.<number>>} Endss.
*/
MultiPolygon.prototype.getEndss = function() {
return this.endss_;
};
/**
* @return {Array.<number>} Flat interior points.
*/
MultiPolygon.prototype.getFlatInteriorPoints = function() {
if (this.flatInteriorPointsRevision_ != this.getRevision()) {
const flatCenters = linearRingssCenter(
this.flatCoordinates, 0, this.endss_, this.stride);
this.flatInteriorPoints_ = getInteriorPointsOfMultiArray(
this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride,
flatCenters);
this.flatInteriorPointsRevision_ = this.getRevision();
}
return this.flatInteriorPoints_;
};
/**
* Return the interior points as {@link module:ol/geom/MultiPoint multipoint}.
* @return {module:ol/geom/MultiPoint} Interior points as XYM coordinates, where M is
* the length of the horizontal intersection that the point belongs to.
* @api
*/
MultiPolygon.prototype.getInteriorPoints = function() {
return new MultiPoint(this.getFlatInteriorPoints().slice(), GeometryLayout.XYM);
};
/**
* @return {Array.<number>} Oriented flat coordinates.
*/
MultiPolygon.prototype.getOrientedFlatCoordinates = function() {
if (this.orientedRevision_ != this.getRevision()) {
const flatCoordinates = this.flatCoordinates;
if (linearRingsAreOriented(
flatCoordinates, 0, this.endss_, this.stride)) {
this.orientedFlatCoordinates_ = flatCoordinates;
} else {
this.orientedFlatCoordinates_ = flatCoordinates.slice();
this.orientedFlatCoordinates_.length =
orientLinearRingsArray(
this.orientedFlatCoordinates_, 0, this.endss_, this.stride);
}
this.orientedRevision_ = this.getRevision();
}
return this.orientedFlatCoordinates_;
};
/**
* @inheritDoc
*/
MultiPolygon.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) {
const simplifiedFlatCoordinates = [];
const simplifiedEndss = [];
simplifiedFlatCoordinates.length = quantizeMultiArray(
this.flatCoordinates, 0, this.endss_, this.stride,
Math.sqrt(squaredTolerance),
simplifiedFlatCoordinates, 0, simplifiedEndss);
return new MultiPolygon(simplifiedFlatCoordinates, GeometryLayout.XY, simplifiedEndss);
};
/**
* Return the polygon at the specified index.
* @param {number} index Index.
* @return {module:ol/geom/Polygon} Polygon.
* @api
*/
MultiPolygon.prototype.getPolygon = function(index) {
if (index < 0 || this.endss_.length <= index) {
return null;
}
let offset;
if (index === 0) {
offset = 0;
} else {
const prevEnds = this.endss_[index - 1];
offset = prevEnds[prevEnds.length - 1];
}
const ends = this.endss_[index].slice();
const end = ends[ends.length - 1];
if (offset !== 0) {
for (let i = 0, ii = ends.length; i < ii; ++i) {
ends[i] -= offset;
}
}
return new Polygon(this.flatCoordinates.slice(offset, end), this.layout, ends);
};
/**
* Return the polygons of this multipolygon.
* @return {Array.<module:ol/geom/Polygon>} Polygons.
* @api
*/
MultiPolygon.prototype.getPolygons = function() {
const layout = this.layout;
const flatCoordinates = this.flatCoordinates;
const endss = this.endss_;
const polygons = [];
let offset = 0;
for (let i = 0, ii = endss.length; i < ii; ++i) {
const ends = endss[i].slice();
const end = ends[ends.length - 1];
if (offset !== 0) {
for (let j = 0, jj = ends.length; j < jj; ++j) {
ends[j] -= offset;
}
}
const polygon = new Polygon(flatCoordinates.slice(offset, end), layout, ends);
polygons.push(polygon);
offset = end;
}
return polygons;
};
/**
* @inheritDoc
* @api
*/
MultiPolygon.prototype.getType = function() {
return GeometryType.MULTI_POLYGON;
};
/**
* @inheritDoc
* @api
*/
MultiPolygon.prototype.intersectsExtent = function(extent) {
return intersectsLinearRingMultiArray(
this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, extent);
};
/**
* Set the coordinates of the multipolygon.
* @param {!Array.<Array.<Array.<module:ol/coordinate~Coordinate>>>} coordinates Coordinates.
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @override
* @api
*/
MultiPolygon.prototype.setCoordinates = function(coordinates, opt_layout) {
this.setLayout(opt_layout, coordinates, 3);
if (!this.flatCoordinates) {
this.flatCoordinates = [];
}
const endss = deflateMultiCoordinatesArray(
this.flatCoordinates, 0, coordinates, this.stride, this.endss_);
if (endss.length === 0) {
this.flatCoordinates.length = 0;
} else {
const lastEnds = endss[endss.length - 1];
this.flatCoordinates.length = lastEnds.length === 0 ?
0 : lastEnds[lastEnds.length - 1];
}
this.changed();
};
export default MultiPolygon;

View File

@@ -18,94 +18,90 @@ import {squaredDistance as squaredDx} from '../math.js';
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @api
*/
const Point = function(coordinates, opt_layout) {
SimpleGeometry.call(this);
this.setCoordinates(coordinates, opt_layout);
};
class Point {
constructor(coordinates, opt_layout) {
SimpleGeometry.call(this);
this.setCoordinates(coordinates, opt_layout);
}
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/Point} Clone.
* @override
* @api
*/
clone() {
const point = new Point(this.flatCoordinates.slice(), this.layout);
return point;
}
/**
* @inheritDoc
*/
closestPointXY(x, y, closestPoint, minSquaredDistance) {
const flatCoordinates = this.flatCoordinates;
const squaredDistance = squaredDx(x, y, flatCoordinates[0], flatCoordinates[1]);
if (squaredDistance < minSquaredDistance) {
const stride = this.stride;
for (let i = 0; i < stride; ++i) {
closestPoint[i] = flatCoordinates[i];
}
closestPoint.length = stride;
return squaredDistance;
} else {
return minSquaredDistance;
}
}
/**
* Return the coordinate of the point.
* @return {module:ol/coordinate~Coordinate} Coordinates.
* @override
* @api
*/
getCoordinates() {
return !this.flatCoordinates ? [] : this.flatCoordinates.slice();
}
/**
* @inheritDoc
*/
computeExtent(extent) {
return createOrUpdateFromCoordinate(this.flatCoordinates, extent);
}
/**
* @inheritDoc
* @api
*/
getType() {
return GeometryType.POINT;
}
/**
* @inheritDoc
* @api
*/
intersectsExtent(extent) {
return containsXY(extent, this.flatCoordinates[0], this.flatCoordinates[1]);
}
/**
* @inheritDoc
* @api
*/
setCoordinates(coordinates, opt_layout) {
this.setLayout(opt_layout, coordinates, 0);
if (!this.flatCoordinates) {
this.flatCoordinates = [];
}
this.flatCoordinates.length = deflateCoordinate(
this.flatCoordinates, 0, coordinates, this.stride);
this.changed();
}
}
inherits(Point, SimpleGeometry);
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/Point} Clone.
* @override
* @api
*/
Point.prototype.clone = function() {
const point = new Point(this.flatCoordinates.slice(), this.layout);
return point;
};
/**
* @inheritDoc
*/
Point.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
const flatCoordinates = this.flatCoordinates;
const squaredDistance = squaredDx(x, y, flatCoordinates[0], flatCoordinates[1]);
if (squaredDistance < minSquaredDistance) {
const stride = this.stride;
for (let i = 0; i < stride; ++i) {
closestPoint[i] = flatCoordinates[i];
}
closestPoint.length = stride;
return squaredDistance;
} else {
return minSquaredDistance;
}
};
/**
* Return the coordinate of the point.
* @return {module:ol/coordinate~Coordinate} Coordinates.
* @override
* @api
*/
Point.prototype.getCoordinates = function() {
return !this.flatCoordinates ? [] : this.flatCoordinates.slice();
};
/**
* @inheritDoc
*/
Point.prototype.computeExtent = function(extent) {
return createOrUpdateFromCoordinate(this.flatCoordinates, extent);
};
/**
* @inheritDoc
* @api
*/
Point.prototype.getType = function() {
return GeometryType.POINT;
};
/**
* @inheritDoc
* @api
*/
Point.prototype.intersectsExtent = function(extent) {
return containsXY(extent, this.flatCoordinates[0], this.flatCoordinates[1]);
};
/**
* @inheritDoc
* @api
*/
Point.prototype.setCoordinates = function(coordinates, opt_layout) {
this.setLayout(opt_layout, coordinates, 0);
if (!this.flatCoordinates) {
this.flatCoordinates = [];
}
this.flatCoordinates.length = deflateCoordinate(
this.flatCoordinates, 0, coordinates, this.stride);
this.changed();
};
export default Point;

View File

@@ -39,314 +39,300 @@ import {modulo} from '../math.js';
* coordinates).
* @api
*/
const Polygon = function(coordinates, opt_layout, opt_ends) {
class Polygon {
constructor(coordinates, opt_layout, opt_ends) {
SimpleGeometry.call(this);
SimpleGeometry.call(this);
/**
* @type {Array.<number>}
* @private
*/
this.ends_ = [];
/**
* @type {Array.<number>}
* @private
*/
this.ends_ = [];
/**
* @private
* @type {number}
*/
this.flatInteriorPointRevision_ = -1;
/**
* @private
* @type {number}
*/
this.flatInteriorPointRevision_ = -1;
/**
* @private
* @type {module:ol/coordinate~Coordinate}
*/
this.flatInteriorPoint_ = null;
/**
* @private
* @type {module:ol/coordinate~Coordinate}
*/
this.flatInteriorPoint_ = null;
/**
* @private
* @type {number}
*/
this.maxDelta_ = -1;
/**
* @private
* @type {number}
*/
this.maxDelta_ = -1;
/**
* @private
* @type {number}
*/
this.maxDeltaRevision_ = -1;
/**
* @private
* @type {number}
*/
this.maxDeltaRevision_ = -1;
/**
* @private
* @type {number}
*/
this.orientedRevision_ = -1;
/**
* @private
* @type {number}
*/
this.orientedRevision_ = -1;
/**
* @private
* @type {Array.<number>}
*/
this.orientedFlatCoordinates_ = null;
/**
* @private
* @type {Array.<number>}
*/
this.orientedFlatCoordinates_ = null;
if (opt_layout !== undefined && opt_ends) {
this.setFlatCoordinates(opt_layout, coordinates);
this.ends_ = opt_ends;
} else {
this.setCoordinates(coordinates, opt_layout);
}
if (opt_layout !== undefined && opt_ends) {
this.setFlatCoordinates(opt_layout, coordinates);
this.ends_ = opt_ends;
} else {
this.setCoordinates(coordinates, opt_layout);
}
};
/**
* Append the passed linear ring to this polygon.
* @param {module:ol/geom/LinearRing} linearRing Linear ring.
* @api
*/
appendLinearRing(linearRing) {
if (!this.flatCoordinates) {
this.flatCoordinates = linearRing.getFlatCoordinates().slice();
} else {
extend(this.flatCoordinates, linearRing.getFlatCoordinates());
}
this.ends_.push(this.flatCoordinates.length);
this.changed();
}
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/Polygon} Clone.
* @override
* @api
*/
clone() {
return new Polygon(this.flatCoordinates.slice(), this.layout, this.ends_.slice());
}
/**
* @inheritDoc
*/
closestPointXY(x, y, closestPoint, minSquaredDistance) {
if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
return minSquaredDistance;
}
if (this.maxDeltaRevision_ != this.getRevision()) {
this.maxDelta_ = Math.sqrt(arrayMaxSquaredDelta(
this.flatCoordinates, 0, this.ends_, this.stride, 0));
this.maxDeltaRevision_ = this.getRevision();
}
return assignClosestArrayPoint(
this.flatCoordinates, 0, this.ends_, this.stride,
this.maxDelta_, true, x, y, closestPoint, minSquaredDistance);
}
/**
* @inheritDoc
*/
containsXY(x, y) {
return linearRingsContainsXY(this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, x, y);
}
/**
* Return the area of the polygon on projected plane.
* @return {number} Area (on projected plane).
* @api
*/
getArea() {
return linearRingsArea(this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride);
}
/**
* Get the coordinate array for this geometry. This array has the structure
* of a GeoJSON coordinate array for polygons.
*
* @param {boolean=} opt_right Orient coordinates according to the right-hand
* rule (counter-clockwise for exterior and clockwise for interior rings).
* If `false`, coordinates will be oriented according to the left-hand rule
* (clockwise for exterior and counter-clockwise for interior rings).
* By default, coordinate orientation will depend on how the geometry was
* constructed.
* @return {Array.<Array.<module:ol/coordinate~Coordinate>>} Coordinates.
* @override
* @api
*/
getCoordinates(opt_right) {
let flatCoordinates;
if (opt_right !== undefined) {
flatCoordinates = this.getOrientedFlatCoordinates().slice();
orientLinearRings(
flatCoordinates, 0, this.ends_, this.stride, opt_right);
} else {
flatCoordinates = this.flatCoordinates;
}
return inflateCoordinatesArray(
flatCoordinates, 0, this.ends_, this.stride);
}
/**
* @return {Array.<number>} Ends.
*/
getEnds() {
return this.ends_;
}
/**
* @return {Array.<number>} Interior point.
*/
getFlatInteriorPoint() {
if (this.flatInteriorPointRevision_ != this.getRevision()) {
const flatCenter = getCenter(this.getExtent());
this.flatInteriorPoint_ = getInteriorPointOfArray(
this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride,
flatCenter, 0);
this.flatInteriorPointRevision_ = this.getRevision();
}
return this.flatInteriorPoint_;
}
/**
* Return an interior point of the polygon.
* @return {module:ol/geom/Point} Interior point as XYM coordinate, where M is the
* length of the horizontal intersection that the point belongs to.
* @api
*/
getInteriorPoint() {
return new Point(this.getFlatInteriorPoint(), GeometryLayout.XYM);
}
/**
* Return the number of rings of the polygon, this includes the exterior
* ring and any interior rings.
*
* @return {number} Number of rings.
* @api
*/
getLinearRingCount() {
return this.ends_.length;
}
/**
* Return the Nth linear ring of the polygon geometry. Return `null` if the
* given index is out of range.
* The exterior linear ring is available at index `0` and the interior rings
* at index `1` and beyond.
*
* @param {number} index Index.
* @return {module:ol/geom/LinearRing} Linear ring.
* @api
*/
getLinearRing(index) {
if (index < 0 || this.ends_.length <= index) {
return null;
}
return new LinearRing(this.flatCoordinates.slice(
index === 0 ? 0 : this.ends_[index - 1], this.ends_[index]), this.layout);
}
/**
* Return the linear rings of the polygon.
* @return {Array.<module:ol/geom/LinearRing>} Linear rings.
* @api
*/
getLinearRings() {
const layout = this.layout;
const flatCoordinates = this.flatCoordinates;
const ends = this.ends_;
const linearRings = [];
let offset = 0;
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
const linearRing = new LinearRing(flatCoordinates.slice(offset, end), layout);
linearRings.push(linearRing);
offset = end;
}
return linearRings;
}
/**
* @return {Array.<number>} Oriented flat coordinates.
*/
getOrientedFlatCoordinates() {
if (this.orientedRevision_ != this.getRevision()) {
const flatCoordinates = this.flatCoordinates;
if (linearRingIsOriented(
flatCoordinates, 0, this.ends_, this.stride)) {
this.orientedFlatCoordinates_ = flatCoordinates;
} else {
this.orientedFlatCoordinates_ = flatCoordinates.slice();
this.orientedFlatCoordinates_.length =
orientLinearRings(
this.orientedFlatCoordinates_, 0, this.ends_, this.stride);
}
this.orientedRevision_ = this.getRevision();
}
return this.orientedFlatCoordinates_;
}
/**
* @inheritDoc
*/
getSimplifiedGeometryInternal(squaredTolerance) {
const simplifiedFlatCoordinates = [];
const simplifiedEnds = [];
simplifiedFlatCoordinates.length = quantizeArray(
this.flatCoordinates, 0, this.ends_, this.stride,
Math.sqrt(squaredTolerance),
simplifiedFlatCoordinates, 0, simplifiedEnds);
return new Polygon(simplifiedFlatCoordinates, GeometryLayout.XY, simplifiedEnds);
}
/**
* @inheritDoc
* @api
*/
getType() {
return GeometryType.POLYGON;
}
/**
* @inheritDoc
* @api
*/
intersectsExtent(extent) {
return intersectsLinearRingArray(
this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, extent);
}
/**
* Set the coordinates of the polygon.
* @param {!Array.<Array.<module:ol/coordinate~Coordinate>>} coordinates Coordinates.
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @override
* @api
*/
setCoordinates(coordinates, opt_layout) {
this.setLayout(opt_layout, coordinates, 2);
if (!this.flatCoordinates) {
this.flatCoordinates = [];
}
const ends = deflateCoordinatesArray(
this.flatCoordinates, 0, coordinates, this.stride, this.ends_);
this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1];
this.changed();
}
}
inherits(Polygon, SimpleGeometry);
/**
* Append the passed linear ring to this polygon.
* @param {module:ol/geom/LinearRing} linearRing Linear ring.
* @api
*/
Polygon.prototype.appendLinearRing = function(linearRing) {
if (!this.flatCoordinates) {
this.flatCoordinates = linearRing.getFlatCoordinates().slice();
} else {
extend(this.flatCoordinates, linearRing.getFlatCoordinates());
}
this.ends_.push(this.flatCoordinates.length);
this.changed();
};
/**
* Make a complete copy of the geometry.
* @return {!module:ol/geom/Polygon} Clone.
* @override
* @api
*/
Polygon.prototype.clone = function() {
return new Polygon(this.flatCoordinates.slice(), this.layout, this.ends_.slice());
};
/**
* @inheritDoc
*/
Polygon.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {
if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
return minSquaredDistance;
}
if (this.maxDeltaRevision_ != this.getRevision()) {
this.maxDelta_ = Math.sqrt(arrayMaxSquaredDelta(
this.flatCoordinates, 0, this.ends_, this.stride, 0));
this.maxDeltaRevision_ = this.getRevision();
}
return assignClosestArrayPoint(
this.flatCoordinates, 0, this.ends_, this.stride,
this.maxDelta_, true, x, y, closestPoint, minSquaredDistance);
};
/**
* @inheritDoc
*/
Polygon.prototype.containsXY = function(x, y) {
return linearRingsContainsXY(this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, x, y);
};
/**
* Return the area of the polygon on projected plane.
* @return {number} Area (on projected plane).
* @api
*/
Polygon.prototype.getArea = function() {
return linearRingsArea(this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride);
};
/**
* Get the coordinate array for this geometry. This array has the structure
* of a GeoJSON coordinate array for polygons.
*
* @param {boolean=} opt_right Orient coordinates according to the right-hand
* rule (counter-clockwise for exterior and clockwise for interior rings).
* If `false`, coordinates will be oriented according to the left-hand rule
* (clockwise for exterior and counter-clockwise for interior rings).
* By default, coordinate orientation will depend on how the geometry was
* constructed.
* @return {Array.<Array.<module:ol/coordinate~Coordinate>>} Coordinates.
* @override
* @api
*/
Polygon.prototype.getCoordinates = function(opt_right) {
let flatCoordinates;
if (opt_right !== undefined) {
flatCoordinates = this.getOrientedFlatCoordinates().slice();
orientLinearRings(
flatCoordinates, 0, this.ends_, this.stride, opt_right);
} else {
flatCoordinates = this.flatCoordinates;
}
return inflateCoordinatesArray(
flatCoordinates, 0, this.ends_, this.stride);
};
/**
* @return {Array.<number>} Ends.
*/
Polygon.prototype.getEnds = function() {
return this.ends_;
};
/**
* @return {Array.<number>} Interior point.
*/
Polygon.prototype.getFlatInteriorPoint = function() {
if (this.flatInteriorPointRevision_ != this.getRevision()) {
const flatCenter = getCenter(this.getExtent());
this.flatInteriorPoint_ = getInteriorPointOfArray(
this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride,
flatCenter, 0);
this.flatInteriorPointRevision_ = this.getRevision();
}
return this.flatInteriorPoint_;
};
/**
* Return an interior point of the polygon.
* @return {module:ol/geom/Point} Interior point as XYM coordinate, where M is the
* length of the horizontal intersection that the point belongs to.
* @api
*/
Polygon.prototype.getInteriorPoint = function() {
return new Point(this.getFlatInteriorPoint(), GeometryLayout.XYM);
};
/**
* Return the number of rings of the polygon, this includes the exterior
* ring and any interior rings.
*
* @return {number} Number of rings.
* @api
*/
Polygon.prototype.getLinearRingCount = function() {
return this.ends_.length;
};
/**
* Return the Nth linear ring of the polygon geometry. Return `null` if the
* given index is out of range.
* The exterior linear ring is available at index `0` and the interior rings
* at index `1` and beyond.
*
* @param {number} index Index.
* @return {module:ol/geom/LinearRing} Linear ring.
* @api
*/
Polygon.prototype.getLinearRing = function(index) {
if (index < 0 || this.ends_.length <= index) {
return null;
}
return new LinearRing(this.flatCoordinates.slice(
index === 0 ? 0 : this.ends_[index - 1], this.ends_[index]), this.layout);
};
/**
* Return the linear rings of the polygon.
* @return {Array.<module:ol/geom/LinearRing>} Linear rings.
* @api
*/
Polygon.prototype.getLinearRings = function() {
const layout = this.layout;
const flatCoordinates = this.flatCoordinates;
const ends = this.ends_;
const linearRings = [];
let offset = 0;
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
const linearRing = new LinearRing(flatCoordinates.slice(offset, end), layout);
linearRings.push(linearRing);
offset = end;
}
return linearRings;
};
/**
* @return {Array.<number>} Oriented flat coordinates.
*/
Polygon.prototype.getOrientedFlatCoordinates = function() {
if (this.orientedRevision_ != this.getRevision()) {
const flatCoordinates = this.flatCoordinates;
if (linearRingIsOriented(
flatCoordinates, 0, this.ends_, this.stride)) {
this.orientedFlatCoordinates_ = flatCoordinates;
} else {
this.orientedFlatCoordinates_ = flatCoordinates.slice();
this.orientedFlatCoordinates_.length =
orientLinearRings(
this.orientedFlatCoordinates_, 0, this.ends_, this.stride);
}
this.orientedRevision_ = this.getRevision();
}
return this.orientedFlatCoordinates_;
};
/**
* @inheritDoc
*/
Polygon.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) {
const simplifiedFlatCoordinates = [];
const simplifiedEnds = [];
simplifiedFlatCoordinates.length = quantizeArray(
this.flatCoordinates, 0, this.ends_, this.stride,
Math.sqrt(squaredTolerance),
simplifiedFlatCoordinates, 0, simplifiedEnds);
return new Polygon(simplifiedFlatCoordinates, GeometryLayout.XY, simplifiedEnds);
};
/**
* @inheritDoc
* @api
*/
Polygon.prototype.getType = function() {
return GeometryType.POLYGON;
};
/**
* @inheritDoc
* @api
*/
Polygon.prototype.intersectsExtent = function(extent) {
return intersectsLinearRingArray(
this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, extent);
};
/**
* Set the coordinates of the polygon.
* @param {!Array.<Array.<module:ol/coordinate~Coordinate>>} coordinates Coordinates.
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
* @override
* @api
*/
Polygon.prototype.setCoordinates = function(coordinates, opt_layout) {
this.setLayout(opt_layout, coordinates, 2);
if (!this.flatCoordinates) {
this.flatCoordinates = [];
}
const ends = deflateCoordinatesArray(
this.flatCoordinates, 0, coordinates, this.stride, this.ends_);
this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1];
this.changed();
};
export default Polygon;

View File

@@ -19,29 +19,243 @@ import {clear} from '../obj.js';
* @extends {module:ol/geom/Geometry}
* @api
*/
const SimpleGeometry = function() {
class SimpleGeometry {
constructor() {
Geometry.call(this);
Geometry.call(this);
/**
* @protected
* @type {module:ol/geom/GeometryLayout}
*/
this.layout = GeometryLayout.XY;
/**
* @protected
* @type {number}
*/
this.stride = 2;
/**
* @protected
* @type {Array.<number>}
*/
this.flatCoordinates = null;
}
/**
* @protected
* @type {module:ol/geom/GeometryLayout}
* @inheritDoc
*/
this.layout = GeometryLayout.XY;
computeExtent(extent) {
return createOrUpdateFromFlatCoordinates(this.flatCoordinates,
0, this.flatCoordinates.length, this.stride, extent);
}
/**
* @protected
* @type {number}
* @abstract
* @return {Array} Coordinates.
*/
this.stride = 2;
getCoordinates() {}
/**
* @protected
* @type {Array.<number>}
* Return the first coordinate of the geometry.
* @return {module:ol/coordinate~Coordinate} First coordinate.
* @api
*/
this.flatCoordinates = null;
getFirstCoordinate() {
return this.flatCoordinates.slice(0, this.stride);
}
};
/**
* @return {Array.<number>} Flat coordinates.
*/
getFlatCoordinates() {
return this.flatCoordinates;
}
/**
* Return the last coordinate of the geometry.
* @return {module:ol/coordinate~Coordinate} Last point.
* @api
*/
getLastCoordinate() {
return this.flatCoordinates.slice(this.flatCoordinates.length - this.stride);
}
/**
* Return the {@link module:ol/geom/GeometryLayout~GeometryLayout layout} of the geometry.
* @return {module:ol/geom/GeometryLayout} Layout.
* @api
*/
getLayout() {
return this.layout;
}
/**
* @inheritDoc
*/
getSimplifiedGeometry(squaredTolerance) {
if (this.simplifiedGeometryRevision != this.getRevision()) {
clear(this.simplifiedGeometryCache);
this.simplifiedGeometryMaxMinSquaredTolerance = 0;
this.simplifiedGeometryRevision = this.getRevision();
}
// If squaredTolerance is negative or if we know that simplification will not
// have any effect then just return this.
if (squaredTolerance < 0 ||
(this.simplifiedGeometryMaxMinSquaredTolerance !== 0 &&
squaredTolerance <= this.simplifiedGeometryMaxMinSquaredTolerance)) {
return this;
}
const key = squaredTolerance.toString();
if (this.simplifiedGeometryCache.hasOwnProperty(key)) {
return this.simplifiedGeometryCache[key];
} else {
const simplifiedGeometry =
this.getSimplifiedGeometryInternal(squaredTolerance);
const simplifiedFlatCoordinates = simplifiedGeometry.getFlatCoordinates();
if (simplifiedFlatCoordinates.length < this.flatCoordinates.length) {
this.simplifiedGeometryCache[key] = simplifiedGeometry;
return simplifiedGeometry;
} else {
// Simplification did not actually remove any coordinates. We now know
// that any calls to getSimplifiedGeometry with a squaredTolerance less
// than or equal to the current squaredTolerance will also not have any
// effect. This allows us to short circuit simplification (saving CPU
// cycles) and prevents the cache of simplified geometries from filling
// up with useless identical copies of this geometry (saving memory).
this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance;
return this;
}
}
}
/**
* @param {number} squaredTolerance Squared tolerance.
* @return {module:ol/geom/SimpleGeometry} Simplified geometry.
* @protected
*/
getSimplifiedGeometryInternal(squaredTolerance) {
return this;
}
/**
* @return {number} Stride.
*/
getStride() {
return this.stride;
}
/**
* @param {module:ol/geom/GeometryLayout} layout Layout.
* @param {Array.<number>} flatCoordinates Flat coordinates.
*/
setFlatCoordinates(layout, flatCoordinates) {
this.stride = getStrideForLayout(layout);
this.layout = layout;
this.flatCoordinates = flatCoordinates;
}
/**
* @abstract
* @param {!Array} coordinates Coordinates.
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
*/
setCoordinates(coordinates, opt_layout) {}
/**
* @param {module:ol/geom/GeometryLayout|undefined} layout Layout.
* @param {Array} coordinates Coordinates.
* @param {number} nesting Nesting.
* @protected
*/
setLayout(layout, coordinates, nesting) {
/** @type {number} */
let stride;
if (layout) {
stride = getStrideForLayout(layout);
} else {
for (let i = 0; i < nesting; ++i) {
if (coordinates.length === 0) {
this.layout = GeometryLayout.XY;
this.stride = 2;
return;
} else {
coordinates = /** @type {Array} */ (coordinates[0]);
}
}
stride = coordinates.length;
layout = getLayoutForStride(stride);
}
this.layout = layout;
this.stride = stride;
}
/**
* @inheritDoc
* @api
*/
applyTransform(transformFn) {
if (this.flatCoordinates) {
transformFn(this.flatCoordinates, this.flatCoordinates, this.stride);
this.changed();
}
}
/**
* @inheritDoc
* @api
*/
rotate(angle, anchor) {
const flatCoordinates = this.getFlatCoordinates();
if (flatCoordinates) {
const stride = this.getStride();
rotate(
flatCoordinates, 0, flatCoordinates.length,
stride, angle, anchor, flatCoordinates);
this.changed();
}
}
/**
* @inheritDoc
* @api
*/
scale(sx, opt_sy, opt_anchor) {
let sy = opt_sy;
if (sy === undefined) {
sy = sx;
}
let anchor = opt_anchor;
if (!anchor) {
anchor = getCenter(this.getExtent());
}
const flatCoordinates = this.getFlatCoordinates();
if (flatCoordinates) {
const stride = this.getStride();
scale(
flatCoordinates, 0, flatCoordinates.length,
stride, sx, sy, anchor, flatCoordinates);
this.changed();
}
}
/**
* @inheritDoc
* @api
*/
translate(deltaX, deltaY) {
const flatCoordinates = this.getFlatCoordinates();
if (flatCoordinates) {
const stride = this.getStride();
translate(
flatCoordinates, 0, flatCoordinates.length, stride,
deltaX, deltaY, flatCoordinates);
this.changed();
}
}
}
inherits(SimpleGeometry, Geometry);
@@ -88,234 +302,6 @@ export function getStrideForLayout(layout) {
SimpleGeometry.prototype.containsXY = FALSE;
/**
* @inheritDoc
*/
SimpleGeometry.prototype.computeExtent = function(extent) {
return createOrUpdateFromFlatCoordinates(this.flatCoordinates,
0, this.flatCoordinates.length, this.stride, extent);
};
/**
* @abstract
* @return {Array} Coordinates.
*/
SimpleGeometry.prototype.getCoordinates = function() {};
/**
* Return the first coordinate of the geometry.
* @return {module:ol/coordinate~Coordinate} First coordinate.
* @api
*/
SimpleGeometry.prototype.getFirstCoordinate = function() {
return this.flatCoordinates.slice(0, this.stride);
};
/**
* @return {Array.<number>} Flat coordinates.
*/
SimpleGeometry.prototype.getFlatCoordinates = function() {
return this.flatCoordinates;
};
/**
* Return the last coordinate of the geometry.
* @return {module:ol/coordinate~Coordinate} Last point.
* @api
*/
SimpleGeometry.prototype.getLastCoordinate = function() {
return this.flatCoordinates.slice(this.flatCoordinates.length - this.stride);
};
/**
* Return the {@link module:ol/geom/GeometryLayout~GeometryLayout layout} of the geometry.
* @return {module:ol/geom/GeometryLayout} Layout.
* @api
*/
SimpleGeometry.prototype.getLayout = function() {
return this.layout;
};
/**
* @inheritDoc
*/
SimpleGeometry.prototype.getSimplifiedGeometry = function(squaredTolerance) {
if (this.simplifiedGeometryRevision != this.getRevision()) {
clear(this.simplifiedGeometryCache);
this.simplifiedGeometryMaxMinSquaredTolerance = 0;
this.simplifiedGeometryRevision = this.getRevision();
}
// If squaredTolerance is negative or if we know that simplification will not
// have any effect then just return this.
if (squaredTolerance < 0 ||
(this.simplifiedGeometryMaxMinSquaredTolerance !== 0 &&
squaredTolerance <= this.simplifiedGeometryMaxMinSquaredTolerance)) {
return this;
}
const key = squaredTolerance.toString();
if (this.simplifiedGeometryCache.hasOwnProperty(key)) {
return this.simplifiedGeometryCache[key];
} else {
const simplifiedGeometry =
this.getSimplifiedGeometryInternal(squaredTolerance);
const simplifiedFlatCoordinates = simplifiedGeometry.getFlatCoordinates();
if (simplifiedFlatCoordinates.length < this.flatCoordinates.length) {
this.simplifiedGeometryCache[key] = simplifiedGeometry;
return simplifiedGeometry;
} else {
// Simplification did not actually remove any coordinates. We now know
// that any calls to getSimplifiedGeometry with a squaredTolerance less
// than or equal to the current squaredTolerance will also not have any
// effect. This allows us to short circuit simplification (saving CPU
// cycles) and prevents the cache of simplified geometries from filling
// up with useless identical copies of this geometry (saving memory).
this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance;
return this;
}
}
};
/**
* @param {number} squaredTolerance Squared tolerance.
* @return {module:ol/geom/SimpleGeometry} Simplified geometry.
* @protected
*/
SimpleGeometry.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) {
return this;
};
/**
* @return {number} Stride.
*/
SimpleGeometry.prototype.getStride = function() {
return this.stride;
};
/**
* @param {module:ol/geom/GeometryLayout} layout Layout.
* @param {Array.<number>} flatCoordinates Flat coordinates.
*/
SimpleGeometry.prototype.setFlatCoordinates = function(layout, flatCoordinates) {
this.stride = getStrideForLayout(layout);
this.layout = layout;
this.flatCoordinates = flatCoordinates;
};
/**
* @abstract
* @param {!Array} coordinates Coordinates.
* @param {module:ol/geom/GeometryLayout=} opt_layout Layout.
*/
SimpleGeometry.prototype.setCoordinates = function(coordinates, opt_layout) {};
/**
* @param {module:ol/geom/GeometryLayout|undefined} layout Layout.
* @param {Array} coordinates Coordinates.
* @param {number} nesting Nesting.
* @protected
*/
SimpleGeometry.prototype.setLayout = function(layout, coordinates, nesting) {
/** @type {number} */
let stride;
if (layout) {
stride = getStrideForLayout(layout);
} else {
for (let i = 0; i < nesting; ++i) {
if (coordinates.length === 0) {
this.layout = GeometryLayout.XY;
this.stride = 2;
return;
} else {
coordinates = /** @type {Array} */ (coordinates[0]);
}
}
stride = coordinates.length;
layout = getLayoutForStride(stride);
}
this.layout = layout;
this.stride = stride;
};
/**
* @inheritDoc
* @api
*/
SimpleGeometry.prototype.applyTransform = function(transformFn) {
if (this.flatCoordinates) {
transformFn(this.flatCoordinates, this.flatCoordinates, this.stride);
this.changed();
}
};
/**
* @inheritDoc
* @api
*/
SimpleGeometry.prototype.rotate = function(angle, anchor) {
const flatCoordinates = this.getFlatCoordinates();
if (flatCoordinates) {
const stride = this.getStride();
rotate(
flatCoordinates, 0, flatCoordinates.length,
stride, angle, anchor, flatCoordinates);
this.changed();
}
};
/**
* @inheritDoc
* @api
*/
SimpleGeometry.prototype.scale = function(sx, opt_sy, opt_anchor) {
let sy = opt_sy;
if (sy === undefined) {
sy = sx;
}
let anchor = opt_anchor;
if (!anchor) {
anchor = getCenter(this.getExtent());
}
const flatCoordinates = this.getFlatCoordinates();
if (flatCoordinates) {
const stride = this.getStride();
scale(
flatCoordinates, 0, flatCoordinates.length,
stride, sx, sy, anchor, flatCoordinates);
this.changed();
}
};
/**
* @inheritDoc
* @api
*/
SimpleGeometry.prototype.translate = function(deltaX, deltaY) {
const flatCoordinates = this.getFlatCoordinates();
if (flatCoordinates) {
const stride = this.getStride();
translate(
flatCoordinates, 0, flatCoordinates.length, stride,
deltaX, deltaY, flatCoordinates);
this.changed();
}
};
/**
* @param {module:ol/geom/SimpleGeometry} simpleGeometry Simple geometry.
* @param {module:ol/transform~Transform} transform Transform.

View File

@@ -89,47 +89,156 @@ inherits(DragAndDropEvent, Event);
* @param {module:ol/interaction/DragAndDrop~Options=} opt_options Options.
* @api
*/
const DragAndDrop = function(opt_options) {
class DragAndDrop {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
Interaction.call(this, {
handleEvent: TRUE
});
Interaction.call(this, {
handleEvent: TRUE
});
/**
* @private
* @type {Array.<function(new: module:ol/format/Feature)>}
*/
this.formatConstructors_ = options.formatConstructors ?
options.formatConstructors : [];
/**
* @private
* @type {module:ol/proj/Projection}
*/
this.projection_ = options.projection ?
getProjection(options.projection) : null;
/**
* @private
* @type {Array.<module:ol/events~EventsKey>}
*/
this.dropListenKeys_ = null;
/**
* @private
* @type {module:ol/source/Vector}
*/
this.source_ = options.source || null;
/**
* @private
* @type {Element}
*/
this.target = options.target ? options.target : null;
}
/**
* @param {File} file File.
* @param {Event} event Load event.
* @private
*/
handleResult_(file, event) {
const result = event.target.result;
const map = this.getMap();
let projection = this.projection_;
if (!projection) {
const view = map.getView();
projection = view.getProjection();
}
const formatConstructors = this.formatConstructors_;
let features = [];
for (let i = 0, ii = formatConstructors.length; i < ii; ++i) {
/**
* Avoid "cannot instantiate abstract class" error.
* @type {Function}
*/
const formatConstructor = formatConstructors[i];
/**
* @type {module:ol/format/Feature}
*/
const format = new formatConstructor();
features = this.tryReadFeatures_(format, result, {
featureProjection: projection
});
if (features && features.length > 0) {
break;
}
}
if (this.source_) {
this.source_.clear();
this.source_.addFeatures(features);
}
this.dispatchEvent(
new DragAndDropEvent(
DragAndDropEventType.ADD_FEATURES, file,
features, projection));
}
/**
* @private
* @type {Array.<function(new: module:ol/format/Feature)>}
*/
this.formatConstructors_ = options.formatConstructors ?
options.formatConstructors : [];
registerListeners_() {
const map = this.getMap();
if (map) {
const dropArea = this.target ? this.target : map.getViewport();
this.dropListenKeys_ = [
listen(dropArea, EventType.DROP, handleDrop, this),
listen(dropArea, EventType.DRAGENTER, handleStop, this),
listen(dropArea, EventType.DRAGOVER, handleStop, this),
listen(dropArea, EventType.DROP, handleStop, this)
];
}
}
/**
* @inheritDoc
*/
setActive(active) {
Interaction.prototype.setActive.call(this, active);
if (active) {
this.registerListeners_();
} else {
this.unregisterListeners_();
}
}
/**
* @inheritDoc
*/
setMap(map) {
this.unregisterListeners_();
Interaction.prototype.setMap.call(this, map);
if (this.getActive()) {
this.registerListeners_();
}
}
/**
* @param {module:ol/format/Feature} format Format.
* @param {string} text Text.
* @param {module:ol/format/Feature~ReadOptions} options Read options.
* @private
* @return {Array.<module:ol/Feature>} Features.
*/
tryReadFeatures_(format, text, options) {
try {
return format.readFeatures(text, options);
} catch (e) {
return null;
}
}
/**
* @private
* @type {module:ol/proj/Projection}
*/
this.projection_ = options.projection ?
getProjection(options.projection) : null;
/**
* @private
* @type {Array.<module:ol/events~EventsKey>}
*/
this.dropListenKeys_ = null;
/**
* @private
* @type {module:ol/source/Vector}
*/
this.source_ = options.source || null;
/**
* @private
* @type {Element}
*/
this.target = options.target ? options.target : null;
};
unregisterListeners_() {
if (this.dropListenKeys_) {
this.dropListenKeys_.forEach(unlistenByKey);
this.dropListenKeys_ = null;
}
}
}
inherits(DragAndDrop, Interaction);
@@ -159,117 +268,4 @@ function handleStop(event) {
}
/**
* @param {File} file File.
* @param {Event} event Load event.
* @private
*/
DragAndDrop.prototype.handleResult_ = function(file, event) {
const result = event.target.result;
const map = this.getMap();
let projection = this.projection_;
if (!projection) {
const view = map.getView();
projection = view.getProjection();
}
const formatConstructors = this.formatConstructors_;
let features = [];
for (let i = 0, ii = formatConstructors.length; i < ii; ++i) {
/**
* Avoid "cannot instantiate abstract class" error.
* @type {Function}
*/
const formatConstructor = formatConstructors[i];
/**
* @type {module:ol/format/Feature}
*/
const format = new formatConstructor();
features = this.tryReadFeatures_(format, result, {
featureProjection: projection
});
if (features && features.length > 0) {
break;
}
}
if (this.source_) {
this.source_.clear();
this.source_.addFeatures(features);
}
this.dispatchEvent(
new DragAndDropEvent(
DragAndDropEventType.ADD_FEATURES, file,
features, projection));
};
/**
* @private
*/
DragAndDrop.prototype.registerListeners_ = function() {
const map = this.getMap();
if (map) {
const dropArea = this.target ? this.target : map.getViewport();
this.dropListenKeys_ = [
listen(dropArea, EventType.DROP, handleDrop, this),
listen(dropArea, EventType.DRAGENTER, handleStop, this),
listen(dropArea, EventType.DRAGOVER, handleStop, this),
listen(dropArea, EventType.DROP, handleStop, this)
];
}
};
/**
* @inheritDoc
*/
DragAndDrop.prototype.setActive = function(active) {
Interaction.prototype.setActive.call(this, active);
if (active) {
this.registerListeners_();
} else {
this.unregisterListeners_();
}
};
/**
* @inheritDoc
*/
DragAndDrop.prototype.setMap = function(map) {
this.unregisterListeners_();
Interaction.prototype.setMap.call(this, map);
if (this.getActive()) {
this.registerListeners_();
}
};
/**
* @param {module:ol/format/Feature} format Format.
* @param {string} text Text.
* @param {module:ol/format/Feature~ReadOptions} options Read options.
* @private
* @return {Array.<module:ol/Feature>} Features.
*/
DragAndDrop.prototype.tryReadFeatures_ = function(format, text, options) {
try {
return format.readFeatures(text, options);
} catch (e) {
return null;
}
};
/**
* @private
*/
DragAndDrop.prototype.unregisterListeners_ = function() {
if (this.dropListenKeys_) {
this.dropListenKeys_.forEach(unlistenByKey);
this.dropListenKeys_ = null;
}
};
export default DragAndDrop;

View File

@@ -110,47 +110,58 @@ inherits(DragBoxEvent, Event);
* @param {module:ol/interaction/DragBox~Options=} opt_options Options.
* @api
*/
const DragBox = function(opt_options) {
class DragBox {
constructor(opt_options) {
PointerInteraction.call(this, {
handleDownEvent: handleDownEvent,
handleDragEvent: handleDragEvent,
handleUpEvent: handleUpEvent
});
PointerInteraction.call(this, {
handleDownEvent: handleDownEvent,
handleDragEvent: handleDragEvent,
handleUpEvent: handleUpEvent
});
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
/**
* @type {module:ol/render/Box}
* @private
*/
this.box_ = new RenderBox(options.className || 'ol-dragbox');
/**
* @type {module:ol/render/Box}
* @private
*/
this.box_ = new RenderBox(options.className || 'ol-dragbox');
/**
* @type {number}
* @private
*/
this.minArea_ = options.minArea !== undefined ? options.minArea : 64;
/**
* @type {number}
* @private
*/
this.minArea_ = options.minArea !== undefined ? options.minArea : 64;
/**
* @type {module:ol~Pixel}
* @private
*/
this.startPixel_ = null;
/**
* @type {module:ol~Pixel}
* @private
*/
this.startPixel_ = null;
/**
* @private
* @type {module:ol/events/condition~Condition}
*/
this.condition_ = options.condition ? options.condition : always;
/**
* @private
* @type {module:ol/events/condition~Condition}
*/
this.condition_ = options.condition ? options.condition : always;
/**
* @private
* @type {module:ol/interaction/DragBox~EndCondition}
*/
this.boxEndCondition_ = options.boxEndCondition ?
options.boxEndCondition : defaultBoxEndCondition;
};
/**
* @private
* @type {module:ol/interaction/DragBox~EndCondition}
*/
this.boxEndCondition_ = options.boxEndCondition ?
options.boxEndCondition : defaultBoxEndCondition;
}
/**
* Returns geometry of last drawn box.
* @return {module:ol/geom/Polygon} Geometry.
* @api
*/
getGeometry() {
return this.box_.getGeometry();
}
}
inherits(DragBox, PointerInteraction);
@@ -188,16 +199,6 @@ function handleDragEvent(mapBrowserEvent) {
}
/**
* Returns geometry of last drawn box.
* @return {module:ol/geom/Polygon} Geometry.
* @api
*/
DragBox.prototype.getGeometry = function() {
return this.box_.getGeometry();
};
/**
* To be overridden by child classes.
* FIXME: use constructor option instead of relying on overriding.

View File

@@ -35,68 +35,71 @@ import DragBox from '../interaction/DragBox.js';
* @param {module:ol/interaction/DragZoom~Options=} opt_options Options.
* @api
*/
const DragZoom = function(opt_options) {
const options = opt_options ? opt_options : {};
class DragZoom {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const condition = options.condition ? options.condition : shiftKeyOnly;
const condition = options.condition ? options.condition : shiftKeyOnly;
/**
* @private
* @type {number}
*/
this.duration_ = options.duration !== undefined ? options.duration : 200;
/**
* @private
* @type {boolean}
*/
this.out_ = options.out !== undefined ? options.out : false;
DragBox.call(this, {
condition: condition,
className: options.className || 'ol-dragzoom'
});
}
/**
* @private
* @type {number}
* @inheritDoc
*/
this.duration_ = options.duration !== undefined ? options.duration : 200;
onBoxEnd() {
const map = this.getMap();
/**
* @private
* @type {boolean}
*/
this.out_ = options.out !== undefined ? options.out : false;
const view = /** @type {!module:ol/View} */ (map.getView());
DragBox.call(this, {
condition: condition,
className: options.className || 'ol-dragzoom'
});
const size = /** @type {!module:ol/size~Size} */ (map.getSize());
};
let extent = this.getGeometry().getExtent();
if (this.out_) {
const mapExtent = view.calculateExtent(size);
const boxPixelExtent = createOrUpdateFromCoordinates([
map.getPixelFromCoordinate(getBottomLeft(extent)),
map.getPixelFromCoordinate(getTopRight(extent))]);
const factor = view.getResolutionForExtent(boxPixelExtent, size);
scaleFromCenter(mapExtent, 1 / factor);
extent = mapExtent;
}
const resolution = view.constrainResolution(
view.getResolutionForExtent(extent, size));
let center = getCenter(extent);
center = view.constrainCenter(center);
view.animate({
resolution: resolution,
center: center,
duration: this.duration_,
easing: easeOut
});
}
}
inherits(DragZoom, DragBox);
/**
* @inheritDoc
*/
DragZoom.prototype.onBoxEnd = function() {
const map = this.getMap();
const view = /** @type {!module:ol/View} */ (map.getView());
const size = /** @type {!module:ol/size~Size} */ (map.getSize());
let extent = this.getGeometry().getExtent();
if (this.out_) {
const mapExtent = view.calculateExtent(size);
const boxPixelExtent = createOrUpdateFromCoordinates([
map.getPixelFromCoordinate(getBottomLeft(extent)),
map.getPixelFromCoordinate(getTopRight(extent))]);
const factor = view.getResolutionForExtent(boxPixelExtent, size);
scaleFromCenter(mapExtent, 1 / factor);
extent = mapExtent;
}
const resolution = view.constrainResolution(
view.getResolutionForExtent(extent, size));
let center = getCenter(extent);
center = view.constrainCenter(center);
view.animate({
resolution: resolution,
center: center,
duration: this.duration_,
easing: easeOut
});
};
export default DragZoom;

File diff suppressed because it is too large Load Diff

View File

@@ -82,98 +82,233 @@ inherits(ExtentInteractionEvent, Event);
* @param {module:ol/interaction/Extent~Options=} opt_options Options.
* @api
*/
const ExtentInteraction = function(opt_options) {
class ExtentInteraction {
constructor(opt_options) {
const options = opt_options || {};
const options = opt_options || {};
/**
* Extent of the drawn box
* @type {module:ol/extent~Extent}
* @private
*/
this.extent_ = null;
/**
* Extent of the drawn box
* @type {module:ol/extent~Extent}
* @private
*/
this.extent_ = null;
/**
* Handler for pointer move events
* @type {function (module:ol/coordinate~Coordinate): module:ol/extent~Extent|null}
* @private
*/
this.pointerHandler_ = null;
/**
* Handler for pointer move events
* @type {function (module:ol/coordinate~Coordinate): module:ol/extent~Extent|null}
* @private
*/
this.pointerHandler_ = null;
/**
* Pixel threshold to snap to extent
* @type {number}
* @private
*/
this.pixelTolerance_ = options.pixelTolerance !== undefined ?
options.pixelTolerance : 10;
/**
* Pixel threshold to snap to extent
* @type {number}
* @private
*/
this.pixelTolerance_ = options.pixelTolerance !== undefined ?
options.pixelTolerance : 10;
/**
* Is the pointer snapped to an extent vertex
* @type {boolean}
* @private
*/
this.snappedToVertex_ = false;
/**
* Is the pointer snapped to an extent vertex
* @type {boolean}
* @private
*/
this.snappedToVertex_ = false;
/**
* Feature for displaying the visible extent
* @type {module:ol/Feature}
* @private
*/
this.extentFeature_ = null;
/**
* Feature for displaying the visible extent
* @type {module:ol/Feature}
* @private
*/
this.extentFeature_ = null;
/**
* Feature for displaying the visible pointer
* @type {module:ol/Feature}
* @private
*/
this.vertexFeature_ = null;
/**
* Feature for displaying the visible pointer
* @type {module:ol/Feature}
* @private
*/
this.vertexFeature_ = null;
if (!opt_options) {
opt_options = {};
if (!opt_options) {
opt_options = {};
}
PointerInteraction.call(this, {
handleDownEvent: handleDownEvent,
handleDragEvent: handleDragEvent,
handleEvent: handleEvent,
handleUpEvent: handleUpEvent
});
/**
* Layer for the extentFeature
* @type {module:ol/layer/Vector}
* @private
*/
this.extentOverlay_ = new VectorLayer({
source: new VectorSource({
useSpatialIndex: false,
wrapX: !!opt_options.wrapX
}),
style: opt_options.boxStyle ? opt_options.boxStyle : getDefaultExtentStyleFunction(),
updateWhileAnimating: true,
updateWhileInteracting: true
});
/**
* Layer for the vertexFeature
* @type {module:ol/layer/Vector}
* @private
*/
this.vertexOverlay_ = new VectorLayer({
source: new VectorSource({
useSpatialIndex: false,
wrapX: !!opt_options.wrapX
}),
style: opt_options.pointerStyle ? opt_options.pointerStyle : getDefaultPointerStyleFunction(),
updateWhileAnimating: true,
updateWhileInteracting: true
});
if (opt_options.extent) {
this.setExtent(opt_options.extent);
}
}
PointerInteraction.call(this, {
handleDownEvent: handleDownEvent,
handleDragEvent: handleDragEvent,
handleEvent: handleEvent,
handleUpEvent: handleUpEvent
});
/**
* Layer for the extentFeature
* @type {module:ol/layer/Vector}
* @param {module:ol~Pixel} pixel cursor location
* @param {module:ol/PluggableMap} map map
* @returns {module:ol/coordinate~Coordinate|null} snapped vertex on extent
* @private
*/
this.extentOverlay_ = new VectorLayer({
source: new VectorSource({
useSpatialIndex: false,
wrapX: !!opt_options.wrapX
}),
style: opt_options.boxStyle ? opt_options.boxStyle : getDefaultExtentStyleFunction(),
updateWhileAnimating: true,
updateWhileInteracting: true
});
snapToVertex_(pixel, map) {
const pixelCoordinate = map.getCoordinateFromPixel(pixel);
const sortByDistance = function(a, b) {
return squaredDistanceToSegment(pixelCoordinate, a) -
squaredDistanceToSegment(pixelCoordinate, b);
};
const extent = this.getExtent();
if (extent) {
//convert extents to line segments and find the segment closest to pixelCoordinate
const segments = getSegments(extent);
segments.sort(sortByDistance);
const closestSegment = segments[0];
/**
* Layer for the vertexFeature
* @type {module:ol/layer/Vector}
* @private
*/
this.vertexOverlay_ = new VectorLayer({
source: new VectorSource({
useSpatialIndex: false,
wrapX: !!opt_options.wrapX
}),
style: opt_options.pointerStyle ? opt_options.pointerStyle : getDefaultPointerStyleFunction(),
updateWhileAnimating: true,
updateWhileInteracting: true
});
let vertex = (closestOnSegment(pixelCoordinate,
closestSegment));
const vertexPixel = map.getPixelFromCoordinate(vertex);
if (opt_options.extent) {
this.setExtent(opt_options.extent);
//if the distance is within tolerance, snap to the segment
if (coordinateDistance(pixel, vertexPixel) <= this.pixelTolerance_) {
//test if we should further snap to a vertex
const pixel1 = map.getPixelFromCoordinate(closestSegment[0]);
const pixel2 = map.getPixelFromCoordinate(closestSegment[1]);
const squaredDist1 = squaredCoordinateDistance(vertexPixel, pixel1);
const squaredDist2 = squaredCoordinateDistance(vertexPixel, pixel2);
const dist = Math.sqrt(Math.min(squaredDist1, squaredDist2));
this.snappedToVertex_ = dist <= this.pixelTolerance_;
if (this.snappedToVertex_) {
vertex = squaredDist1 > squaredDist2 ?
closestSegment[1] : closestSegment[0];
}
return vertex;
}
}
return null;
}
};
/**
* @param {module:ol/MapBrowserEvent} mapBrowserEvent pointer move event
* @private
*/
handlePointerMove_(mapBrowserEvent) {
const pixel = mapBrowserEvent.pixel;
const map = mapBrowserEvent.map;
let vertex = this.snapToVertex_(pixel, map);
if (!vertex) {
vertex = map.getCoordinateFromPixel(pixel);
}
this.createOrUpdatePointerFeature_(vertex);
}
/**
* @param {module:ol/extent~Extent} extent extent
* @returns {module:ol/Feature} extent as featrue
* @private
*/
createOrUpdateExtentFeature_(extent) {
let extentFeature = this.extentFeature_;
if (!extentFeature) {
if (!extent) {
extentFeature = new Feature({});
} else {
extentFeature = new Feature(polygonFromExtent(extent));
}
this.extentFeature_ = extentFeature;
this.extentOverlay_.getSource().addFeature(extentFeature);
} else {
if (!extent) {
extentFeature.setGeometry(undefined);
} else {
extentFeature.setGeometry(polygonFromExtent(extent));
}
}
return extentFeature;
}
/**
* @param {module:ol/coordinate~Coordinate} vertex location of feature
* @returns {module:ol/Feature} vertex as feature
* @private
*/
createOrUpdatePointerFeature_(vertex) {
let vertexFeature = this.vertexFeature_;
if (!vertexFeature) {
vertexFeature = new Feature(new Point(vertex));
this.vertexFeature_ = vertexFeature;
this.vertexOverlay_.getSource().addFeature(vertexFeature);
} else {
const geometry = /** @type {module:ol/geom/Point} */ (vertexFeature.getGeometry());
geometry.setCoordinates(vertex);
}
return vertexFeature;
}
/**
* @inheritDoc
*/
setMap(map) {
this.extentOverlay_.setMap(map);
this.vertexOverlay_.setMap(map);
PointerInteraction.prototype.setMap.call(this, map);
}
/**
* Returns the current drawn extent in the view projection
*
* @return {module:ol/extent~Extent} Drawn extent in the view projection.
* @api
*/
getExtent() {
return this.extent_;
}
/**
* Manually sets the drawn extent, using the view projection.
*
* @param {module:ol/extent~Extent} extent Extent
* @api
*/
setExtent(extent) {
//Null extent means no bbox
this.extent_ = extent ? extent : null;
this.createOrUpdateExtentFeature_(extent);
this.dispatchEvent(new ExtentInteractionEvent(this.extent_));
}
}
inherits(ExtentInteraction, PointerInteraction);
@@ -350,140 +485,5 @@ function getSegments(extent) {
];
}
/**
* @param {module:ol~Pixel} pixel cursor location
* @param {module:ol/PluggableMap} map map
* @returns {module:ol/coordinate~Coordinate|null} snapped vertex on extent
* @private
*/
ExtentInteraction.prototype.snapToVertex_ = function(pixel, map) {
const pixelCoordinate = map.getCoordinateFromPixel(pixel);
const sortByDistance = function(a, b) {
return squaredDistanceToSegment(pixelCoordinate, a) -
squaredDistanceToSegment(pixelCoordinate, b);
};
const extent = this.getExtent();
if (extent) {
//convert extents to line segments and find the segment closest to pixelCoordinate
const segments = getSegments(extent);
segments.sort(sortByDistance);
const closestSegment = segments[0];
let vertex = (closestOnSegment(pixelCoordinate,
closestSegment));
const vertexPixel = map.getPixelFromCoordinate(vertex);
//if the distance is within tolerance, snap to the segment
if (coordinateDistance(pixel, vertexPixel) <= this.pixelTolerance_) {
//test if we should further snap to a vertex
const pixel1 = map.getPixelFromCoordinate(closestSegment[0]);
const pixel2 = map.getPixelFromCoordinate(closestSegment[1]);
const squaredDist1 = squaredCoordinateDistance(vertexPixel, pixel1);
const squaredDist2 = squaredCoordinateDistance(vertexPixel, pixel2);
const dist = Math.sqrt(Math.min(squaredDist1, squaredDist2));
this.snappedToVertex_ = dist <= this.pixelTolerance_;
if (this.snappedToVertex_) {
vertex = squaredDist1 > squaredDist2 ?
closestSegment[1] : closestSegment[0];
}
return vertex;
}
}
return null;
};
/**
* @param {module:ol/MapBrowserEvent} mapBrowserEvent pointer move event
* @private
*/
ExtentInteraction.prototype.handlePointerMove_ = function(mapBrowserEvent) {
const pixel = mapBrowserEvent.pixel;
const map = mapBrowserEvent.map;
let vertex = this.snapToVertex_(pixel, map);
if (!vertex) {
vertex = map.getCoordinateFromPixel(pixel);
}
this.createOrUpdatePointerFeature_(vertex);
};
/**
* @param {module:ol/extent~Extent} extent extent
* @returns {module:ol/Feature} extent as featrue
* @private
*/
ExtentInteraction.prototype.createOrUpdateExtentFeature_ = function(extent) {
let extentFeature = this.extentFeature_;
if (!extentFeature) {
if (!extent) {
extentFeature = new Feature({});
} else {
extentFeature = new Feature(polygonFromExtent(extent));
}
this.extentFeature_ = extentFeature;
this.extentOverlay_.getSource().addFeature(extentFeature);
} else {
if (!extent) {
extentFeature.setGeometry(undefined);
} else {
extentFeature.setGeometry(polygonFromExtent(extent));
}
}
return extentFeature;
};
/**
* @param {module:ol/coordinate~Coordinate} vertex location of feature
* @returns {module:ol/Feature} vertex as feature
* @private
*/
ExtentInteraction.prototype.createOrUpdatePointerFeature_ = function(vertex) {
let vertexFeature = this.vertexFeature_;
if (!vertexFeature) {
vertexFeature = new Feature(new Point(vertex));
this.vertexFeature_ = vertexFeature;
this.vertexOverlay_.getSource().addFeature(vertexFeature);
} else {
const geometry = /** @type {module:ol/geom/Point} */ (vertexFeature.getGeometry());
geometry.setCoordinates(vertex);
}
return vertexFeature;
};
/**
* @inheritDoc
*/
ExtentInteraction.prototype.setMap = function(map) {
this.extentOverlay_.setMap(map);
this.vertexOverlay_.setMap(map);
PointerInteraction.prototype.setMap.call(this, map);
};
/**
* Returns the current drawn extent in the view projection
*
* @return {module:ol/extent~Extent} Drawn extent in the view projection.
* @api
*/
ExtentInteraction.prototype.getExtent = function() {
return this.extent_;
};
/**
* Manually sets the drawn extent, using the view projection.
*
* @param {module:ol/extent~Extent} extent Extent
* @api
*/
ExtentInteraction.prototype.setExtent = function(extent) {
//Null extent means no bbox
this.extent_ = extent ? extent : null;
this.createOrUpdateExtentFeature_(extent);
this.dispatchEvent(new ExtentInteractionEvent(this.extent_));
};
export default ExtentInteraction;

View File

@@ -36,71 +36,69 @@ import {clamp} from '../math.js';
* @extends {module:ol/Object}
* @api
*/
const Interaction = function(options) {
class Interaction {
constructor(options) {
BaseObject.call(this);
BaseObject.call(this);
/**
* @private
* @type {module:ol/PluggableMap}
*/
this.map_ = null;
this.setActive(true);
/**
* @type {function(module:ol/MapBrowserEvent):boolean}
*/
this.handleEvent = options.handleEvent;
}
/**
* @private
* @type {module:ol/PluggableMap}
* Return whether the interaction is currently active.
* @return {boolean} `true` if the interaction is active, `false` otherwise.
* @observable
* @api
*/
this.map_ = null;
this.setActive(true);
getActive() {
return /** @type {boolean} */ (this.get(InteractionProperty.ACTIVE));
}
/**
* @type {function(module:ol/MapBrowserEvent):boolean}
* Get the map associated with this interaction.
* @return {module:ol/PluggableMap} Map.
* @api
*/
this.handleEvent = options.handleEvent;
getMap() {
return this.map_;
}
};
/**
* Activate or deactivate the interaction.
* @param {boolean} active Active.
* @observable
* @api
*/
setActive(active) {
this.set(InteractionProperty.ACTIVE, active);
}
/**
* Remove the interaction from its current map and attach it to the new map.
* Subclasses may set up event handlers to get notified about changes to
* the map here.
* @param {module:ol/PluggableMap} map Map.
*/
setMap(map) {
this.map_ = map;
}
}
inherits(Interaction, BaseObject);
/**
* Return whether the interaction is currently active.
* @return {boolean} `true` if the interaction is active, `false` otherwise.
* @observable
* @api
*/
Interaction.prototype.getActive = function() {
return /** @type {boolean} */ (this.get(InteractionProperty.ACTIVE));
};
/**
* Get the map associated with this interaction.
* @return {module:ol/PluggableMap} Map.
* @api
*/
Interaction.prototype.getMap = function() {
return this.map_;
};
/**
* Activate or deactivate the interaction.
* @param {boolean} active Active.
* @observable
* @api
*/
Interaction.prototype.setActive = function(active) {
this.set(InteractionProperty.ACTIVE, active);
};
/**
* Remove the interaction from its current map and attach it to the new map.
* Subclasses may set up event handlers to get notified about changes to
* the map here.
* @param {module:ol/PluggableMap} map Map.
*/
Interaction.prototype.setMap = function(map) {
this.map_ = map;
};
/**
* @param {module:ol/View} view View.
* @param {module:ol/coordinate~Coordinate} delta Delta.

File diff suppressed because it is too large Load Diff

View File

@@ -53,101 +53,144 @@ export const Mode = {
* @param {module:ol/interaction/MouseWheelZoom~Options=} opt_options Options.
* @api
*/
const MouseWheelZoom = function(opt_options) {
class MouseWheelZoom {
constructor(opt_options) {
Interaction.call(this, {
handleEvent: handleEvent
});
Interaction.call(this, {
handleEvent: handleEvent
});
const options = opt_options || {};
const options = opt_options || {};
/**
* @private
* @type {number}
*/
this.delta_ = 0;
/**
* @private
* @type {number}
*/
this.duration_ = options.duration !== undefined ? options.duration : 250;
/**
* @private
* @type {number}
*/
this.timeout_ = options.timeout !== undefined ? options.timeout : 80;
/**
* @private
* @type {boolean}
*/
this.useAnchor_ = options.useAnchor !== undefined ? options.useAnchor : true;
/**
* @private
* @type {boolean}
*/
this.constrainResolution_ = options.constrainResolution || false;
/**
* @private
* @type {module:ol/events/condition~Condition}
*/
this.condition_ = options.condition ? options.condition : always;
/**
* @private
* @type {?module:ol/coordinate~Coordinate}
*/
this.lastAnchor_ = null;
/**
* @private
* @type {number|undefined}
*/
this.startTime_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.timeoutId_ = undefined;
/**
* @private
* @type {module:ol/interaction/MouseWheelZoom~Mode|undefined}
*/
this.mode_ = undefined;
/**
* Trackpad events separated by this delay will be considered separate
* interactions.
* @type {number}
*/
this.trackpadEventGap_ = 400;
/**
* @type {number|undefined}
*/
this.trackpadTimeoutId_ = undefined;
/**
* The number of delta values per zoom level
* @private
* @type {number}
*/
this.trackpadDeltaPerZoom_ = 300;
/**
* The zoom factor by which scroll zooming is allowed to exceed the limits.
* @private
* @type {number}
*/
this.trackpadZoomBuffer_ = 1.5;
}
/**
* @private
* @type {number}
*/
this.delta_ = 0;
decrementInteractingHint_() {
this.trackpadTimeoutId_ = undefined;
const view = this.getMap().getView();
view.setHint(ViewHint.INTERACTING, -1);
}
/**
* @private
* @type {number}
* @param {module:ol/PluggableMap} map Map.
*/
this.duration_ = options.duration !== undefined ? options.duration : 250;
handleWheelZoom_(map) {
const view = map.getView();
if (view.getAnimating()) {
view.cancelAnimations();
}
const maxDelta = MAX_DELTA;
const delta = clamp(this.delta_, -maxDelta, maxDelta);
zoomByDelta(view, -delta, this.lastAnchor_, this.duration_);
this.mode_ = undefined;
this.delta_ = 0;
this.lastAnchor_ = null;
this.startTime_ = undefined;
this.timeoutId_ = undefined;
}
/**
* @private
* @type {number}
* Enable or disable using the mouse's location as an anchor when zooming
* @param {boolean} useAnchor true to zoom to the mouse's location, false
* to zoom to the center of the map
* @api
*/
this.timeout_ = options.timeout !== undefined ? options.timeout : 80;
/**
* @private
* @type {boolean}
*/
this.useAnchor_ = options.useAnchor !== undefined ? options.useAnchor : true;
/**
* @private
* @type {boolean}
*/
this.constrainResolution_ = options.constrainResolution || false;
/**
* @private
* @type {module:ol/events/condition~Condition}
*/
this.condition_ = options.condition ? options.condition : always;
/**
* @private
* @type {?module:ol/coordinate~Coordinate}
*/
this.lastAnchor_ = null;
/**
* @private
* @type {number|undefined}
*/
this.startTime_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.timeoutId_ = undefined;
/**
* @private
* @type {module:ol/interaction/MouseWheelZoom~Mode|undefined}
*/
this.mode_ = undefined;
/**
* Trackpad events separated by this delay will be considered separate
* interactions.
* @type {number}
*/
this.trackpadEventGap_ = 400;
/**
* @type {number|undefined}
*/
this.trackpadTimeoutId_ = undefined;
/**
* The number of delta values per zoom level
* @private
* @type {number}
*/
this.trackpadDeltaPerZoom_ = 300;
/**
* The zoom factor by which scroll zooming is allowed to exceed the limits.
* @private
* @type {number}
*/
this.trackpadZoomBuffer_ = 1.5;
};
setMouseAnchor(useAnchor) {
this.useAnchor_ = useAnchor;
if (!useAnchor) {
this.lastAnchor_ = null;
}
}
}
inherits(MouseWheelZoom, Interaction);
@@ -276,48 +319,4 @@ function handleEvent(mapBrowserEvent) {
}
/**
* @private
*/
MouseWheelZoom.prototype.decrementInteractingHint_ = function() {
this.trackpadTimeoutId_ = undefined;
const view = this.getMap().getView();
view.setHint(ViewHint.INTERACTING, -1);
};
/**
* @private
* @param {module:ol/PluggableMap} map Map.
*/
MouseWheelZoom.prototype.handleWheelZoom_ = function(map) {
const view = map.getView();
if (view.getAnimating()) {
view.cancelAnimations();
}
const maxDelta = MAX_DELTA;
const delta = clamp(this.delta_, -maxDelta, maxDelta);
zoomByDelta(view, -delta, this.lastAnchor_, this.duration_);
this.mode_ = undefined;
this.delta_ = 0;
this.lastAnchor_ = null;
this.startTime_ = undefined;
this.timeoutId_ = undefined;
};
/**
* Enable or disable using the mouse's location as an anchor when zooming
* @param {boolean} useAnchor true to zoom to the mouse's location, false
* to zoom to the center of the map
* @api
*/
MouseWheelZoom.prototype.setMouseAnchor = function(useAnchor) {
this.useAnchor_ = useAnchor;
if (!useAnchor) {
this.lastAnchor_ = null;
}
};
export default MouseWheelZoom;

View File

@@ -77,61 +77,102 @@ const handleMoveEvent = UNDEFINED;
* @extends {module:ol/interaction/Interaction}
* @api
*/
const PointerInteraction = function(opt_options) {
class PointerInteraction {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
Interaction.call(this, {
handleEvent: options.handleEvent || handleEvent
});
Interaction.call(this, {
handleEvent: options.handleEvent || handleEvent
});
/**
* @type {function(module:ol/MapBrowserPointerEvent):boolean}
* @private
*/
this.handleDownEvent_ = options.handleDownEvent ?
options.handleDownEvent : handleDownEvent;
/**
* @type {function(module:ol/MapBrowserPointerEvent)}
* @private
*/
this.handleDragEvent_ = options.handleDragEvent ?
options.handleDragEvent : handleDragEvent;
/**
* @type {function(module:ol/MapBrowserPointerEvent)}
* @private
*/
this.handleMoveEvent_ = options.handleMoveEvent ?
options.handleMoveEvent : handleMoveEvent;
/**
* @type {function(module:ol/MapBrowserPointerEvent):boolean}
* @private
*/
this.handleUpEvent_ = options.handleUpEvent ?
options.handleUpEvent : handleUpEvent;
/**
* @type {boolean}
* @protected
*/
this.handlingDownUpSequence = false;
/**
* @type {!Object.<string, module:ol/pointer/PointerEvent>}
* @private
*/
this.trackedPointers_ = {};
/**
* @type {Array.<module:ol/pointer/PointerEvent>}
* @protected
*/
this.targetPointers = [];
}
/**
* @type {function(module:ol/MapBrowserPointerEvent):boolean}
* @param {module:ol/MapBrowserPointerEvent} mapBrowserEvent Event.
* @private
*/
this.handleDownEvent_ = options.handleDownEvent ?
options.handleDownEvent : handleDownEvent;
updateTrackedPointers_(mapBrowserEvent) {
if (isPointerDraggingEvent(mapBrowserEvent)) {
const event = mapBrowserEvent.pointerEvent;
const id = event.pointerId.toString();
if (mapBrowserEvent.type == MapBrowserEventType.POINTERUP) {
delete this.trackedPointers_[id];
} else if (mapBrowserEvent.type ==
MapBrowserEventType.POINTERDOWN) {
this.trackedPointers_[id] = event;
} else if (id in this.trackedPointers_) {
// update only when there was a pointerdown event for this pointer
this.trackedPointers_[id] = event;
}
this.targetPointers = getValues(this.trackedPointers_);
}
}
/**
* @type {function(module:ol/MapBrowserPointerEvent)}
* @private
*/
this.handleDragEvent_ = options.handleDragEvent ?
options.handleDragEvent : handleDragEvent;
/**
* @type {function(module:ol/MapBrowserPointerEvent)}
* @private
*/
this.handleMoveEvent_ = options.handleMoveEvent ?
options.handleMoveEvent : handleMoveEvent;
/**
* @type {function(module:ol/MapBrowserPointerEvent):boolean}
* @private
*/
this.handleUpEvent_ = options.handleUpEvent ?
options.handleUpEvent : handleUpEvent;
/**
* @type {boolean}
* This method is used to determine if "down" events should be propagated to
* other interactions or should be stopped.
*
* The method receives the return code of the "handleDownEvent" function.
*
* By default this function is the "identity" function. It's overridden in
* child classes.
*
* @param {boolean} handled Was the event handled by the interaction?
* @return {boolean} Should the event be stopped?
* @protected
*/
this.handlingDownUpSequence = false;
/**
* @type {!Object.<string, module:ol/pointer/PointerEvent>}
* @private
*/
this.trackedPointers_ = {};
/**
* @type {Array.<module:ol/pointer/PointerEvent>}
* @protected
*/
this.targetPointers = [];
};
shouldStopEvent(handled) {
return handled;
}
}
inherits(PointerInteraction, Interaction);
@@ -165,29 +206,6 @@ function isPointerDraggingEvent(mapBrowserEvent) {
}
/**
* @param {module:ol/MapBrowserPointerEvent} mapBrowserEvent Event.
* @private
*/
PointerInteraction.prototype.updateTrackedPointers_ = function(mapBrowserEvent) {
if (isPointerDraggingEvent(mapBrowserEvent)) {
const event = mapBrowserEvent.pointerEvent;
const id = event.pointerId.toString();
if (mapBrowserEvent.type == MapBrowserEventType.POINTERUP) {
delete this.trackedPointers_[id];
} else if (mapBrowserEvent.type ==
MapBrowserEventType.POINTERDOWN) {
this.trackedPointers_[id] = event;
} else if (id in this.trackedPointers_) {
// update only when there was a pointerdown event for this pointer
this.trackedPointers_[id] = event;
}
this.targetPointers = getValues(this.trackedPointers_);
}
};
/**
* Handles the {@link module:ol/MapBrowserEvent map browser event} and may call into
* other functions, if event sequences like e.g. 'drag' or 'down-up' etc. are
@@ -224,21 +242,4 @@ export function handleEvent(mapBrowserEvent) {
}
/**
* This method is used to determine if "down" events should be propagated to
* other interactions or should be stopped.
*
* The method receives the return code of the "handleDownEvent" function.
*
* By default this function is the "identity" function. It's overridden in
* child classes.
*
* @param {boolean} handled Was the event handled by the interaction?
* @return {boolean} Should the event be stopped?
* @protected
*/
PointerInteraction.prototype.shouldStopEvent = function(handled) {
return handled;
};
export default PointerInteraction;

View File

@@ -156,162 +156,223 @@ inherits(SelectEvent, Event);
* @fires SelectEvent
* @api
*/
const Select = function(opt_options) {
class Select {
constructor(opt_options) {
Interaction.call(this, {
handleEvent: handleEvent
});
Interaction.call(this, {
handleEvent: handleEvent
});
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
/**
* @private
* @type {module:ol/events/condition~Condition}
*/
this.condition_ = options.condition ? options.condition : singleClick;
/**
* @private
* @type {module:ol/events/condition~Condition}
*/
this.condition_ = options.condition ? options.condition : singleClick;
/**
* @private
* @type {module:ol/events/condition~Condition}
*/
this.addCondition_ = options.addCondition ? options.addCondition : never;
/**
* @private
* @type {module:ol/events/condition~Condition}
*/
this.addCondition_ = options.addCondition ? options.addCondition : never;
/**
* @private
* @type {module:ol/events/condition~Condition}
*/
this.removeCondition_ = options.removeCondition ? options.removeCondition : never;
/**
* @private
* @type {module:ol/events/condition~Condition}
*/
this.removeCondition_ = options.removeCondition ? options.removeCondition : never;
/**
* @private
* @type {module:ol/events/condition~Condition}
*/
this.toggleCondition_ = options.toggleCondition ? options.toggleCondition : shiftKeyOnly;
/**
* @private
* @type {module:ol/events/condition~Condition}
*/
this.toggleCondition_ = options.toggleCondition ? options.toggleCondition : shiftKeyOnly;
/**
* @private
* @type {boolean}
*/
this.multi_ = options.multi ? options.multi : false;
/**
* @private
* @type {boolean}
*/
this.multi_ = options.multi ? options.multi : false;
/**
* @private
* @type {module:ol/interaction/Select~FilterFunction}
*/
this.filter_ = options.filter ? options.filter : TRUE;
/**
* @private
* @type {module:ol/interaction/Select~FilterFunction}
*/
this.filter_ = options.filter ? options.filter : TRUE;
/**
* @private
* @type {number}
*/
this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0;
/**
* @private
* @type {number}
*/
this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0;
const featureOverlay = new VectorLayer({
source: new VectorSource({
useSpatialIndex: false,
features: options.features,
wrapX: options.wrapX
}),
style: options.style ? options.style :
getDefaultStyleFunction(),
updateWhileAnimating: true,
updateWhileInteracting: true
});
const featureOverlay = new VectorLayer({
source: new VectorSource({
useSpatialIndex: false,
features: options.features,
wrapX: options.wrapX
}),
style: options.style ? options.style :
getDefaultStyleFunction(),
updateWhileAnimating: true,
updateWhileInteracting: true
});
/**
* @private
* @type {module:ol/layer/Vector}
*/
this.featureOverlay_ = featureOverlay;
/**
* @private
* @type {module:ol/layer/Vector}
*/
this.featureOverlay_ = featureOverlay;
/** @type {function(module:ol/layer/Layer): boolean} */
let layerFilter;
if (options.layers) {
if (typeof options.layers === 'function') {
layerFilter = options.layers;
/** @type {function(module:ol/layer/Layer): boolean} */
let layerFilter;
if (options.layers) {
if (typeof options.layers === 'function') {
layerFilter = options.layers;
} else {
const layers = options.layers;
layerFilter = function(layer) {
return includes(layers, layer);
};
}
} else {
const layers = options.layers;
layerFilter = function(layer) {
return includes(layers, layer);
};
layerFilter = TRUE;
}
} else {
layerFilter = TRUE;
/**
* @private
* @type {function(module:ol/layer/Layer): boolean}
*/
this.layerFilter_ = layerFilter;
/**
* An association between selected feature (key)
* and layer (value)
* @private
* @type {Object.<number, module:ol/layer/Layer>}
*/
this.featureLayerAssociation_ = {};
const features = this.featureOverlay_.getSource().getFeaturesCollection();
listen(features, CollectionEventType.ADD,
this.addFeature_, this);
listen(features, CollectionEventType.REMOVE,
this.removeFeature_, this);
}
/**
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
* @param {module:ol/layer/Layer} layer Layer.
* @private
* @type {function(module:ol/layer/Layer): boolean}
*/
this.layerFilter_ = layerFilter;
addFeatureLayerAssociation_(feature, layer) {
const key = getUid(feature);
this.featureLayerAssociation_[key] = layer;
}
/**
* An association between selected feature (key)
* and layer (value)
* @private
* @type {Object.<number, module:ol/layer/Layer>}
* Get the selected features.
* @return {module:ol/Collection.<module:ol/Feature>} Features collection.
* @api
*/
this.featureLayerAssociation_ = {};
getFeatures() {
return this.featureOverlay_.getSource().getFeaturesCollection();
}
const features = this.featureOverlay_.getSource().getFeaturesCollection();
listen(features, CollectionEventType.ADD,
this.addFeature_, this);
listen(features, CollectionEventType.REMOVE,
this.removeFeature_, this);
/**
* Returns the Hit-detection tolerance.
* @returns {number} Hit tolerance in pixels.
* @api
*/
getHitTolerance() {
return this.hitTolerance_;
}
};
/**
* Returns the associated {@link module:ol/layer/Vector~Vector vectorlayer} of
* the (last) selected feature. Note that this will not work with any
* programmatic method like pushing features to
* {@link module:ol/interaction/Select~Select#getFeatures collection}.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature
* @return {module:ol/layer/Vector} Layer.
* @api
*/
getLayer(feature) {
const key = getUid(feature);
return (
/** @type {module:ol/layer/Vector} */ (this.featureLayerAssociation_[key])
);
}
/**
* Hit-detection tolerance. Pixels inside the radius around the given position
* will be checked for features. This only works for the canvas renderer and
* not for WebGL.
* @param {number} hitTolerance Hit tolerance in pixels.
* @api
*/
setHitTolerance(hitTolerance) {
this.hitTolerance_ = hitTolerance;
}
/**
* Remove the interaction from its current map, if any, and attach it to a new
* map, if any. Pass `null` to just remove the interaction from the current map.
* @param {module:ol/PluggableMap} map Map.
* @override
* @api
*/
setMap(map) {
const currentMap = this.getMap();
const selectedFeatures =
this.featureOverlay_.getSource().getFeaturesCollection();
if (currentMap) {
selectedFeatures.forEach(currentMap.unskipFeature.bind(currentMap));
}
Interaction.prototype.setMap.call(this, map);
this.featureOverlay_.setMap(map);
if (map) {
selectedFeatures.forEach(map.skipFeature.bind(map));
}
}
/**
* @param {module:ol/Collection~CollectionEvent} evt Event.
* @private
*/
addFeature_(evt) {
const map = this.getMap();
if (map) {
map.skipFeature(/** @type {module:ol/Feature} */ (evt.element));
}
}
/**
* @param {module:ol/Collection~CollectionEvent} evt Event.
* @private
*/
removeFeature_(evt) {
const map = this.getMap();
if (map) {
map.unskipFeature(/** @type {module:ol/Feature} */ (evt.element));
}
}
/**
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
* @private
*/
removeFeatureLayerAssociation_(feature) {
const key = getUid(feature);
delete this.featureLayerAssociation_[key];
}
}
inherits(Select, Interaction);
/**
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
* @param {module:ol/layer/Layer} layer Layer.
* @private
*/
Select.prototype.addFeatureLayerAssociation_ = function(feature, layer) {
const key = getUid(feature);
this.featureLayerAssociation_[key] = layer;
};
/**
* Get the selected features.
* @return {module:ol/Collection.<module:ol/Feature>} Features collection.
* @api
*/
Select.prototype.getFeatures = function() {
return this.featureOverlay_.getSource().getFeaturesCollection();
};
/**
* Returns the Hit-detection tolerance.
* @returns {number} Hit tolerance in pixels.
* @api
*/
Select.prototype.getHitTolerance = function() {
return this.hitTolerance_;
};
/**
* Returns the associated {@link module:ol/layer/Vector~Vector vectorlayer} of
* the (last) selected feature. Note that this will not work with any
* programmatic method like pushing features to
* {@link module:ol/interaction/Select~Select#getFeatures collection}.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature
* @return {module:ol/layer/Vector} Layer.
* @api
*/
Select.prototype.getLayer = function(feature) {
const key = getUid(feature);
return (
/** @type {module:ol/layer/Vector} */ (this.featureLayerAssociation_[key])
);
};
/**
* Handles the {@link module:ol/MapBrowserEvent map browser event} and may change the
* selected state of features.
@@ -405,40 +466,6 @@ function handleEvent(mapBrowserEvent) {
}
/**
* Hit-detection tolerance. Pixels inside the radius around the given position
* will be checked for features. This only works for the canvas renderer and
* not for WebGL.
* @param {number} hitTolerance Hit tolerance in pixels.
* @api
*/
Select.prototype.setHitTolerance = function(hitTolerance) {
this.hitTolerance_ = hitTolerance;
};
/**
* Remove the interaction from its current map, if any, and attach it to a new
* map, if any. Pass `null` to just remove the interaction from the current map.
* @param {module:ol/PluggableMap} map Map.
* @override
* @api
*/
Select.prototype.setMap = function(map) {
const currentMap = this.getMap();
const selectedFeatures =
this.featureOverlay_.getSource().getFeaturesCollection();
if (currentMap) {
selectedFeatures.forEach(currentMap.unskipFeature.bind(currentMap));
}
Interaction.prototype.setMap.call(this, map);
this.featureOverlay_.setMap(map);
if (map) {
selectedFeatures.forEach(map.skipFeature.bind(map));
}
};
/**
* @return {module:ol/style/Style~StyleFunction} Styles.
*/
@@ -456,38 +483,4 @@ function getDefaultStyleFunction() {
}
/**
* @param {module:ol/Collection~CollectionEvent} evt Event.
* @private
*/
Select.prototype.addFeature_ = function(evt) {
const map = this.getMap();
if (map) {
map.skipFeature(/** @type {module:ol/Feature} */ (evt.element));
}
};
/**
* @param {module:ol/Collection~CollectionEvent} evt Event.
* @private
*/
Select.prototype.removeFeature_ = function(evt) {
const map = this.getMap();
if (map) {
map.unskipFeature(/** @type {module:ol/Feature} */ (evt.element));
}
};
/**
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
* @private
*/
Select.prototype.removeFeatureLayerAssociation_ = function(feature) {
const key = getUid(feature);
delete this.featureLayerAssociation_[key];
};
export default Select;

File diff suppressed because it is too large Load Diff

View File

@@ -96,68 +96,143 @@ inherits(TranslateEvent, Event);
* @param {module:ol/interaction/Translate~Options=} opt_options Options.
* @api
*/
const Translate = function(opt_options) {
PointerInteraction.call(this, {
handleDownEvent: handleDownEvent,
handleDragEvent: handleDragEvent,
handleMoveEvent: handleMoveEvent,
handleUpEvent: handleUpEvent
});
class Translate {
constructor(opt_options) {
PointerInteraction.call(this, {
handleDownEvent: handleDownEvent,
handleDragEvent: handleDragEvent,
handleMoveEvent: handleMoveEvent,
handleUpEvent: handleUpEvent
});
const options = opt_options ? opt_options : {};
const options = opt_options ? opt_options : {};
/**
* The last position we translated to.
* @type {module:ol/coordinate~Coordinate}
* @private
*/
this.lastCoordinate_ = null;
/**
* The last position we translated to.
* @type {module:ol/coordinate~Coordinate}
* @private
*/
this.lastCoordinate_ = null;
/**
* @type {module:ol/Collection.<module:ol/Feature>}
* @private
*/
this.features_ = options.features !== undefined ? options.features : null;
/**
* @type {module:ol/Collection.<module:ol/Feature>}
* @private
*/
this.features_ = options.features !== undefined ? options.features : null;
/** @type {function(module:ol/layer/Layer): boolean} */
let layerFilter;
if (options.layers) {
if (typeof options.layers === 'function') {
layerFilter = options.layers;
/** @type {function(module:ol/layer/Layer): boolean} */
let layerFilter;
if (options.layers) {
if (typeof options.layers === 'function') {
layerFilter = options.layers;
} else {
const layers = options.layers;
layerFilter = function(layer) {
return includes(layers, layer);
};
}
} else {
const layers = options.layers;
layerFilter = function(layer) {
return includes(layers, layer);
};
layerFilter = TRUE;
}
} else {
layerFilter = TRUE;
/**
* @private
* @type {function(module:ol/layer/Layer): boolean}
*/
this.layerFilter_ = layerFilter;
/**
* @private
* @type {number}
*/
this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0;
/**
* @type {module:ol/Feature}
* @private
*/
this.lastFeature_ = null;
listen(this,
getChangeEventType(InteractionProperty.ACTIVE),
this.handleActiveChanged_, this);
}
/**
* Tests to see if the given coordinates intersects any of our selected
* features.
* @param {module:ol~Pixel} pixel Pixel coordinate to test for intersection.
* @param {module:ol/PluggableMap} map Map to test the intersection on.
* @return {module:ol/Feature} Returns the feature found at the specified pixel
* coordinates.
* @private
*/
featuresAtPixel_(pixel, map) {
return map.forEachFeatureAtPixel(pixel,
function(feature) {
if (!this.features_ || includes(this.features_.getArray(), feature)) {
return feature;
}
}.bind(this), {
layerFilter: this.layerFilter_,
hitTolerance: this.hitTolerance_
});
}
/**
* Returns the Hit-detection tolerance.
* @returns {number} Hit tolerance in pixels.
* @api
*/
getHitTolerance() {
return this.hitTolerance_;
}
/**
* Hit-detection tolerance. Pixels inside the radius around the given position
* will be checked for features. This only works for the canvas renderer and
* not for WebGL.
* @param {number} hitTolerance Hit tolerance in pixels.
* @api
*/
setHitTolerance(hitTolerance) {
this.hitTolerance_ = hitTolerance;
}
/**
* @inheritDoc
*/
setMap(map) {
const oldMap = this.getMap();
PointerInteraction.prototype.setMap.call(this, map);
this.updateState_(oldMap);
}
/**
* @private
* @type {function(module:ol/layer/Layer): boolean}
*/
this.layerFilter_ = layerFilter;
handleActiveChanged_() {
this.updateState_(null);
}
/**
* @private
* @type {number}
*/
this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0;
/**
* @type {module:ol/Feature}
* @param {module:ol/PluggableMap} oldMap Old map.
* @private
*/
this.lastFeature_ = null;
listen(this,
getChangeEventType(InteractionProperty.ACTIVE),
this.handleActiveChanged_, this);
};
updateState_(oldMap) {
let map = this.getMap();
const active = this.getActive();
if (!map || !active) {
map = map || oldMap;
if (map) {
const elem = map.getViewport();
elem.classList.remove('ol-grab', 'ol-grabbing');
}
}
}
}
inherits(Translate, PointerInteraction);
@@ -252,83 +327,4 @@ function handleMoveEvent(event) {
}
/**
* Tests to see if the given coordinates intersects any of our selected
* features.
* @param {module:ol~Pixel} pixel Pixel coordinate to test for intersection.
* @param {module:ol/PluggableMap} map Map to test the intersection on.
* @return {module:ol/Feature} Returns the feature found at the specified pixel
* coordinates.
* @private
*/
Translate.prototype.featuresAtPixel_ = function(pixel, map) {
return map.forEachFeatureAtPixel(pixel,
function(feature) {
if (!this.features_ || includes(this.features_.getArray(), feature)) {
return feature;
}
}.bind(this), {
layerFilter: this.layerFilter_,
hitTolerance: this.hitTolerance_
});
};
/**
* Returns the Hit-detection tolerance.
* @returns {number} Hit tolerance in pixels.
* @api
*/
Translate.prototype.getHitTolerance = function() {
return this.hitTolerance_;
};
/**
* Hit-detection tolerance. Pixels inside the radius around the given position
* will be checked for features. This only works for the canvas renderer and
* not for WebGL.
* @param {number} hitTolerance Hit tolerance in pixels.
* @api
*/
Translate.prototype.setHitTolerance = function(hitTolerance) {
this.hitTolerance_ = hitTolerance;
};
/**
* @inheritDoc
*/
Translate.prototype.setMap = function(map) {
const oldMap = this.getMap();
PointerInteraction.prototype.setMap.call(this, map);
this.updateState_(oldMap);
};
/**
* @private
*/
Translate.prototype.handleActiveChanged_ = function() {
this.updateState_(null);
};
/**
* @param {module:ol/PluggableMap} oldMap Old map.
* @private
*/
Translate.prototype.updateState_ = function(oldMap) {
let map = this.getMap();
const active = this.getActive();
if (!map || !active) {
map = map || oldMap;
if (map) {
const elem = map.getViewport();
elem.classList.remove('ol-grab', 'ol-grabbing');
}
}
};
export default Translate;

View File

@@ -37,232 +37,219 @@ import {assign} from '../obj.js';
* @param {module:ol/layer/Base~Options} options Layer options.
* @api
*/
const BaseLayer = function(options) {
class BaseLayer {
constructor(options) {
BaseObject.call(this);
BaseObject.call(this);
/**
* @type {Object.<string, *>}
*/
const properties = assign({}, options);
properties[LayerProperty.OPACITY] =
options.opacity !== undefined ? options.opacity : 1;
properties[LayerProperty.VISIBLE] =
options.visible !== undefined ? options.visible : true;
properties[LayerProperty.Z_INDEX] =
options.zIndex !== undefined ? options.zIndex : 0;
properties[LayerProperty.MAX_RESOLUTION] =
options.maxResolution !== undefined ? options.maxResolution : Infinity;
properties[LayerProperty.MIN_RESOLUTION] =
options.minResolution !== undefined ? options.minResolution : 0;
/**
* @type {Object.<string, *>}
*/
const properties = assign({}, options);
properties[LayerProperty.OPACITY] =
options.opacity !== undefined ? options.opacity : 1;
properties[LayerProperty.VISIBLE] =
options.visible !== undefined ? options.visible : true;
properties[LayerProperty.Z_INDEX] =
options.zIndex !== undefined ? options.zIndex : 0;
properties[LayerProperty.MAX_RESOLUTION] =
options.maxResolution !== undefined ? options.maxResolution : Infinity;
properties[LayerProperty.MIN_RESOLUTION] =
options.minResolution !== undefined ? options.minResolution : 0;
this.setProperties(properties);
this.setProperties(properties);
/**
* @type {module:ol/layer/Layer~State}
* @private
*/
this.state_ = /** @type {module:ol/layer/Layer~State} */ ({
layer: /** @type {module:ol/layer/Layer} */ (this),
managed: true
});
/**
* @type {module:ol/layer/Layer~State}
* @private
*/
this.state_ = /** @type {module:ol/layer/Layer~State} */ ({
layer: /** @type {module:ol/layer/Layer} */ (this),
managed: true
});
/**
* The layer type.
* @type {module:ol/LayerType}
* @protected;
*/
this.type;
/**
* The layer type.
* @type {module:ol/LayerType}
* @protected;
*/
this.type;
};
}
/**
* Get the layer type (used when creating a layer renderer).
* @return {module:ol/LayerType} The layer type.
*/
getType() {
return this.type;
}
/**
* @return {module:ol/layer/Layer~State} Layer state.
*/
getLayerState() {
this.state_.opacity = clamp(this.getOpacity(), 0, 1);
this.state_.sourceState = this.getSourceState();
this.state_.visible = this.getVisible();
this.state_.extent = this.getExtent();
this.state_.zIndex = this.getZIndex();
this.state_.maxResolution = this.getMaxResolution();
this.state_.minResolution = Math.max(this.getMinResolution(), 0);
return this.state_;
}
/**
* @abstract
* @param {Array.<module:ol/layer/Layer>=} opt_array Array of layers (to be
* modified in place).
* @return {Array.<module:ol/layer/Layer>} Array of layers.
*/
getLayersArray(opt_array) {}
/**
* @abstract
* @param {Array.<module:ol/layer/Layer~State>=} opt_states Optional list of layer
* states (to be modified in place).
* @return {Array.<module:ol/layer/Layer~State>} List of layer states.
*/
getLayerStatesArray(opt_states) {}
/**
* Return the {@link module:ol/extent~Extent extent} of the layer or `undefined` if it
* will be visible regardless of extent.
* @return {module:ol/extent~Extent|undefined} The layer extent.
* @observable
* @api
*/
getExtent() {
return (
/** @type {module:ol/extent~Extent|undefined} */ (this.get(LayerProperty.EXTENT))
);
}
/**
* Return the maximum resolution of the layer.
* @return {number} The maximum resolution of the layer.
* @observable
* @api
*/
getMaxResolution() {
return /** @type {number} */ (this.get(LayerProperty.MAX_RESOLUTION));
}
/**
* Return the minimum resolution of the layer.
* @return {number} The minimum resolution of the layer.
* @observable
* @api
*/
getMinResolution() {
return /** @type {number} */ (this.get(LayerProperty.MIN_RESOLUTION));
}
/**
* Return the opacity of the layer (between 0 and 1).
* @return {number} The opacity of the layer.
* @observable
* @api
*/
getOpacity() {
return /** @type {number} */ (this.get(LayerProperty.OPACITY));
}
/**
* @abstract
* @return {module:ol/source/State} Source state.
*/
getSourceState() {}
/**
* Return the visibility of the layer (`true` or `false`).
* @return {boolean} The visibility of the layer.
* @observable
* @api
*/
getVisible() {
return /** @type {boolean} */ (this.get(LayerProperty.VISIBLE));
}
/**
* Return the Z-index of the layer, which is used to order layers before
* rendering. The default Z-index is 0.
* @return {number} The Z-index of the layer.
* @observable
* @api
*/
getZIndex() {
return /** @type {number} */ (this.get(LayerProperty.Z_INDEX));
}
/**
* Set the extent at which the layer is visible. If `undefined`, the layer
* will be visible at all extents.
* @param {module:ol/extent~Extent|undefined} extent The extent of the layer.
* @observable
* @api
*/
setExtent(extent) {
this.set(LayerProperty.EXTENT, extent);
}
/**
* Set the maximum resolution at which the layer is visible.
* @param {number} maxResolution The maximum resolution of the layer.
* @observable
* @api
*/
setMaxResolution(maxResolution) {
this.set(LayerProperty.MAX_RESOLUTION, maxResolution);
}
/**
* Set the minimum resolution at which the layer is visible.
* @param {number} minResolution The minimum resolution of the layer.
* @observable
* @api
*/
setMinResolution(minResolution) {
this.set(LayerProperty.MIN_RESOLUTION, minResolution);
}
/**
* Set the opacity of the layer, allowed values range from 0 to 1.
* @param {number} opacity The opacity of the layer.
* @observable
* @api
*/
setOpacity(opacity) {
this.set(LayerProperty.OPACITY, opacity);
}
/**
* Set the visibility of the layer (`true` or `false`).
* @param {boolean} visible The visibility of the layer.
* @observable
* @api
*/
setVisible(visible) {
this.set(LayerProperty.VISIBLE, visible);
}
/**
* Set Z-index of the layer, which is used to order layers before rendering.
* The default Z-index is 0.
* @param {number} zindex The z-index of the layer.
* @observable
* @api
*/
setZIndex(zindex) {
this.set(LayerProperty.Z_INDEX, zindex);
}
}
inherits(BaseLayer, BaseObject);
/**
* Get the layer type (used when creating a layer renderer).
* @return {module:ol/LayerType} The layer type.
*/
BaseLayer.prototype.getType = function() {
return this.type;
};
/**
* @return {module:ol/layer/Layer~State} Layer state.
*/
BaseLayer.prototype.getLayerState = function() {
this.state_.opacity = clamp(this.getOpacity(), 0, 1);
this.state_.sourceState = this.getSourceState();
this.state_.visible = this.getVisible();
this.state_.extent = this.getExtent();
this.state_.zIndex = this.getZIndex();
this.state_.maxResolution = this.getMaxResolution();
this.state_.minResolution = Math.max(this.getMinResolution(), 0);
return this.state_;
};
/**
* @abstract
* @param {Array.<module:ol/layer/Layer>=} opt_array Array of layers (to be
* modified in place).
* @return {Array.<module:ol/layer/Layer>} Array of layers.
*/
BaseLayer.prototype.getLayersArray = function(opt_array) {};
/**
* @abstract
* @param {Array.<module:ol/layer/Layer~State>=} opt_states Optional list of layer
* states (to be modified in place).
* @return {Array.<module:ol/layer/Layer~State>} List of layer states.
*/
BaseLayer.prototype.getLayerStatesArray = function(opt_states) {};
/**
* Return the {@link module:ol/extent~Extent extent} of the layer or `undefined` if it
* will be visible regardless of extent.
* @return {module:ol/extent~Extent|undefined} The layer extent.
* @observable
* @api
*/
BaseLayer.prototype.getExtent = function() {
return (
/** @type {module:ol/extent~Extent|undefined} */ (this.get(LayerProperty.EXTENT))
);
};
/**
* Return the maximum resolution of the layer.
* @return {number} The maximum resolution of the layer.
* @observable
* @api
*/
BaseLayer.prototype.getMaxResolution = function() {
return /** @type {number} */ (this.get(LayerProperty.MAX_RESOLUTION));
};
/**
* Return the minimum resolution of the layer.
* @return {number} The minimum resolution of the layer.
* @observable
* @api
*/
BaseLayer.prototype.getMinResolution = function() {
return /** @type {number} */ (this.get(LayerProperty.MIN_RESOLUTION));
};
/**
* Return the opacity of the layer (between 0 and 1).
* @return {number} The opacity of the layer.
* @observable
* @api
*/
BaseLayer.prototype.getOpacity = function() {
return /** @type {number} */ (this.get(LayerProperty.OPACITY));
};
/**
* @abstract
* @return {module:ol/source/State} Source state.
*/
BaseLayer.prototype.getSourceState = function() {};
/**
* Return the visibility of the layer (`true` or `false`).
* @return {boolean} The visibility of the layer.
* @observable
* @api
*/
BaseLayer.prototype.getVisible = function() {
return /** @type {boolean} */ (this.get(LayerProperty.VISIBLE));
};
/**
* Return the Z-index of the layer, which is used to order layers before
* rendering. The default Z-index is 0.
* @return {number} The Z-index of the layer.
* @observable
* @api
*/
BaseLayer.prototype.getZIndex = function() {
return /** @type {number} */ (this.get(LayerProperty.Z_INDEX));
};
/**
* Set the extent at which the layer is visible. If `undefined`, the layer
* will be visible at all extents.
* @param {module:ol/extent~Extent|undefined} extent The extent of the layer.
* @observable
* @api
*/
BaseLayer.prototype.setExtent = function(extent) {
this.set(LayerProperty.EXTENT, extent);
};
/**
* Set the maximum resolution at which the layer is visible.
* @param {number} maxResolution The maximum resolution of the layer.
* @observable
* @api
*/
BaseLayer.prototype.setMaxResolution = function(maxResolution) {
this.set(LayerProperty.MAX_RESOLUTION, maxResolution);
};
/**
* Set the minimum resolution at which the layer is visible.
* @param {number} minResolution The minimum resolution of the layer.
* @observable
* @api
*/
BaseLayer.prototype.setMinResolution = function(minResolution) {
this.set(LayerProperty.MIN_RESOLUTION, minResolution);
};
/**
* Set the opacity of the layer, allowed values range from 0 to 1.
* @param {number} opacity The opacity of the layer.
* @observable
* @api
*/
BaseLayer.prototype.setOpacity = function(opacity) {
this.set(LayerProperty.OPACITY, opacity);
};
/**
* Set the visibility of the layer (`true` or `false`).
* @param {boolean} visible The visibility of the layer.
* @observable
* @api
*/
BaseLayer.prototype.setVisible = function(visible) {
this.set(LayerProperty.VISIBLE, visible);
};
/**
* Set Z-index of the layer, which is used to order layers before rendering.
* The default Z-index is 0.
* @param {number} zindex The z-index of the layer.
* @observable
* @api
*/
BaseLayer.prototype.setZIndex = function(zindex) {
this.set(LayerProperty.Z_INDEX, zindex);
};
export default BaseLayer;

View File

@@ -51,198 +51,192 @@ const Property = {
* @param {module:ol/layer/Group~Options=} opt_options Layer options.
* @api
*/
const LayerGroup = function(opt_options) {
class LayerGroup {
constructor(opt_options) {
const options = opt_options || {};
const baseOptions = /** @type {module:ol/layer/Group~Options} */ (assign({}, options));
delete baseOptions.layers;
const options = opt_options || {};
const baseOptions = /** @type {module:ol/layer/Group~Options} */ (assign({}, options));
delete baseOptions.layers;
let layers = options.layers;
let layers = options.layers;
BaseLayer.call(this, baseOptions);
BaseLayer.call(this, baseOptions);
/**
* @private
* @type {Array.<module:ol/events~EventsKey>}
*/
this.layersListenerKeys_ = [];
/**
* @private
* @type {Array.<module:ol/events~EventsKey>}
*/
this.layersListenerKeys_ = [];
/**
* @private
* @type {Object.<string, Array.<module:ol/events~EventsKey>>}
*/
this.listenerKeys_ = {};
/**
* @private
* @type {Object.<string, Array.<module:ol/events~EventsKey>>}
*/
this.listenerKeys_ = {};
listen(this,
getChangeEventType(Property.LAYERS),
this.handleLayersChanged_, this);
listen(this,
getChangeEventType(Property.LAYERS),
this.handleLayersChanged_, this);
if (layers) {
if (Array.isArray(layers)) {
layers = new Collection(layers.slice(), {unique: true});
if (layers) {
if (Array.isArray(layers)) {
layers = new Collection(layers.slice(), {unique: true});
} else {
assert(layers instanceof Collection,
43); // Expected `layers` to be an array or a `Collection`
layers = layers;
}
} else {
assert(layers instanceof Collection,
43); // Expected `layers` to be an array or a `Collection`
layers = layers;
layers = new Collection(undefined, {unique: true});
}
} else {
layers = new Collection(undefined, {unique: true});
this.setLayers(layers);
}
this.setLayers(layers);
/**
* @private
*/
handleLayerChange_() {
this.changed();
}
};
/**
* @param {module:ol/events/Event} event Event.
* @private
*/
handleLayersChanged_(event) {
this.layersListenerKeys_.forEach(unlistenByKey);
this.layersListenerKeys_.length = 0;
const layers = this.getLayers();
this.layersListenerKeys_.push(
listen(layers, CollectionEventType.ADD, this.handleLayersAdd_, this),
listen(layers, CollectionEventType.REMOVE, this.handleLayersRemove_, this)
);
for (const id in this.listenerKeys_) {
this.listenerKeys_[id].forEach(unlistenByKey);
}
clear(this.listenerKeys_);
const layersArray = layers.getArray();
for (let i = 0, ii = layersArray.length; i < ii; i++) {
const layer = layersArray[i];
this.listenerKeys_[getUid(layer).toString()] = [
listen(layer, ObjectEventType.PROPERTYCHANGE, this.handleLayerChange_, this),
listen(layer, EventType.CHANGE, this.handleLayerChange_, this)
];
}
this.changed();
}
/**
* @param {module:ol/Collection~CollectionEvent} collectionEvent CollectionEvent.
* @private
*/
handleLayersAdd_(collectionEvent) {
const layer = /** @type {module:ol/layer/Base} */ (collectionEvent.element);
const key = getUid(layer).toString();
this.listenerKeys_[key] = [
listen(layer, ObjectEventType.PROPERTYCHANGE, this.handleLayerChange_, this),
listen(layer, EventType.CHANGE, this.handleLayerChange_, this)
];
this.changed();
}
/**
* @param {module:ol/Collection~CollectionEvent} collectionEvent CollectionEvent.
* @private
*/
handleLayersRemove_(collectionEvent) {
const layer = /** @type {module:ol/layer/Base} */ (collectionEvent.element);
const key = getUid(layer).toString();
this.listenerKeys_[key].forEach(unlistenByKey);
delete this.listenerKeys_[key];
this.changed();
}
/**
* Returns the {@link module:ol/Collection collection} of {@link module:ol/layer/Layer~Layer layers}
* in this group.
* @return {!module:ol/Collection.<module:ol/layer/Base>} Collection of
* {@link module:ol/layer/Base layers} that are part of this group.
* @observable
* @api
*/
getLayers() {
return (
/** @type {!module:ol/Collection.<module:ol/layer/Base>} */ (this.get(Property.LAYERS))
);
}
/**
* Set the {@link module:ol/Collection collection} of {@link module:ol/layer/Layer~Layer layers}
* in this group.
* @param {!module:ol/Collection.<module:ol/layer/Base>} layers Collection of
* {@link module:ol/layer/Base layers} that are part of this group.
* @observable
* @api
*/
setLayers(layers) {
this.set(Property.LAYERS, layers);
}
/**
* @inheritDoc
*/
getLayersArray(opt_array) {
const array = opt_array !== undefined ? opt_array : [];
this.getLayers().forEach(function(layer) {
layer.getLayersArray(array);
});
return array;
}
/**
* @inheritDoc
*/
getLayerStatesArray(opt_states) {
const states = opt_states !== undefined ? opt_states : [];
const pos = states.length;
this.getLayers().forEach(function(layer) {
layer.getLayerStatesArray(states);
});
const ownLayerState = this.getLayerState();
for (let i = pos, ii = states.length; i < ii; i++) {
const layerState = states[i];
layerState.opacity *= ownLayerState.opacity;
layerState.visible = layerState.visible && ownLayerState.visible;
layerState.maxResolution = Math.min(
layerState.maxResolution, ownLayerState.maxResolution);
layerState.minResolution = Math.max(
layerState.minResolution, ownLayerState.minResolution);
if (ownLayerState.extent !== undefined) {
if (layerState.extent !== undefined) {
layerState.extent = getIntersection(layerState.extent, ownLayerState.extent);
} else {
layerState.extent = ownLayerState.extent;
}
}
}
return states;
}
/**
* @inheritDoc
*/
getSourceState() {
return SourceState.READY;
}
}
inherits(LayerGroup, BaseLayer);
/**
* @private
*/
LayerGroup.prototype.handleLayerChange_ = function() {
this.changed();
};
/**
* @param {module:ol/events/Event} event Event.
* @private
*/
LayerGroup.prototype.handleLayersChanged_ = function(event) {
this.layersListenerKeys_.forEach(unlistenByKey);
this.layersListenerKeys_.length = 0;
const layers = this.getLayers();
this.layersListenerKeys_.push(
listen(layers, CollectionEventType.ADD, this.handleLayersAdd_, this),
listen(layers, CollectionEventType.REMOVE, this.handleLayersRemove_, this)
);
for (const id in this.listenerKeys_) {
this.listenerKeys_[id].forEach(unlistenByKey);
}
clear(this.listenerKeys_);
const layersArray = layers.getArray();
for (let i = 0, ii = layersArray.length; i < ii; i++) {
const layer = layersArray[i];
this.listenerKeys_[getUid(layer).toString()] = [
listen(layer, ObjectEventType.PROPERTYCHANGE, this.handleLayerChange_, this),
listen(layer, EventType.CHANGE, this.handleLayerChange_, this)
];
}
this.changed();
};
/**
* @param {module:ol/Collection~CollectionEvent} collectionEvent CollectionEvent.
* @private
*/
LayerGroup.prototype.handleLayersAdd_ = function(collectionEvent) {
const layer = /** @type {module:ol/layer/Base} */ (collectionEvent.element);
const key = getUid(layer).toString();
this.listenerKeys_[key] = [
listen(layer, ObjectEventType.PROPERTYCHANGE, this.handleLayerChange_, this),
listen(layer, EventType.CHANGE, this.handleLayerChange_, this)
];
this.changed();
};
/**
* @param {module:ol/Collection~CollectionEvent} collectionEvent CollectionEvent.
* @private
*/
LayerGroup.prototype.handleLayersRemove_ = function(collectionEvent) {
const layer = /** @type {module:ol/layer/Base} */ (collectionEvent.element);
const key = getUid(layer).toString();
this.listenerKeys_[key].forEach(unlistenByKey);
delete this.listenerKeys_[key];
this.changed();
};
/**
* Returns the {@link module:ol/Collection collection} of {@link module:ol/layer/Layer~Layer layers}
* in this group.
* @return {!module:ol/Collection.<module:ol/layer/Base>} Collection of
* {@link module:ol/layer/Base layers} that are part of this group.
* @observable
* @api
*/
LayerGroup.prototype.getLayers = function() {
return (
/** @type {!module:ol/Collection.<module:ol/layer/Base>} */ (this.get(Property.LAYERS))
);
};
/**
* Set the {@link module:ol/Collection collection} of {@link module:ol/layer/Layer~Layer layers}
* in this group.
* @param {!module:ol/Collection.<module:ol/layer/Base>} layers Collection of
* {@link module:ol/layer/Base layers} that are part of this group.
* @observable
* @api
*/
LayerGroup.prototype.setLayers = function(layers) {
this.set(Property.LAYERS, layers);
};
/**
* @inheritDoc
*/
LayerGroup.prototype.getLayersArray = function(opt_array) {
const array = opt_array !== undefined ? opt_array : [];
this.getLayers().forEach(function(layer) {
layer.getLayersArray(array);
});
return array;
};
/**
* @inheritDoc
*/
LayerGroup.prototype.getLayerStatesArray = function(opt_states) {
const states = opt_states !== undefined ? opt_states : [];
const pos = states.length;
this.getLayers().forEach(function(layer) {
layer.getLayerStatesArray(states);
});
const ownLayerState = this.getLayerState();
for (let i = pos, ii = states.length; i < ii; i++) {
const layerState = states[i];
layerState.opacity *= ownLayerState.opacity;
layerState.visible = layerState.visible && ownLayerState.visible;
layerState.maxResolution = Math.min(
layerState.maxResolution, ownLayerState.maxResolution);
layerState.minResolution = Math.max(
layerState.minResolution, ownLayerState.minResolution);
if (ownLayerState.extent !== undefined) {
if (layerState.extent !== undefined) {
layerState.extent = getIntersection(layerState.extent, ownLayerState.extent);
} else {
layerState.extent = ownLayerState.extent;
}
}
}
return states;
};
/**
* @inheritDoc
*/
LayerGroup.prototype.getSourceState = function() {
return SourceState.READY;
};
export default LayerGroup;

View File

@@ -73,97 +73,215 @@ const DEFAULT_GRADIENT = ['#00f', '#0ff', '#0f0', '#ff0', '#f00'];
* @param {module:ol/layer/Heatmap~Options=} opt_options Options.
* @api
*/
const Heatmap = function(opt_options) {
const options = opt_options ? opt_options : {};
class Heatmap {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const baseOptions = assign({}, options);
const baseOptions = assign({}, options);
delete baseOptions.gradient;
delete baseOptions.radius;
delete baseOptions.blur;
delete baseOptions.shadow;
delete baseOptions.weight;
VectorLayer.call(this, /** @type {module:ol/layer/Vector~Options} */ (baseOptions));
delete baseOptions.gradient;
delete baseOptions.radius;
delete baseOptions.blur;
delete baseOptions.shadow;
delete baseOptions.weight;
VectorLayer.call(this, /** @type {module:ol/layer/Vector~Options} */ (baseOptions));
/**
* @private
* @type {Uint8ClampedArray}
*/
this.gradient_ = null;
/**
* @private
* @type {Uint8ClampedArray}
*/
this.gradient_ = null;
/**
* @private
* @type {number}
*/
this.shadow_ = options.shadow !== undefined ? options.shadow : 250;
/**
* @private
* @type {number}
*/
this.shadow_ = options.shadow !== undefined ? options.shadow : 250;
/**
* @private
* @type {string|undefined}
*/
this.circleImage_ = undefined;
/**
* @private
* @type {string|undefined}
*/
this.circleImage_ = undefined;
/**
* @private
* @type {Array.<Array.<module:ol/style/Style>>}
*/
this.styleCache_ = null;
/**
* @private
* @type {Array.<Array.<module:ol/style/Style>>}
*/
this.styleCache_ = null;
listen(this,
getChangeEventType(Property.GRADIENT),
this.handleGradientChanged_, this);
listen(this,
getChangeEventType(Property.GRADIENT),
this.handleGradientChanged_, this);
this.setGradient(options.gradient ? options.gradient : DEFAULT_GRADIENT);
this.setGradient(options.gradient ? options.gradient : DEFAULT_GRADIENT);
this.setBlur(options.blur !== undefined ? options.blur : 15);
this.setBlur(options.blur !== undefined ? options.blur : 15);
this.setRadius(options.radius !== undefined ? options.radius : 8);
this.setRadius(options.radius !== undefined ? options.radius : 8);
listen(this,
getChangeEventType(Property.BLUR),
this.handleStyleChanged_, this);
listen(this,
getChangeEventType(Property.RADIUS),
this.handleStyleChanged_, this);
listen(this,
getChangeEventType(Property.BLUR),
this.handleStyleChanged_, this);
listen(this,
getChangeEventType(Property.RADIUS),
this.handleStyleChanged_, this);
this.handleStyleChanged_();
this.handleStyleChanged_();
const weight = options.weight ? options.weight : 'weight';
let weightFunction;
if (typeof weight === 'string') {
weightFunction = function(feature) {
return feature.get(weight);
};
} else {
weightFunction = weight;
const weight = options.weight ? options.weight : 'weight';
let weightFunction;
if (typeof weight === 'string') {
weightFunction = function(feature) {
return feature.get(weight);
};
} else {
weightFunction = weight;
}
this.setStyle(function(feature, resolution) {
const weight = weightFunction(feature);
const opacity = weight !== undefined ? clamp(weight, 0, 1) : 1;
// cast to 8 bits
const index = (255 * opacity) | 0;
let style = this.styleCache_[index];
if (!style) {
style = [
new Style({
image: new Icon({
opacity: opacity,
src: this.circleImage_
})
})
];
this.styleCache_[index] = style;
}
return style;
}.bind(this));
// For performance reasons, don't sort the features before rendering.
// The render order is not relevant for a heatmap representation.
this.setRenderOrder(null);
listen(this, RenderEventType.RENDER, this.handleRender_, this);
}
this.setStyle(function(feature, resolution) {
const weight = weightFunction(feature);
const opacity = weight !== undefined ? clamp(weight, 0, 1) : 1;
// cast to 8 bits
const index = (255 * opacity) | 0;
let style = this.styleCache_[index];
if (!style) {
style = [
new Style({
image: new Icon({
opacity: opacity,
src: this.circleImage_
})
})
];
this.styleCache_[index] = style;
/**
* @return {string} Data URL for a circle.
* @private
*/
createCircle_() {
const radius = this.getRadius();
const blur = this.getBlur();
const halfSize = radius + blur + 1;
const size = 2 * halfSize;
const context = createCanvasContext2D(size, size);
context.shadowOffsetX = context.shadowOffsetY = this.shadow_;
context.shadowBlur = blur;
context.shadowColor = '#000';
context.beginPath();
const center = halfSize - this.shadow_;
context.arc(center, center, radius, 0, Math.PI * 2, true);
context.fill();
return context.canvas.toDataURL();
}
/**
* Return the blur size in pixels.
* @return {number} Blur size in pixels.
* @api
* @observable
*/
getBlur() {
return /** @type {number} */ (this.get(Property.BLUR));
}
/**
* Return the gradient colors as array of strings.
* @return {Array.<string>} Colors.
* @api
* @observable
*/
getGradient() {
return /** @type {Array.<string>} */ (this.get(Property.GRADIENT));
}
/**
* Return the size of the radius in pixels.
* @return {number} Radius size in pixel.
* @api
* @observable
*/
getRadius() {
return /** @type {number} */ (this.get(Property.RADIUS));
}
/**
* @private
*/
handleGradientChanged_() {
this.gradient_ = createGradient(this.getGradient());
}
/**
* @private
*/
handleStyleChanged_() {
this.circleImage_ = this.createCircle_();
this.styleCache_ = new Array(256);
this.changed();
}
/**
* @param {module:ol/render/Event} event Post compose event
* @private
*/
handleRender_(event) {
const context = event.context;
const canvas = context.canvas;
const image = context.getImageData(0, 0, canvas.width, canvas.height);
const view8 = image.data;
for (let i = 0, ii = view8.length; i < ii; i += 4) {
const alpha = view8[i + 3] * 4;
if (alpha) {
view8[i] = this.gradient_[alpha];
view8[i + 1] = this.gradient_[alpha + 1];
view8[i + 2] = this.gradient_[alpha + 2];
}
}
return style;
}.bind(this));
context.putImageData(image, 0, 0);
}
// For performance reasons, don't sort the features before rendering.
// The render order is not relevant for a heatmap representation.
this.setRenderOrder(null);
/**
* Set the blur size in pixels.
* @param {number} blur Blur size in pixels.
* @api
* @observable
*/
setBlur(blur) {
this.set(Property.BLUR, blur);
}
listen(this, RenderEventType.RENDER, this.handleRender_, this);
};
/**
* Set the gradient colors as array of strings.
* @param {Array.<string>} colors Gradient.
* @api
* @observable
*/
setGradient(colors) {
this.set(Property.GRADIENT, colors);
}
/**
* Set the size of the radius in pixels.
* @param {number} radius Radius size in pixel.
* @api
* @observable
*/
setRadius(radius) {
this.set(Property.RADIUS, radius);
}
}
inherits(Heatmap, VectorLayer);
@@ -191,129 +309,4 @@ const createGradient = function(colors) {
};
/**
* @return {string} Data URL for a circle.
* @private
*/
Heatmap.prototype.createCircle_ = function() {
const radius = this.getRadius();
const blur = this.getBlur();
const halfSize = radius + blur + 1;
const size = 2 * halfSize;
const context = createCanvasContext2D(size, size);
context.shadowOffsetX = context.shadowOffsetY = this.shadow_;
context.shadowBlur = blur;
context.shadowColor = '#000';
context.beginPath();
const center = halfSize - this.shadow_;
context.arc(center, center, radius, 0, Math.PI * 2, true);
context.fill();
return context.canvas.toDataURL();
};
/**
* Return the blur size in pixels.
* @return {number} Blur size in pixels.
* @api
* @observable
*/
Heatmap.prototype.getBlur = function() {
return /** @type {number} */ (this.get(Property.BLUR));
};
/**
* Return the gradient colors as array of strings.
* @return {Array.<string>} Colors.
* @api
* @observable
*/
Heatmap.prototype.getGradient = function() {
return /** @type {Array.<string>} */ (this.get(Property.GRADIENT));
};
/**
* Return the size of the radius in pixels.
* @return {number} Radius size in pixel.
* @api
* @observable
*/
Heatmap.prototype.getRadius = function() {
return /** @type {number} */ (this.get(Property.RADIUS));
};
/**
* @private
*/
Heatmap.prototype.handleGradientChanged_ = function() {
this.gradient_ = createGradient(this.getGradient());
};
/**
* @private
*/
Heatmap.prototype.handleStyleChanged_ = function() {
this.circleImage_ = this.createCircle_();
this.styleCache_ = new Array(256);
this.changed();
};
/**
* @param {module:ol/render/Event} event Post compose event
* @private
*/
Heatmap.prototype.handleRender_ = function(event) {
const context = event.context;
const canvas = context.canvas;
const image = context.getImageData(0, 0, canvas.width, canvas.height);
const view8 = image.data;
for (let i = 0, ii = view8.length; i < ii; i += 4) {
const alpha = view8[i + 3] * 4;
if (alpha) {
view8[i] = this.gradient_[alpha];
view8[i + 1] = this.gradient_[alpha + 1];
view8[i + 2] = this.gradient_[alpha + 2];
}
}
context.putImageData(image, 0, 0);
};
/**
* Set the blur size in pixels.
* @param {number} blur Blur size in pixels.
* @api
* @observable
*/
Heatmap.prototype.setBlur = function(blur) {
this.set(Property.BLUR, blur);
};
/**
* Set the gradient colors as array of strings.
* @param {Array.<string>} colors Gradient.
* @api
* @observable
*/
Heatmap.prototype.setGradient = function(colors) {
this.set(Property.GRADIENT, colors);
};
/**
* Set the size of the radius in pixels.
* @param {number} radius Radius size in pixel.
* @api
* @observable
*/
Heatmap.prototype.setRadius = function(radius) {
this.set(Property.RADIUS, radius);
};
export default Heatmap;

View File

@@ -66,42 +66,153 @@ import SourceState from '../source/State.js';
* @param {module:ol/layer/Layer~Options} options Layer options.
* @api
*/
const Layer = function(options) {
class Layer {
constructor(options) {
const baseOptions = assign({}, options);
delete baseOptions.source;
const baseOptions = assign({}, options);
delete baseOptions.source;
BaseLayer.call(this, /** @type {module:ol/layer/Base~Options} */ (baseOptions));
BaseLayer.call(this, /** @type {module:ol/layer/Base~Options} */ (baseOptions));
/**
* @private
* @type {?module:ol/events~EventsKey}
*/
this.mapPrecomposeKey_ = null;
/**
* @private
* @type {?module:ol/events~EventsKey}
*/
this.mapPrecomposeKey_ = null;
/**
* @private
* @type {?module:ol/events~EventsKey}
*/
this.mapRenderKey_ = null;
/**
* @private
* @type {?module:ol/events~EventsKey}
*/
this.mapRenderKey_ = null;
/**
* @private
* @type {?module:ol/events~EventsKey}
*/
this.sourceChangeKey_ = null;
/**
* @private
* @type {?module:ol/events~EventsKey}
*/
this.sourceChangeKey_ = null;
if (options.map) {
this.setMap(options.map);
if (options.map) {
this.setMap(options.map);
}
listen(this,
getChangeEventType(LayerProperty.SOURCE),
this.handleSourcePropertyChange_, this);
const source = options.source ? options.source : null;
this.setSource(source);
}
listen(this,
getChangeEventType(LayerProperty.SOURCE),
this.handleSourcePropertyChange_, this);
/**
* @inheritDoc
*/
getLayersArray(opt_array) {
const array = opt_array ? opt_array : [];
array.push(this);
return array;
}
const source = options.source ? options.source : null;
this.setSource(source);
};
/**
* @inheritDoc
*/
getLayerStatesArray(opt_states) {
const states = opt_states ? opt_states : [];
states.push(this.getLayerState());
return states;
}
/**
* Get the layer source.
* @return {module:ol/source/Source} The layer source (or `null` if not yet set).
* @observable
* @api
*/
getSource() {
const source = this.get(LayerProperty.SOURCE);
return (
/** @type {module:ol/source/Source} */ (source) || null
);
}
/**
* @inheritDoc
*/
getSourceState() {
const source = this.getSource();
return !source ? SourceState.UNDEFINED : source.getState();
}
/**
* @private
*/
handleSourceChange_() {
this.changed();
}
/**
* @private
*/
handleSourcePropertyChange_() {
if (this.sourceChangeKey_) {
unlistenByKey(this.sourceChangeKey_);
this.sourceChangeKey_ = null;
}
const source = this.getSource();
if (source) {
this.sourceChangeKey_ = listen(source,
EventType.CHANGE, this.handleSourceChange_, this);
}
this.changed();
}
/**
* Sets the layer to be rendered on top of other layers on a map. The map will
* not manage this layer in its layers collection, and the callback in
* {@link module:ol/Map#forEachLayerAtPixel} will receive `null` as layer. This
* is useful for temporary layers. To remove an unmanaged layer from the map,
* use `#setMap(null)`.
*
* To add the layer to a map and have it managed by the map, use
* {@link module:ol/Map#addLayer} instead.
* @param {module:ol/PluggableMap} map Map.
* @api
*/
setMap(map) {
if (this.mapPrecomposeKey_) {
unlistenByKey(this.mapPrecomposeKey_);
this.mapPrecomposeKey_ = null;
}
if (!map) {
this.changed();
}
if (this.mapRenderKey_) {
unlistenByKey(this.mapRenderKey_);
this.mapRenderKey_ = null;
}
if (map) {
this.mapPrecomposeKey_ = listen(map, RenderEventType.PRECOMPOSE, function(evt) {
const layerState = this.getLayerState();
layerState.managed = false;
layerState.zIndex = Infinity;
evt.frameState.layerStatesArray.push(layerState);
evt.frameState.layerStates[getUid(this)] = layerState;
}, this);
this.mapRenderKey_ = listen(this, EventType.CHANGE, map.render, map);
this.changed();
}
}
/**
* Set the layer source.
* @param {module:ol/source/Source} source The layer source.
* @observable
* @api
*/
setSource(source) {
this.set(LayerProperty.SOURCE, source);
}
}
inherits(Layer, BaseLayer);
@@ -120,119 +231,4 @@ export function visibleAtResolution(layerState, resolution) {
}
/**
* @inheritDoc
*/
Layer.prototype.getLayersArray = function(opt_array) {
const array = opt_array ? opt_array : [];
array.push(this);
return array;
};
/**
* @inheritDoc
*/
Layer.prototype.getLayerStatesArray = function(opt_states) {
const states = opt_states ? opt_states : [];
states.push(this.getLayerState());
return states;
};
/**
* Get the layer source.
* @return {module:ol/source/Source} The layer source (or `null` if not yet set).
* @observable
* @api
*/
Layer.prototype.getSource = function() {
const source = this.get(LayerProperty.SOURCE);
return (
/** @type {module:ol/source/Source} */ (source) || null
);
};
/**
* @inheritDoc
*/
Layer.prototype.getSourceState = function() {
const source = this.getSource();
return !source ? SourceState.UNDEFINED : source.getState();
};
/**
* @private
*/
Layer.prototype.handleSourceChange_ = function() {
this.changed();
};
/**
* @private
*/
Layer.prototype.handleSourcePropertyChange_ = function() {
if (this.sourceChangeKey_) {
unlistenByKey(this.sourceChangeKey_);
this.sourceChangeKey_ = null;
}
const source = this.getSource();
if (source) {
this.sourceChangeKey_ = listen(source,
EventType.CHANGE, this.handleSourceChange_, this);
}
this.changed();
};
/**
* Sets the layer to be rendered on top of other layers on a map. The map will
* not manage this layer in its layers collection, and the callback in
* {@link module:ol/Map#forEachLayerAtPixel} will receive `null` as layer. This
* is useful for temporary layers. To remove an unmanaged layer from the map,
* use `#setMap(null)`.
*
* To add the layer to a map and have it managed by the map, use
* {@link module:ol/Map#addLayer} instead.
* @param {module:ol/PluggableMap} map Map.
* @api
*/
Layer.prototype.setMap = function(map) {
if (this.mapPrecomposeKey_) {
unlistenByKey(this.mapPrecomposeKey_);
this.mapPrecomposeKey_ = null;
}
if (!map) {
this.changed();
}
if (this.mapRenderKey_) {
unlistenByKey(this.mapRenderKey_);
this.mapRenderKey_ = null;
}
if (map) {
this.mapPrecomposeKey_ = listen(map, RenderEventType.PRECOMPOSE, function(evt) {
const layerState = this.getLayerState();
layerState.managed = false;
layerState.zIndex = Infinity;
evt.frameState.layerStatesArray.push(layerState);
evt.frameState.layerStates[getUid(this)] = layerState;
}, this);
this.mapRenderKey_ = listen(this, EventType.CHANGE, map.render, map);
this.changed();
}
};
/**
* Set the layer source.
* @param {module:ol/source/Source} source The layer source.
* @observable
* @api
*/
Layer.prototype.setSource = function(source) {
this.set(LayerProperty.SOURCE, source);
};
export default Layer;

View File

@@ -45,42 +45,73 @@ import {assign} from '../obj.js';
* @param {module:ol/layer/Tile~Options=} opt_options Tile layer options.
* @api
*/
const TileLayer = function(opt_options) {
const options = opt_options ? opt_options : {};
class TileLayer {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const baseOptions = assign({}, options);
const baseOptions = assign({}, options);
delete baseOptions.preload;
delete baseOptions.useInterimTilesOnError;
Layer.call(this, /** @type {module:ol/layer/Layer~Options} */ (baseOptions));
delete baseOptions.preload;
delete baseOptions.useInterimTilesOnError;
Layer.call(this, /** @type {module:ol/layer/Layer~Options} */ (baseOptions));
this.setPreload(options.preload !== undefined ? options.preload : 0);
this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ?
options.useInterimTilesOnError : true);
this.setPreload(options.preload !== undefined ? options.preload : 0);
this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ?
options.useInterimTilesOnError : true);
/**
* The layer type.
* @protected
* @type {module:ol/LayerType}
*/
this.type = LayerType.TILE;
/**
* The layer type.
* @protected
* @type {module:ol/LayerType}
*/
this.type = LayerType.TILE;
};
}
/**
* Return the level as number to which we will preload tiles up to.
* @return {number} The level to preload tiles up to.
* @observable
* @api
*/
getPreload() {
return /** @type {number} */ (this.get(TileProperty.PRELOAD));
}
/**
* Set the level as number to which we will preload tiles up to.
* @param {number} preload The level to preload tiles up to.
* @observable
* @api
*/
setPreload(preload) {
this.set(TileProperty.PRELOAD, preload);
}
/**
* Whether we use interim tiles on error.
* @return {boolean} Use interim tiles on error.
* @observable
* @api
*/
getUseInterimTilesOnError() {
return /** @type {boolean} */ (this.get(TileProperty.USE_INTERIM_TILES_ON_ERROR));
}
/**
* Set whether we use interim tiles on error.
* @param {boolean} useInterimTilesOnError Use interim tiles on error.
* @observable
* @api
*/
setUseInterimTilesOnError(useInterimTilesOnError) {
this.set(TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError);
}
}
inherits(TileLayer, Layer);
/**
* Return the level as number to which we will preload tiles up to.
* @return {number} The level to preload tiles up to.
* @observable
* @api
*/
TileLayer.prototype.getPreload = function() {
return /** @type {number} */ (this.get(TileProperty.PRELOAD));
};
/**
* Return the associated {@link module:ol/source/Tile tilesource} of the layer.
* @function
@@ -90,35 +121,4 @@ TileLayer.prototype.getPreload = function() {
TileLayer.prototype.getSource;
/**
* Set the level as number to which we will preload tiles up to.
* @param {number} preload The level to preload tiles up to.
* @observable
* @api
*/
TileLayer.prototype.setPreload = function(preload) {
this.set(TileProperty.PRELOAD, preload);
};
/**
* Whether we use interim tiles on error.
* @return {boolean} Use interim tiles on error.
* @observable
* @api
*/
TileLayer.prototype.getUseInterimTilesOnError = function() {
return /** @type {boolean} */ (this.get(TileProperty.USE_INTERIM_TILES_ON_ERROR));
};
/**
* Set whether we use interim tiles on error.
* @param {boolean} useInterimTilesOnError Use interim tiles on error.
* @observable
* @api
*/
TileLayer.prototype.setUseInterimTilesOnError = function(useInterimTilesOnError) {
this.set(TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError);
};
export default TileLayer;

View File

@@ -91,114 +91,181 @@ const Property = {
* @param {module:ol/layer/Vector~Options=} opt_options Options.
* @api
*/
const VectorLayer = function(opt_options) {
const options = opt_options ?
opt_options : /** @type {module:ol/layer/Vector~Options} */ ({});
class VectorLayer {
constructor(opt_options) {
const options = opt_options ?
opt_options : /** @type {module:ol/layer/Vector~Options} */ ({});
const baseOptions = assign({}, options);
const baseOptions = assign({}, options);
delete baseOptions.style;
delete baseOptions.renderBuffer;
delete baseOptions.updateWhileAnimating;
delete baseOptions.updateWhileInteracting;
Layer.call(this, /** @type {module:ol/layer/Layer~Options} */ (baseOptions));
delete baseOptions.style;
delete baseOptions.renderBuffer;
delete baseOptions.updateWhileAnimating;
delete baseOptions.updateWhileInteracting;
Layer.call(this, /** @type {module:ol/layer/Layer~Options} */ (baseOptions));
/**
* @private
* @type {boolean}
*/
this.declutter_ = options.declutter !== undefined ? options.declutter : false;
/**
* @private
* @type {boolean}
*/
this.declutter_ = options.declutter !== undefined ? options.declutter : false;
/**
* @type {number}
* @private
*/
this.renderBuffer_ = options.renderBuffer !== undefined ?
options.renderBuffer : 100;
/**
* @type {number}
* @private
*/
this.renderBuffer_ = options.renderBuffer !== undefined ?
options.renderBuffer : 100;
/**
* User provided style.
* @type {module:ol/style/Style|Array.<module:ol/style/Style>|module:ol/style/Style~StyleFunction}
* @private
*/
this.style_ = null;
/**
* User provided style.
* @type {module:ol/style/Style|Array.<module:ol/style/Style>|module:ol/style/Style~StyleFunction}
* @private
*/
this.style_ = null;
/**
* Style function for use within the library.
* @type {module:ol/style/Style~StyleFunction|undefined}
* @private
*/
this.styleFunction_ = undefined;
/**
* Style function for use within the library.
* @type {module:ol/style/Style~StyleFunction|undefined}
* @private
*/
this.styleFunction_ = undefined;
this.setStyle(options.style);
this.setStyle(options.style);
/**
* @type {boolean}
* @private
*/
this.updateWhileAnimating_ = options.updateWhileAnimating !== undefined ?
options.updateWhileAnimating : false;
/**
* @type {boolean}
* @private
*/
this.updateWhileAnimating_ = options.updateWhileAnimating !== undefined ?
options.updateWhileAnimating : false;
/**
* @type {boolean}
* @private
*/
this.updateWhileInteracting_ = options.updateWhileInteracting !== undefined ?
options.updateWhileInteracting : false;
/**
* @type {boolean}
* @private
*/
this.updateWhileInteracting_ = options.updateWhileInteracting !== undefined ?
options.updateWhileInteracting : false;
/**
* @private
* @type {module:ol/layer/VectorTileRenderType|string}
*/
this.renderMode_ = options.renderMode || VectorRenderType.VECTOR;
/**
* @private
* @type {module:ol/layer/VectorTileRenderType|string}
*/
this.renderMode_ = options.renderMode || VectorRenderType.VECTOR;
/**
* The layer type.
* @protected
* @type {module:ol/LayerType}
*/
this.type = LayerType.VECTOR;
/**
* The layer type.
* @protected
* @type {module:ol/LayerType}
*/
this.type = LayerType.VECTOR;
};
}
/**
* @return {boolean} Declutter.
*/
getDeclutter() {
return this.declutter_;
}
/**
* @param {boolean} declutter Declutter.
*/
setDeclutter(declutter) {
this.declutter_ = declutter;
}
/**
* @return {number|undefined} Render buffer.
*/
getRenderBuffer() {
return this.renderBuffer_;
}
/**
* @return {function(module:ol/Feature, module:ol/Feature): number|null|undefined} Render
* order.
*/
getRenderOrder() {
return (
/** @type {module:ol/render~OrderFunction|null|undefined} */ (this.get(Property.RENDER_ORDER))
);
}
/**
* Get the style for features. This returns whatever was passed to the `style`
* option at construction or to the `setStyle` method.
* @return {module:ol/style/Style|Array.<module:ol/style/Style>|module:ol/style/Style~StyleFunction}
* Layer style.
* @api
*/
getStyle() {
return this.style_;
}
/**
* Get the style function.
* @return {module:ol/style/Style~StyleFunction|undefined} Layer style function.
* @api
*/
getStyleFunction() {
return this.styleFunction_;
}
/**
* @return {boolean} Whether the rendered layer should be updated while
* animating.
*/
getUpdateWhileAnimating() {
return this.updateWhileAnimating_;
}
/**
* @return {boolean} Whether the rendered layer should be updated while
* interacting.
*/
getUpdateWhileInteracting() {
return this.updateWhileInteracting_;
}
/**
* @param {module:ol/render~OrderFunction|null|undefined} renderOrder
* Render order.
*/
setRenderOrder(renderOrder) {
this.set(Property.RENDER_ORDER, renderOrder);
}
/**
* Set the style for features. This can be a single style object, an array
* of styles, or a function that takes a feature and resolution and returns
* an array of styles. If it is `undefined` the default style is used. If
* it is `null` the layer has no style (a `null` style), so only features
* that have their own styles will be rendered in the layer. See
* {@link module:ol/style} for information on the default style.
* @param {module:ol/style/Style|Array.<module:ol/style/Style>|module:ol/style/Style~StyleFunction|null|undefined}
* style Layer style.
* @api
*/
setStyle(style) {
this.style_ = style !== undefined ? style : createDefaultStyle;
this.styleFunction_ = style === null ?
undefined : toStyleFunction(this.style_);
this.changed();
}
/**
* @return {module:ol/layer/VectorRenderType|string} The render mode.
*/
getRenderMode() {
return this.renderMode_;
}
}
inherits(VectorLayer, Layer);
/**
* @return {boolean} Declutter.
*/
VectorLayer.prototype.getDeclutter = function() {
return this.declutter_;
};
/**
* @param {boolean} declutter Declutter.
*/
VectorLayer.prototype.setDeclutter = function(declutter) {
this.declutter_ = declutter;
};
/**
* @return {number|undefined} Render buffer.
*/
VectorLayer.prototype.getRenderBuffer = function() {
return this.renderBuffer_;
};
/**
* @return {function(module:ol/Feature, module:ol/Feature): number|null|undefined} Render
* order.
*/
VectorLayer.prototype.getRenderOrder = function() {
return (
/** @type {module:ol/render~OrderFunction|null|undefined} */ (this.get(Property.RENDER_ORDER))
);
};
/**
* Return the associated {@link module:ol/source/Vector vectorsource} of the layer.
* @function
@@ -208,80 +275,4 @@ VectorLayer.prototype.getRenderOrder = function() {
VectorLayer.prototype.getSource;
/**
* Get the style for features. This returns whatever was passed to the `style`
* option at construction or to the `setStyle` method.
* @return {module:ol/style/Style|Array.<module:ol/style/Style>|module:ol/style/Style~StyleFunction}
* Layer style.
* @api
*/
VectorLayer.prototype.getStyle = function() {
return this.style_;
};
/**
* Get the style function.
* @return {module:ol/style/Style~StyleFunction|undefined} Layer style function.
* @api
*/
VectorLayer.prototype.getStyleFunction = function() {
return this.styleFunction_;
};
/**
* @return {boolean} Whether the rendered layer should be updated while
* animating.
*/
VectorLayer.prototype.getUpdateWhileAnimating = function() {
return this.updateWhileAnimating_;
};
/**
* @return {boolean} Whether the rendered layer should be updated while
* interacting.
*/
VectorLayer.prototype.getUpdateWhileInteracting = function() {
return this.updateWhileInteracting_;
};
/**
* @param {module:ol/render~OrderFunction|null|undefined} renderOrder
* Render order.
*/
VectorLayer.prototype.setRenderOrder = function(renderOrder) {
this.set(Property.RENDER_ORDER, renderOrder);
};
/**
* Set the style for features. This can be a single style object, an array
* of styles, or a function that takes a feature and resolution and returns
* an array of styles. If it is `undefined` the default style is used. If
* it is `null` the layer has no style (a `null` style), so only features
* that have their own styles will be rendered in the layer. See
* {@link module:ol/style} for information on the default style.
* @param {module:ol/style/Style|Array.<module:ol/style/Style>|module:ol/style/Style~StyleFunction|null|undefined}
* style Layer style.
* @api
*/
VectorLayer.prototype.setStyle = function(style) {
this.style_ = style !== undefined ? style : createDefaultStyle;
this.styleFunction_ = style === null ?
undefined : toStyleFunction(this.style_);
this.changed();
};
/**
* @return {module:ol/layer/VectorRenderType|string} The render mode.
*/
VectorLayer.prototype.getRenderMode = function() {
return this.renderMode_;
};
export default VectorLayer;

View File

@@ -99,86 +99,84 @@ export const RenderType = {
* @param {module:ol/layer/VectorTile~Options=} opt_options Options.
* @api
*/
const VectorTileLayer = function(opt_options) {
const options = opt_options ? opt_options : {};
class VectorTileLayer {
constructor(opt_options) {
const options = opt_options ? opt_options : {};
let renderMode = options.renderMode || VectorTileRenderType.HYBRID;
assert(renderMode == undefined ||
renderMode == VectorTileRenderType.IMAGE ||
renderMode == VectorTileRenderType.HYBRID ||
renderMode == VectorTileRenderType.VECTOR,
28); // `renderMode` must be `'image'`, `'hybrid'` or `'vector'`
if (options.declutter && renderMode == VectorTileRenderType.IMAGE) {
renderMode = VectorTileRenderType.HYBRID;
}
options.renderMode = renderMode;
let renderMode = options.renderMode || VectorTileRenderType.HYBRID;
assert(renderMode == undefined ||
renderMode == VectorTileRenderType.IMAGE ||
renderMode == VectorTileRenderType.HYBRID ||
renderMode == VectorTileRenderType.VECTOR,
28); // `renderMode` must be `'image'`, `'hybrid'` or `'vector'`
if (options.declutter && renderMode == VectorTileRenderType.IMAGE) {
renderMode = VectorTileRenderType.HYBRID;
}
options.renderMode = renderMode;
const baseOptions = assign({}, options);
const baseOptions = assign({}, options);
delete baseOptions.preload;
delete baseOptions.useInterimTilesOnError;
VectorLayer.call(this, /** @type {module:ol/layer/Vector~Options} */ (baseOptions));
delete baseOptions.preload;
delete baseOptions.useInterimTilesOnError;
VectorLayer.call(this, /** @type {module:ol/layer/Vector~Options} */ (baseOptions));
this.setPreload(options.preload ? options.preload : 0);
this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ?
options.useInterimTilesOnError : true);
this.setPreload(options.preload ? options.preload : 0);
this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ?
options.useInterimTilesOnError : true);
/**
* The layer type.
* @protected
* @type {module:ol/LayerType}
*/
this.type = LayerType.VECTOR_TILE;
/**
* The layer type.
* @protected
* @type {module:ol/LayerType}
*/
this.type = LayerType.VECTOR_TILE;
};
}
/**
* Return the level as number to which we will preload tiles up to.
* @return {number} The level to preload tiles up to.
* @observable
* @api
*/
getPreload() {
return /** @type {number} */ (this.get(TileProperty.PRELOAD));
}
/**
* Whether we use interim tiles on error.
* @return {boolean} Use interim tiles on error.
* @observable
* @api
*/
getUseInterimTilesOnError() {
return /** @type {boolean} */ (this.get(TileProperty.USE_INTERIM_TILES_ON_ERROR));
}
/**
* Set the level as number to which we will preload tiles up to.
* @param {number} preload The level to preload tiles up to.
* @observable
* @api
*/
setPreload(preload) {
this.set(TileProperty.PRELOAD, preload);
}
/**
* Set whether we use interim tiles on error.
* @param {boolean} useInterimTilesOnError Use interim tiles on error.
* @observable
* @api
*/
setUseInterimTilesOnError(useInterimTilesOnError) {
this.set(TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError);
}
}
inherits(VectorTileLayer, VectorLayer);
/**
* Return the level as number to which we will preload tiles up to.
* @return {number} The level to preload tiles up to.
* @observable
* @api
*/
VectorTileLayer.prototype.getPreload = function() {
return /** @type {number} */ (this.get(TileProperty.PRELOAD));
};
/**
* Whether we use interim tiles on error.
* @return {boolean} Use interim tiles on error.
* @observable
* @api
*/
VectorTileLayer.prototype.getUseInterimTilesOnError = function() {
return /** @type {boolean} */ (this.get(TileProperty.USE_INTERIM_TILES_ON_ERROR));
};
/**
* Set the level as number to which we will preload tiles up to.
* @param {number} preload The level to preload tiles up to.
* @observable
* @api
*/
VectorTileLayer.prototype.setPreload = function(preload) {
this.set(TileProperty.PRELOAD, preload);
};
/**
* Set whether we use interim tiles on error.
* @param {boolean} useInterimTilesOnError Use interim tiles on error.
* @observable
* @api
*/
VectorTileLayer.prototype.setUseInterimTilesOnError = function(useInterimTilesOnError) {
this.set(TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError);
};
/**
* Return the associated {@link module:ol/source/VectorTile vectortilesource} of the layer.
* @function

View File

@@ -6,36 +6,37 @@
* @param {!Object.<string, function(Event)>} mapping Event mapping.
* @constructor
*/
const EventSource = function(dispatcher, mapping) {
/**
* @type {module:ol/pointer/PointerEventHandler}
*/
this.dispatcher = dispatcher;
class EventSource {
constructor(dispatcher, mapping) {
/**
* @type {module:ol/pointer/PointerEventHandler}
*/
this.dispatcher = dispatcher;
/**
* @private
* @const
* @type {!Object.<string, function(Event)>}
*/
this.mapping_ = mapping;
};
/**
* @private
* @const
* @type {!Object.<string, function(Event)>}
*/
this.mapping_ = mapping;
}
/**
* List of events supported by this source.
* @return {Array.<string>} Event names
*/
getEvents() {
return Object.keys(this.mapping_);
}
/**
* List of events supported by this source.
* @return {Array.<string>} Event names
*/
EventSource.prototype.getEvents = function() {
return Object.keys(this.mapping_);
};
/**
* Returns the handler that should handle a given event type.
* @param {string} eventType The event type.
* @return {function(Event)} Handler
*/
getHandlerForEvent(eventType) {
return this.mapping_[eventType];
}
}
/**
* Returns the handler that should handle a given event type.
* @param {string} eventType The event type.
* @return {function(Event)} Handler
*/
EventSource.prototype.getHandlerForEvent = function(eventType) {
return this.mapping_[eventType];
};
export default EventSource;

View File

@@ -39,28 +39,158 @@ import EventSource from '../pointer/EventSource.js';
* @constructor
* @extends {module:ol/pointer/EventSource}
*/
const MouseSource = function(dispatcher) {
const mapping = {
'mousedown': this.mousedown,
'mousemove': this.mousemove,
'mouseup': this.mouseup,
'mouseover': this.mouseover,
'mouseout': this.mouseout
};
EventSource.call(this, dispatcher, mapping);
class MouseSource {
constructor(dispatcher) {
const mapping = {
'mousedown': this.mousedown,
'mousemove': this.mousemove,
'mouseup': this.mouseup,
'mouseover': this.mouseover,
'mouseout': this.mouseout
};
EventSource.call(this, dispatcher, mapping);
/**
* @const
* @type {!Object.<string, Event|Object>}
*/
this.pointerMap = dispatcher.pointerMap;
/**
* @const
* @type {Array.<module:ol~Pixel>}
*/
this.lastTouches = [];
}
/**
* @const
* @type {!Object.<string, Event|Object>}
* Detect if a mouse event was simulated from a touch by
* checking if previously there was a touch event at the
* same position.
*
* FIXME - Known problem with the native Android browser on
* Samsung GT-I9100 (Android 4.1.2):
* In case the page is scrolled, this function does not work
* correctly when a canvas is used (WebGL or canvas renderer).
* Mouse listeners on canvas elements (for this browser), create
* two mouse events: One 'good' and one 'bad' one (on other browsers or
* when a div is used, there is only one event). For the 'bad' one,
* clientX/clientY and also pageX/pageY are wrong when the page
* is scrolled. Because of that, this function can not detect if
* the events were simulated from a touch event. As result, a
* pointer event at a wrong position is dispatched, which confuses
* the map interactions.
* It is unclear, how one can get the correct position for the event
* or detect that the positions are invalid.
*
* @private
* @param {MouseEvent} inEvent The in event.
* @return {boolean} True, if the event was generated by a touch.
*/
this.pointerMap = dispatcher.pointerMap;
isEventSimulatedFromTouch_(inEvent) {
const lts = this.lastTouches;
const x = inEvent.clientX;
const y = inEvent.clientY;
for (let i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) {
// simulated mouse events will be swallowed near a primary touchend
const dx = Math.abs(x - t[0]);
const dy = Math.abs(y - t[1]);
if (dx <= DEDUP_DIST && dy <= DEDUP_DIST) {
return true;
}
}
return false;
}
/**
* @const
* @type {Array.<module:ol~Pixel>}
* Handler for `mousedown`.
*
* @param {MouseEvent} inEvent The in event.
*/
this.lastTouches = [];
};
mousedown(inEvent) {
if (!this.isEventSimulatedFromTouch_(inEvent)) {
// TODO(dfreedman) workaround for some elements not sending mouseup
// http://crbug/149091
if (POINTER_ID.toString() in this.pointerMap) {
this.cancel(inEvent);
}
const e = prepareEvent(inEvent, this.dispatcher);
this.pointerMap[POINTER_ID.toString()] = inEvent;
this.dispatcher.down(e, inEvent);
}
}
/**
* Handler for `mousemove`.
*
* @param {MouseEvent} inEvent The in event.
*/
mousemove(inEvent) {
if (!this.isEventSimulatedFromTouch_(inEvent)) {
const e = prepareEvent(inEvent, this.dispatcher);
this.dispatcher.move(e, inEvent);
}
}
/**
* Handler for `mouseup`.
*
* @param {MouseEvent} inEvent The in event.
*/
mouseup(inEvent) {
if (!this.isEventSimulatedFromTouch_(inEvent)) {
const p = this.pointerMap[POINTER_ID.toString()];
if (p && p.button === inEvent.button) {
const e = prepareEvent(inEvent, this.dispatcher);
this.dispatcher.up(e, inEvent);
this.cleanupMouse();
}
}
}
/**
* Handler for `mouseover`.
*
* @param {MouseEvent} inEvent The in event.
*/
mouseover(inEvent) {
if (!this.isEventSimulatedFromTouch_(inEvent)) {
const e = prepareEvent(inEvent, this.dispatcher);
this.dispatcher.enterOver(e, inEvent);
}
}
/**
* Handler for `mouseout`.
*
* @param {MouseEvent} inEvent The in event.
*/
mouseout(inEvent) {
if (!this.isEventSimulatedFromTouch_(inEvent)) {
const e = prepareEvent(inEvent, this.dispatcher);
this.dispatcher.leaveOut(e, inEvent);
}
}
/**
* Dispatches a `pointercancel` event.
*
* @param {Event} inEvent The in event.
*/
cancel(inEvent) {
const e = prepareEvent(inEvent, this.dispatcher);
this.dispatcher.cancel(e, inEvent);
this.cleanupMouse();
}
/**
* Remove the mouse from the list of active pointers.
*/
cleanupMouse() {
delete this.pointerMap[POINTER_ID.toString()];
}
}
inherits(MouseSource, EventSource);
@@ -85,46 +215,6 @@ export const POINTER_TYPE = 'mouse';
const DEDUP_DIST = 25;
/**
* Detect if a mouse event was simulated from a touch by
* checking if previously there was a touch event at the
* same position.
*
* FIXME - Known problem with the native Android browser on
* Samsung GT-I9100 (Android 4.1.2):
* In case the page is scrolled, this function does not work
* correctly when a canvas is used (WebGL or canvas renderer).
* Mouse listeners on canvas elements (for this browser), create
* two mouse events: One 'good' and one 'bad' one (on other browsers or
* when a div is used, there is only one event). For the 'bad' one,
* clientX/clientY and also pageX/pageY are wrong when the page
* is scrolled. Because of that, this function can not detect if
* the events were simulated from a touch event. As result, a
* pointer event at a wrong position is dispatched, which confuses
* the map interactions.
* It is unclear, how one can get the correct position for the event
* or detect that the positions are invalid.
*
* @private
* @param {MouseEvent} inEvent The in event.
* @return {boolean} True, if the event was generated by a touch.
*/
MouseSource.prototype.isEventSimulatedFromTouch_ = function(inEvent) {
const lts = this.lastTouches;
const x = inEvent.clientX;
const y = inEvent.clientY;
for (let i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) {
// simulated mouse events will be swallowed near a primary touchend
const dx = Math.abs(x - t[0]);
const dy = Math.abs(y - t[1]);
if (dx <= DEDUP_DIST && dy <= DEDUP_DIST) {
return true;
}
}
return false;
};
/**
* Creates a copy of the original event that will be used
* for the fake pointer event.
@@ -151,98 +241,4 @@ function prepareEvent(inEvent, dispatcher) {
}
/**
* Handler for `mousedown`.
*
* @param {MouseEvent} inEvent The in event.
*/
MouseSource.prototype.mousedown = function(inEvent) {
if (!this.isEventSimulatedFromTouch_(inEvent)) {
// TODO(dfreedman) workaround for some elements not sending mouseup
// http://crbug/149091
if (POINTER_ID.toString() in this.pointerMap) {
this.cancel(inEvent);
}
const e = prepareEvent(inEvent, this.dispatcher);
this.pointerMap[POINTER_ID.toString()] = inEvent;
this.dispatcher.down(e, inEvent);
}
};
/**
* Handler for `mousemove`.
*
* @param {MouseEvent} inEvent The in event.
*/
MouseSource.prototype.mousemove = function(inEvent) {
if (!this.isEventSimulatedFromTouch_(inEvent)) {
const e = prepareEvent(inEvent, this.dispatcher);
this.dispatcher.move(e, inEvent);
}
};
/**
* Handler for `mouseup`.
*
* @param {MouseEvent} inEvent The in event.
*/
MouseSource.prototype.mouseup = function(inEvent) {
if (!this.isEventSimulatedFromTouch_(inEvent)) {
const p = this.pointerMap[POINTER_ID.toString()];
if (p && p.button === inEvent.button) {
const e = prepareEvent(inEvent, this.dispatcher);
this.dispatcher.up(e, inEvent);
this.cleanupMouse();
}
}
};
/**
* Handler for `mouseover`.
*
* @param {MouseEvent} inEvent The in event.
*/
MouseSource.prototype.mouseover = function(inEvent) {
if (!this.isEventSimulatedFromTouch_(inEvent)) {
const e = prepareEvent(inEvent, this.dispatcher);
this.dispatcher.enterOver(e, inEvent);
}
};
/**
* Handler for `mouseout`.
*
* @param {MouseEvent} inEvent The in event.
*/
MouseSource.prototype.mouseout = function(inEvent) {
if (!this.isEventSimulatedFromTouch_(inEvent)) {
const e = prepareEvent(inEvent, this.dispatcher);
this.dispatcher.leaveOut(e, inEvent);
}
};
/**
* Dispatches a `pointercancel` event.
*
* @param {Event} inEvent The in event.
*/
MouseSource.prototype.cancel = function(inEvent) {
const e = prepareEvent(inEvent, this.dispatcher);
this.dispatcher.cancel(e, inEvent);
this.cleanupMouse();
};
/**
* Remove the mouse from the list of active pointers.
*/
MouseSource.prototype.cleanupMouse = function() {
delete this.pointerMap[POINTER_ID.toString()];
};
export default MouseSource;

View File

@@ -39,25 +39,136 @@ import EventSource from '../pointer/EventSource.js';
* @constructor
* @extends {module:ol/pointer/EventSource}
*/
const MsSource = function(dispatcher) {
const mapping = {
'MSPointerDown': this.msPointerDown,
'MSPointerMove': this.msPointerMove,
'MSPointerUp': this.msPointerUp,
'MSPointerOut': this.msPointerOut,
'MSPointerOver': this.msPointerOver,
'MSPointerCancel': this.msPointerCancel,
'MSGotPointerCapture': this.msGotPointerCapture,
'MSLostPointerCapture': this.msLostPointerCapture
};
EventSource.call(this, dispatcher, mapping);
class MsSource {
constructor(dispatcher) {
const mapping = {
'MSPointerDown': this.msPointerDown,
'MSPointerMove': this.msPointerMove,
'MSPointerUp': this.msPointerUp,
'MSPointerOut': this.msPointerOut,
'MSPointerOver': this.msPointerOver,
'MSPointerCancel': this.msPointerCancel,
'MSGotPointerCapture': this.msGotPointerCapture,
'MSLostPointerCapture': this.msLostPointerCapture
};
EventSource.call(this, dispatcher, mapping);
/**
* @const
* @type {!Object.<string, MSPointerEvent|Object>}
*/
this.pointerMap = dispatcher.pointerMap;
};
/**
* @const
* @type {!Object.<string, MSPointerEvent|Object>}
*/
this.pointerMap = dispatcher.pointerMap;
}
/**
* Creates a copy of the original event that will be used
* for the fake pointer event.
*
* @private
* @param {MSPointerEvent} inEvent The in event.
* @return {Object} The copied event.
*/
prepareEvent_(inEvent) {
let e = inEvent;
if (typeof inEvent.pointerType === 'number') {
e = this.dispatcher.cloneEvent(inEvent, inEvent);
e.pointerType = POINTER_TYPES[inEvent.pointerType];
}
return e;
}
/**
* Remove this pointer from the list of active pointers.
* @param {number} pointerId Pointer identifier.
*/
cleanup(pointerId) {
delete this.pointerMap[pointerId.toString()];
}
/**
* Handler for `msPointerDown`.
*
* @param {MSPointerEvent} inEvent The in event.
*/
msPointerDown(inEvent) {
this.pointerMap[inEvent.pointerId.toString()] = inEvent;
const e = this.prepareEvent_(inEvent);
this.dispatcher.down(e, inEvent);
}
/**
* Handler for `msPointerMove`.
*
* @param {MSPointerEvent} inEvent The in event.
*/
msPointerMove(inEvent) {
const e = this.prepareEvent_(inEvent);
this.dispatcher.move(e, inEvent);
}
/**
* Handler for `msPointerUp`.
*
* @param {MSPointerEvent} inEvent The in event.
*/
msPointerUp(inEvent) {
const e = this.prepareEvent_(inEvent);
this.dispatcher.up(e, inEvent);
this.cleanup(inEvent.pointerId);
}
/**
* Handler for `msPointerOut`.
*
* @param {MSPointerEvent} inEvent The in event.
*/
msPointerOut(inEvent) {
const e = this.prepareEvent_(inEvent);
this.dispatcher.leaveOut(e, inEvent);
}
/**
* Handler for `msPointerOver`.
*
* @param {MSPointerEvent} inEvent The in event.
*/
msPointerOver(inEvent) {
const e = this.prepareEvent_(inEvent);
this.dispatcher.enterOver(e, inEvent);
}
/**
* Handler for `msPointerCancel`.
*
* @param {MSPointerEvent} inEvent The in event.
*/
msPointerCancel(inEvent) {
const e = this.prepareEvent_(inEvent);
this.dispatcher.cancel(e, inEvent);
this.cleanup(inEvent.pointerId);
}
/**
* Handler for `msLostPointerCapture`.
*
* @param {MSPointerEvent} inEvent The in event.
*/
msLostPointerCapture(inEvent) {
const e = this.dispatcher.makeEvent('lostpointercapture', inEvent, inEvent);
this.dispatcher.dispatchEvent(e);
}
/**
* Handler for `msGotPointerCapture`.
*
* @param {MSPointerEvent} inEvent The in event.
*/
msGotPointerCapture(inEvent) {
const e = this.dispatcher.makeEvent('gotpointercapture', inEvent, inEvent);
this.dispatcher.dispatchEvent(e);
}
}
inherits(MsSource, EventSource);
@@ -74,121 +185,4 @@ const POINTER_TYPES = [
];
/**
* Creates a copy of the original event that will be used
* for the fake pointer event.
*
* @private
* @param {MSPointerEvent} inEvent The in event.
* @return {Object} The copied event.
*/
MsSource.prototype.prepareEvent_ = function(inEvent) {
let e = inEvent;
if (typeof inEvent.pointerType === 'number') {
e = this.dispatcher.cloneEvent(inEvent, inEvent);
e.pointerType = POINTER_TYPES[inEvent.pointerType];
}
return e;
};
/**
* Remove this pointer from the list of active pointers.
* @param {number} pointerId Pointer identifier.
*/
MsSource.prototype.cleanup = function(pointerId) {
delete this.pointerMap[pointerId.toString()];
};
/**
* Handler for `msPointerDown`.
*
* @param {MSPointerEvent} inEvent The in event.
*/
MsSource.prototype.msPointerDown = function(inEvent) {
this.pointerMap[inEvent.pointerId.toString()] = inEvent;
const e = this.prepareEvent_(inEvent);
this.dispatcher.down(e, inEvent);
};
/**
* Handler for `msPointerMove`.
*
* @param {MSPointerEvent} inEvent The in event.
*/
MsSource.prototype.msPointerMove = function(inEvent) {
const e = this.prepareEvent_(inEvent);
this.dispatcher.move(e, inEvent);
};
/**
* Handler for `msPointerUp`.
*
* @param {MSPointerEvent} inEvent The in event.
*/
MsSource.prototype.msPointerUp = function(inEvent) {
const e = this.prepareEvent_(inEvent);
this.dispatcher.up(e, inEvent);
this.cleanup(inEvent.pointerId);
};
/**
* Handler for `msPointerOut`.
*
* @param {MSPointerEvent} inEvent The in event.
*/
MsSource.prototype.msPointerOut = function(inEvent) {
const e = this.prepareEvent_(inEvent);
this.dispatcher.leaveOut(e, inEvent);
};
/**
* Handler for `msPointerOver`.
*
* @param {MSPointerEvent} inEvent The in event.
*/
MsSource.prototype.msPointerOver = function(inEvent) {
const e = this.prepareEvent_(inEvent);
this.dispatcher.enterOver(e, inEvent);
};
/**
* Handler for `msPointerCancel`.
*
* @param {MSPointerEvent} inEvent The in event.
*/
MsSource.prototype.msPointerCancel = function(inEvent) {
const e = this.prepareEvent_(inEvent);
this.dispatcher.cancel(e, inEvent);
this.cleanup(inEvent.pointerId);
};
/**
* Handler for `msLostPointerCapture`.
*
* @param {MSPointerEvent} inEvent The in event.
*/
MsSource.prototype.msLostPointerCapture = function(inEvent) {
const e = this.dispatcher.makeEvent('lostpointercapture', inEvent, inEvent);
this.dispatcher.dispatchEvent(e);
};
/**
* Handler for `msGotPointerCapture`.
*
* @param {MSPointerEvent} inEvent The in event.
*/
MsSource.prototype.msGotPointerCapture = function(inEvent) {
const e = this.dispatcher.makeEvent('gotpointercapture', inEvent, inEvent);
this.dispatcher.dispatchEvent(e);
};
export default MsSource;

View File

@@ -39,99 +39,95 @@ import EventSource from '../pointer/EventSource.js';
* @constructor
* @extends {module:ol/pointer/EventSource}
*/
const NativeSource = function(dispatcher) {
const mapping = {
'pointerdown': this.pointerDown,
'pointermove': this.pointerMove,
'pointerup': this.pointerUp,
'pointerout': this.pointerOut,
'pointerover': this.pointerOver,
'pointercancel': this.pointerCancel,
'gotpointercapture': this.gotPointerCapture,
'lostpointercapture': this.lostPointerCapture
};
EventSource.call(this, dispatcher, mapping);
};
class NativeSource {
constructor(dispatcher) {
const mapping = {
'pointerdown': this.pointerDown,
'pointermove': this.pointerMove,
'pointerup': this.pointerUp,
'pointerout': this.pointerOut,
'pointerover': this.pointerOver,
'pointercancel': this.pointerCancel,
'gotpointercapture': this.gotPointerCapture,
'lostpointercapture': this.lostPointerCapture
};
EventSource.call(this, dispatcher, mapping);
}
/**
* Handler for `pointerdown`.
*
* @param {Event} inEvent The in event.
*/
pointerDown(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
}
/**
* Handler for `pointermove`.
*
* @param {Event} inEvent The in event.
*/
pointerMove(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
}
/**
* Handler for `pointerup`.
*
* @param {Event} inEvent The in event.
*/
pointerUp(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
}
/**
* Handler for `pointerout`.
*
* @param {Event} inEvent The in event.
*/
pointerOut(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
}
/**
* Handler for `pointerover`.
*
* @param {Event} inEvent The in event.
*/
pointerOver(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
}
/**
* Handler for `pointercancel`.
*
* @param {Event} inEvent The in event.
*/
pointerCancel(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
}
/**
* Handler for `lostpointercapture`.
*
* @param {Event} inEvent The in event.
*/
lostPointerCapture(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
}
/**
* Handler for `gotpointercapture`.
*
* @param {Event} inEvent The in event.
*/
gotPointerCapture(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
}
}
inherits(NativeSource, EventSource);
/**
* Handler for `pointerdown`.
*
* @param {Event} inEvent The in event.
*/
NativeSource.prototype.pointerDown = function(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
};
/**
* Handler for `pointermove`.
*
* @param {Event} inEvent The in event.
*/
NativeSource.prototype.pointerMove = function(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
};
/**
* Handler for `pointerup`.
*
* @param {Event} inEvent The in event.
*/
NativeSource.prototype.pointerUp = function(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
};
/**
* Handler for `pointerout`.
*
* @param {Event} inEvent The in event.
*/
NativeSource.prototype.pointerOut = function(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
};
/**
* Handler for `pointerover`.
*
* @param {Event} inEvent The in event.
*/
NativeSource.prototype.pointerOver = function(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
};
/**
* Handler for `pointercancel`.
*
* @param {Event} inEvent The in event.
*/
NativeSource.prototype.pointerCancel = function(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
};
/**
* Handler for `lostpointercapture`.
*
* @param {Event} inEvent The in event.
*/
NativeSource.prototype.lostPointerCapture = function(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
};
/**
* Handler for `gotpointercapture`.
*
* @param {Event} inEvent The in event.
*/
NativeSource.prototype.gotPointerCapture = function(inEvent) {
this.dispatcher.fireNativeEvent(inEvent);
};
export default NativeSource;

View File

@@ -47,150 +47,211 @@ import Event from '../events/Event.js';
* @param {Object.<string, ?>=} opt_eventDict An optional dictionary of
* initial event properties.
*/
const PointerEvent = function(type, originalEvent, opt_eventDict) {
Event.call(this, type);
class PointerEvent {
constructor(type, originalEvent, opt_eventDict) {
Event.call(this, type);
/**
* @const
* @type {Event}
*/
this.originalEvent = originalEvent;
/**
* @const
* @type {Event}
*/
this.originalEvent = originalEvent;
const eventDict = opt_eventDict ? opt_eventDict : {};
const eventDict = opt_eventDict ? opt_eventDict : {};
/**
* @type {number}
*/
this.buttons = this.getButtons_(eventDict);
/**
* @type {number}
*/
this.buttons = this.getButtons_(eventDict);
/**
* @type {number}
*/
this.pressure = this.getPressure_(eventDict, this.buttons);
/**
* @type {number}
*/
this.pressure = this.getPressure_(eventDict, this.buttons);
// MouseEvent related properties
// MouseEvent related properties
/**
* @type {boolean}
*/
this.bubbles = 'bubbles' in eventDict ? eventDict['bubbles'] : false;
/**
* @type {boolean}
*/
this.bubbles = 'bubbles' in eventDict ? eventDict['bubbles'] : false;
/**
* @type {boolean}
*/
this.cancelable = 'cancelable' in eventDict ? eventDict['cancelable'] : false;
/**
* @type {boolean}
*/
this.cancelable = 'cancelable' in eventDict ? eventDict['cancelable'] : false;
/**
* @type {Object}
*/
this.view = 'view' in eventDict ? eventDict['view'] : null;
/**
* @type {Object}
*/
this.view = 'view' in eventDict ? eventDict['view'] : null;
/**
* @type {number}
*/
this.detail = 'detail' in eventDict ? eventDict['detail'] : null;
/**
* @type {number}
*/
this.detail = 'detail' in eventDict ? eventDict['detail'] : null;
/**
* @type {number}
*/
this.screenX = 'screenX' in eventDict ? eventDict['screenX'] : 0;
/**
* @type {number}
*/
this.screenX = 'screenX' in eventDict ? eventDict['screenX'] : 0;
/**
* @type {number}
*/
this.screenY = 'screenY' in eventDict ? eventDict['screenY'] : 0;
/**
* @type {number}
*/
this.screenY = 'screenY' in eventDict ? eventDict['screenY'] : 0;
/**
* @type {number}
*/
this.clientX = 'clientX' in eventDict ? eventDict['clientX'] : 0;
/**
* @type {number}
*/
this.clientX = 'clientX' in eventDict ? eventDict['clientX'] : 0;
/**
* @type {number}
*/
this.clientY = 'clientY' in eventDict ? eventDict['clientY'] : 0;
/**
* @type {number}
*/
this.clientY = 'clientY' in eventDict ? eventDict['clientY'] : 0;
/**
* @type {boolean}
*/
this.ctrlKey = 'ctrlKey' in eventDict ? eventDict['ctrlKey'] : false;
/**
* @type {boolean}
*/
this.ctrlKey = 'ctrlKey' in eventDict ? eventDict['ctrlKey'] : false;
/**
* @type {boolean}
*/
this.altKey = 'altKey' in eventDict ? eventDict['altKey'] : false;
/**
* @type {boolean}
*/
this.altKey = 'altKey' in eventDict ? eventDict['altKey'] : false;
/**
* @type {boolean}
*/
this.shiftKey = 'shiftKey' in eventDict ? eventDict['shiftKey'] : false;
/**
* @type {boolean}
*/
this.shiftKey = 'shiftKey' in eventDict ? eventDict['shiftKey'] : false;
/**
* @type {boolean}
*/
this.metaKey = 'metaKey' in eventDict ? eventDict['metaKey'] : false;
/**
* @type {boolean}
*/
this.metaKey = 'metaKey' in eventDict ? eventDict['metaKey'] : false;
/**
* @type {number}
*/
this.button = 'button' in eventDict ? eventDict['button'] : 0;
/**
* @type {number}
*/
this.button = 'button' in eventDict ? eventDict['button'] : 0;
/**
* @type {Node}
*/
this.relatedTarget = 'relatedTarget' in eventDict ?
eventDict['relatedTarget'] : null;
/**
* @type {Node}
*/
this.relatedTarget = 'relatedTarget' in eventDict ?
eventDict['relatedTarget'] : null;
// PointerEvent related properties
// PointerEvent related properties
/**
* @const
* @type {number}
*/
this.pointerId = 'pointerId' in eventDict ? eventDict['pointerId'] : 0;
/**
* @const
* @type {number}
*/
this.pointerId = 'pointerId' in eventDict ? eventDict['pointerId'] : 0;
/**
* @type {number}
*/
this.width = 'width' in eventDict ? eventDict['width'] : 0;
/**
* @type {number}
*/
this.width = 'width' in eventDict ? eventDict['width'] : 0;
/**
* @type {number}
*/
this.height = 'height' in eventDict ? eventDict['height'] : 0;
/**
* @type {number}
*/
this.height = 'height' in eventDict ? eventDict['height'] : 0;
/**
* @type {number}
*/
this.tiltX = 'tiltX' in eventDict ? eventDict['tiltX'] : 0;
/**
* @type {number}
*/
this.tiltX = 'tiltX' in eventDict ? eventDict['tiltX'] : 0;
/**
* @type {number}
*/
this.tiltY = 'tiltY' in eventDict ? eventDict['tiltY'] : 0;
/**
* @type {number}
*/
this.tiltY = 'tiltY' in eventDict ? eventDict['tiltY'] : 0;
/**
* @type {string}
*/
this.pointerType = 'pointerType' in eventDict ? eventDict['pointerType'] : '';
/**
* @type {string}
*/
this.pointerType = 'pointerType' in eventDict ? eventDict['pointerType'] : '';
/**
* @type {number}
*/
this.hwTimestamp = 'hwTimestamp' in eventDict ? eventDict['hwTimestamp'] : 0;
/**
* @type {number}
*/
this.hwTimestamp = 'hwTimestamp' in eventDict ? eventDict['hwTimestamp'] : 0;
/**
* @type {boolean}
*/
this.isPrimary = 'isPrimary' in eventDict ? eventDict['isPrimary'] : false;
/**
* @type {boolean}
*/
this.isPrimary = 'isPrimary' in eventDict ? eventDict['isPrimary'] : false;
// keep the semantics of preventDefault
if (originalEvent.preventDefault) {
this.preventDefault = function() {
originalEvent.preventDefault();
};
}
};
// keep the semantics of preventDefault
if (originalEvent.preventDefault) {
this.preventDefault = function() {
originalEvent.preventDefault();
};
}
}
/**
* @private
* @param {Object.<string, ?>} eventDict The event dictionary.
* @return {number} Button indicator.
*/
getButtons_(eventDict) {
// According to the w3c spec,
// http://www.w3.org/TR/DOM-Level-3-Events/#events-MouseEvent-button
// MouseEvent.button == 0 can mean either no mouse button depressed, or the
// left mouse button depressed.
//
// As of now, the only way to distinguish between the two states of
// MouseEvent.button is by using the deprecated MouseEvent.which property, as
// this maps mouse buttons to positive integers > 0, and uses 0 to mean that
// no mouse button is held.
//
// MouseEvent.which is derived from MouseEvent.button at MouseEvent creation,
// but initMouseEvent does not expose an argument with which to set
// MouseEvent.which. Calling initMouseEvent with a buttonArg of 0 will set
// MouseEvent.button == 0 and MouseEvent.which == 1, breaking the expectations
// of app developers.
//
// The only way to propagate the correct state of MouseEvent.which and
// MouseEvent.button to a new MouseEvent.button == 0 and MouseEvent.which == 0
// is to call initMouseEvent with a buttonArg value of -1.
//
// This is fixed with DOM Level 4's use of buttons
let buttons;
if (eventDict.buttons || HAS_BUTTONS) {
buttons = eventDict.buttons;
} else {
switch (eventDict.which) {
case 1: buttons = 1; break;
case 2: buttons = 4; break;
case 3: buttons = 2; break;
default: buttons = 0;
}
}
return buttons;
}
/**
* @private
* @param {Object.<string, ?>} eventDict The event dictionary.
* @param {number} buttons Button indicator.
* @return {number} The pressure.
*/
getPressure_(eventDict, buttons) {
// Spec requires that pointers without pressure specified use 0.5 for down
// state and 0 for up state.
let pressure = 0;
if (eventDict.pressure) {
pressure = eventDict.pressure;
} else {
pressure = buttons ? 0.5 : 0;
}
return pressure;
}
}
inherits(PointerEvent, Event);
@@ -202,67 +263,6 @@ inherits(PointerEvent, Event);
let HAS_BUTTONS = false;
/**
* @private
* @param {Object.<string, ?>} eventDict The event dictionary.
* @return {number} Button indicator.
*/
PointerEvent.prototype.getButtons_ = function(eventDict) {
// According to the w3c spec,
// http://www.w3.org/TR/DOM-Level-3-Events/#events-MouseEvent-button
// MouseEvent.button == 0 can mean either no mouse button depressed, or the
// left mouse button depressed.
//
// As of now, the only way to distinguish between the two states of
// MouseEvent.button is by using the deprecated MouseEvent.which property, as
// this maps mouse buttons to positive integers > 0, and uses 0 to mean that
// no mouse button is held.
//
// MouseEvent.which is derived from MouseEvent.button at MouseEvent creation,
// but initMouseEvent does not expose an argument with which to set
// MouseEvent.which. Calling initMouseEvent with a buttonArg of 0 will set
// MouseEvent.button == 0 and MouseEvent.which == 1, breaking the expectations
// of app developers.
//
// The only way to propagate the correct state of MouseEvent.which and
// MouseEvent.button to a new MouseEvent.button == 0 and MouseEvent.which == 0
// is to call initMouseEvent with a buttonArg value of -1.
//
// This is fixed with DOM Level 4's use of buttons
let buttons;
if (eventDict.buttons || HAS_BUTTONS) {
buttons = eventDict.buttons;
} else {
switch (eventDict.which) {
case 1: buttons = 1; break;
case 2: buttons = 4; break;
case 3: buttons = 2; break;
default: buttons = 0;
}
}
return buttons;
};
/**
* @private
* @param {Object.<string, ?>} eventDict The event dictionary.
* @param {number} buttons Button indicator.
* @return {number} The pressure.
*/
PointerEvent.prototype.getPressure_ = function(eventDict, buttons) {
// Spec requires that pointers without pressure specified use 0.5 for down
// state and 0 for up state.
let pressure = 0;
if (eventDict.pressure) {
pressure = eventDict.pressure;
} else {
pressure = buttons ? 0.5 : 0;
}
return pressure;
};
/**
* Checks if the `buttons` property is supported.
*/

View File

@@ -47,36 +47,333 @@ import TouchSource from '../pointer/TouchSource.js';
* @extends {module:ol/events/EventTarget}
* @param {Element|HTMLDocument} element Viewport element.
*/
const PointerEventHandler = function(element) {
EventTarget.call(this);
class PointerEventHandler {
constructor(element) {
EventTarget.call(this);
/**
* @const
* @private
* @type {Element|HTMLDocument}
*/
this.element_ = element;
/**
* @const
* @type {!Object.<string, Event|Object>}
*/
this.pointerMap = {};
/**
* @type {Object.<string, function(Event)>}
* @private
*/
this.eventMap_ = {};
/**
* @type {Array.<module:ol/pointer/EventSource>}
* @private
*/
this.eventSourceList_ = [];
this.registerSources();
}
/**
* @const
* @private
* @type {Element|HTMLDocument}
* Set up the event sources (mouse, touch and native pointers)
* that generate pointer events.
*/
this.element_ = element;
registerSources() {
if (POINTER) {
this.registerSource('native', new NativeSource(this));
} else if (MSPOINTER) {
this.registerSource('ms', new MsSource(this));
} else {
const mouseSource = new MouseSource(this);
this.registerSource('mouse', mouseSource);
if (TOUCH) {
this.registerSource('touch', new TouchSource(this, mouseSource));
}
}
// register events on the viewport element
this.register_();
}
/**
* @const
* @type {!Object.<string, Event|Object>}
* Add a new event source that will generate pointer events.
*
* @param {string} name A name for the event source
* @param {module:ol/pointer/EventSource} source The source event.
*/
this.pointerMap = {};
registerSource(name, source) {
const s = source;
const newEvents = s.getEvents();
if (newEvents) {
newEvents.forEach(function(e) {
const handler = s.getHandlerForEvent(e);
if (handler) {
this.eventMap_[e] = handler.bind(s);
}
}.bind(this));
this.eventSourceList_.push(s);
}
}
/**
* @type {Object.<string, function(Event)>}
* Set up the events for all registered event sources.
* @private
*/
this.eventMap_ = {};
register_() {
const l = this.eventSourceList_.length;
for (let i = 0; i < l; i++) {
const eventSource = this.eventSourceList_[i];
this.addEvents_(eventSource.getEvents());
}
}
/**
* @type {Array.<module:ol/pointer/EventSource>}
* Remove all registered events.
* @private
*/
this.eventSourceList_ = [];
unregister_() {
const l = this.eventSourceList_.length;
for (let i = 0; i < l; i++) {
const eventSource = this.eventSourceList_[i];
this.removeEvents_(eventSource.getEvents());
}
}
this.registerSources();
};
/**
* Calls the right handler for a new event.
* @private
* @param {Event} inEvent Browser event.
*/
eventHandler_(inEvent) {
const type = inEvent.type;
const handler = this.eventMap_[type];
if (handler) {
handler(inEvent);
}
}
/**
* Setup listeners for the given events.
* @private
* @param {Array.<string>} events List of events.
*/
addEvents_(events) {
events.forEach(function(eventName) {
listen(this.element_, eventName, this.eventHandler_, this);
}.bind(this));
}
/**
* Unregister listeners for the given events.
* @private
* @param {Array.<string>} events List of events.
*/
removeEvents_(events) {
events.forEach(function(e) {
unlisten(this.element_, e, this.eventHandler_, this);
}.bind(this));
}
/**
* Returns a snapshot of inEvent, with writable properties.
*
* @param {Event} event Browser event.
* @param {Event|Touch} inEvent An event that contains
* properties to copy.
* @return {Object} An object containing shallow copies of
* `inEvent`'s properties.
*/
cloneEvent(event, inEvent) {
const eventCopy = {};
for (let i = 0, ii = CLONE_PROPS.length; i < ii; i++) {
const p = CLONE_PROPS[i][0];
eventCopy[p] = event[p] || inEvent[p] || CLONE_PROPS[i][1];
}
return eventCopy;
}
// EVENTS
/**
* Triggers a 'pointerdown' event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
down(data, event) {
this.fireEvent(PointerEventType.POINTERDOWN, data, event);
}
/**
* Triggers a 'pointermove' event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
move(data, event) {
this.fireEvent(PointerEventType.POINTERMOVE, data, event);
}
/**
* Triggers a 'pointerup' event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
up(data, event) {
this.fireEvent(PointerEventType.POINTERUP, data, event);
}
/**
* Triggers a 'pointerenter' event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
enter(data, event) {
data.bubbles = false;
this.fireEvent(PointerEventType.POINTERENTER, data, event);
}
/**
* Triggers a 'pointerleave' event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
leave(data, event) {
data.bubbles = false;
this.fireEvent(PointerEventType.POINTERLEAVE, data, event);
}
/**
* Triggers a 'pointerover' event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
over(data, event) {
data.bubbles = true;
this.fireEvent(PointerEventType.POINTEROVER, data, event);
}
/**
* Triggers a 'pointerout' event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
out(data, event) {
data.bubbles = true;
this.fireEvent(PointerEventType.POINTEROUT, data, event);
}
/**
* Triggers a 'pointercancel' event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
cancel(data, event) {
this.fireEvent(PointerEventType.POINTERCANCEL, data, event);
}
/**
* Triggers a combination of 'pointerout' and 'pointerleave' events.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
leaveOut(data, event) {
this.out(data, event);
if (!this.contains_(data.target, data.relatedTarget)) {
this.leave(data, event);
}
}
/**
* Triggers a combination of 'pointerover' and 'pointerevents' events.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
enterOver(data, event) {
this.over(data, event);
if (!this.contains_(data.target, data.relatedTarget)) {
this.enter(data, event);
}
}
/**
* @private
* @param {Element} container The container element.
* @param {Element} contained The contained element.
* @return {boolean} Returns true if the container element
* contains the other element.
*/
contains_(container, contained) {
if (!container || !contained) {
return false;
}
return container.contains(contained);
}
// EVENT CREATION AND TRACKING
/**
* Creates a new Event of type `inType`, based on the information in
* `data`.
*
* @param {string} inType A string representing the type of event to create.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
* @return {module:ol/pointer/PointerEvent} A PointerEvent of type `inType`.
*/
makeEvent(inType, data, event) {
return new PointerEvent(inType, event, data);
}
/**
* Make and dispatch an event in one call.
* @param {string} inType A string representing the type of event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
fireEvent(inType, data, event) {
const e = this.makeEvent(inType, data, event);
this.dispatchEvent(e);
}
/**
* Creates a pointer event from a native pointer event
* and dispatches this event.
* @param {Event} event A platform event with a target.
*/
fireNativeEvent(event) {
const e = this.makeEvent(event.type, event, event);
this.dispatchEvent(e);
}
/**
* Wrap a native mouse event into a pointer event.
* This proxy method is required for the legacy IE support.
* @param {string} eventType The pointer event type.
* @param {Event} event The event.
* @return {module:ol/pointer/PointerEvent} The wrapped event.
*/
wrapMouseEvent(eventType, event) {
const pointerEvent = this.makeEvent(
eventType, MouseSource.prepareEvent(event, this), event);
return pointerEvent;
}
/**
* @inheritDoc
*/
disposeInternal() {
this.unregister_();
EventTarget.prototype.disposeInternal.call(this);
}
}
inherits(PointerEventHandler, EventTarget);
@@ -120,323 +417,4 @@ const CLONE_PROPS = [
];
/**
* Set up the event sources (mouse, touch and native pointers)
* that generate pointer events.
*/
PointerEventHandler.prototype.registerSources = function() {
if (POINTER) {
this.registerSource('native', new NativeSource(this));
} else if (MSPOINTER) {
this.registerSource('ms', new MsSource(this));
} else {
const mouseSource = new MouseSource(this);
this.registerSource('mouse', mouseSource);
if (TOUCH) {
this.registerSource('touch', new TouchSource(this, mouseSource));
}
}
// register events on the viewport element
this.register_();
};
/**
* Add a new event source that will generate pointer events.
*
* @param {string} name A name for the event source
* @param {module:ol/pointer/EventSource} source The source event.
*/
PointerEventHandler.prototype.registerSource = function(name, source) {
const s = source;
const newEvents = s.getEvents();
if (newEvents) {
newEvents.forEach(function(e) {
const handler = s.getHandlerForEvent(e);
if (handler) {
this.eventMap_[e] = handler.bind(s);
}
}.bind(this));
this.eventSourceList_.push(s);
}
};
/**
* Set up the events for all registered event sources.
* @private
*/
PointerEventHandler.prototype.register_ = function() {
const l = this.eventSourceList_.length;
for (let i = 0; i < l; i++) {
const eventSource = this.eventSourceList_[i];
this.addEvents_(eventSource.getEvents());
}
};
/**
* Remove all registered events.
* @private
*/
PointerEventHandler.prototype.unregister_ = function() {
const l = this.eventSourceList_.length;
for (let i = 0; i < l; i++) {
const eventSource = this.eventSourceList_[i];
this.removeEvents_(eventSource.getEvents());
}
};
/**
* Calls the right handler for a new event.
* @private
* @param {Event} inEvent Browser event.
*/
PointerEventHandler.prototype.eventHandler_ = function(inEvent) {
const type = inEvent.type;
const handler = this.eventMap_[type];
if (handler) {
handler(inEvent);
}
};
/**
* Setup listeners for the given events.
* @private
* @param {Array.<string>} events List of events.
*/
PointerEventHandler.prototype.addEvents_ = function(events) {
events.forEach(function(eventName) {
listen(this.element_, eventName, this.eventHandler_, this);
}.bind(this));
};
/**
* Unregister listeners for the given events.
* @private
* @param {Array.<string>} events List of events.
*/
PointerEventHandler.prototype.removeEvents_ = function(events) {
events.forEach(function(e) {
unlisten(this.element_, e, this.eventHandler_, this);
}.bind(this));
};
/**
* Returns a snapshot of inEvent, with writable properties.
*
* @param {Event} event Browser event.
* @param {Event|Touch} inEvent An event that contains
* properties to copy.
* @return {Object} An object containing shallow copies of
* `inEvent`'s properties.
*/
PointerEventHandler.prototype.cloneEvent = function(event, inEvent) {
const eventCopy = {};
for (let i = 0, ii = CLONE_PROPS.length; i < ii; i++) {
const p = CLONE_PROPS[i][0];
eventCopy[p] = event[p] || inEvent[p] || CLONE_PROPS[i][1];
}
return eventCopy;
};
// EVENTS
/**
* Triggers a 'pointerdown' event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
PointerEventHandler.prototype.down = function(data, event) {
this.fireEvent(PointerEventType.POINTERDOWN, data, event);
};
/**
* Triggers a 'pointermove' event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
PointerEventHandler.prototype.move = function(data, event) {
this.fireEvent(PointerEventType.POINTERMOVE, data, event);
};
/**
* Triggers a 'pointerup' event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
PointerEventHandler.prototype.up = function(data, event) {
this.fireEvent(PointerEventType.POINTERUP, data, event);
};
/**
* Triggers a 'pointerenter' event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
PointerEventHandler.prototype.enter = function(data, event) {
data.bubbles = false;
this.fireEvent(PointerEventType.POINTERENTER, data, event);
};
/**
* Triggers a 'pointerleave' event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
PointerEventHandler.prototype.leave = function(data, event) {
data.bubbles = false;
this.fireEvent(PointerEventType.POINTERLEAVE, data, event);
};
/**
* Triggers a 'pointerover' event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
PointerEventHandler.prototype.over = function(data, event) {
data.bubbles = true;
this.fireEvent(PointerEventType.POINTEROVER, data, event);
};
/**
* Triggers a 'pointerout' event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
PointerEventHandler.prototype.out = function(data, event) {
data.bubbles = true;
this.fireEvent(PointerEventType.POINTEROUT, data, event);
};
/**
* Triggers a 'pointercancel' event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
PointerEventHandler.prototype.cancel = function(data, event) {
this.fireEvent(PointerEventType.POINTERCANCEL, data, event);
};
/**
* Triggers a combination of 'pointerout' and 'pointerleave' events.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
PointerEventHandler.prototype.leaveOut = function(data, event) {
this.out(data, event);
if (!this.contains_(data.target, data.relatedTarget)) {
this.leave(data, event);
}
};
/**
* Triggers a combination of 'pointerover' and 'pointerevents' events.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
PointerEventHandler.prototype.enterOver = function(data, event) {
this.over(data, event);
if (!this.contains_(data.target, data.relatedTarget)) {
this.enter(data, event);
}
};
/**
* @private
* @param {Element} container The container element.
* @param {Element} contained The contained element.
* @return {boolean} Returns true if the container element
* contains the other element.
*/
PointerEventHandler.prototype.contains_ = function(container, contained) {
if (!container || !contained) {
return false;
}
return container.contains(contained);
};
// EVENT CREATION AND TRACKING
/**
* Creates a new Event of type `inType`, based on the information in
* `data`.
*
* @param {string} inType A string representing the type of event to create.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
* @return {module:ol/pointer/PointerEvent} A PointerEvent of type `inType`.
*/
PointerEventHandler.prototype.makeEvent = function(inType, data, event) {
return new PointerEvent(inType, event, data);
};
/**
* Make and dispatch an event in one call.
* @param {string} inType A string representing the type of event.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
*/
PointerEventHandler.prototype.fireEvent = function(inType, data, event) {
const e = this.makeEvent(inType, data, event);
this.dispatchEvent(e);
};
/**
* Creates a pointer event from a native pointer event
* and dispatches this event.
* @param {Event} event A platform event with a target.
*/
PointerEventHandler.prototype.fireNativeEvent = function(event) {
const e = this.makeEvent(event.type, event, event);
this.dispatchEvent(e);
};
/**
* Wrap a native mouse event into a pointer event.
* This proxy method is required for the legacy IE support.
* @param {string} eventType The pointer event type.
* @param {Event} event The event.
* @return {module:ol/pointer/PointerEvent} The wrapped event.
*/
PointerEventHandler.prototype.wrapMouseEvent = function(eventType, event) {
const pointerEvent = this.makeEvent(
eventType, MouseSource.prepareEvent(event, this), event);
return pointerEvent;
};
/**
* @inheritDoc
*/
PointerEventHandler.prototype.disposeInternal = function() {
this.unregister_();
EventTarget.prototype.disposeInternal.call(this);
};
export default PointerEventHandler;

View File

@@ -43,53 +43,370 @@ import {POINTER_ID} from '../pointer/MouseSource.js';
* @param {module:ol/pointer/MouseSource} mouseSource Mouse source.
* @extends {module:ol/pointer/EventSource}
*/
const TouchSource = function(dispatcher, mouseSource) {
const mapping = {
'touchstart': this.touchstart,
'touchmove': this.touchmove,
'touchend': this.touchend,
'touchcancel': this.touchcancel
};
EventSource.call(this, dispatcher, mapping);
class TouchSource {
constructor(dispatcher, mouseSource) {
const mapping = {
'touchstart': this.touchstart,
'touchmove': this.touchmove,
'touchend': this.touchend,
'touchcancel': this.touchcancel
};
EventSource.call(this, dispatcher, mapping);
/**
* @const
* @type {!Object.<string, Event|Object>}
*/
this.pointerMap = dispatcher.pointerMap;
/**
* @const
* @type {!Object.<string, Event|Object>}
*/
this.pointerMap = dispatcher.pointerMap;
/**
* @const
* @type {module:ol/pointer/MouseSource}
*/
this.mouseSource = mouseSource;
/**
* @const
* @type {module:ol/pointer/MouseSource}
*/
this.mouseSource = mouseSource;
/**
* @private
* @type {number|undefined}
*/
this.firstTouchId_ = undefined;
/**
* @private
* @type {number}
*/
this.clickCount_ = 0;
/**
* @private
* @type {number|undefined}
*/
this.resetId_ = undefined;
/**
* Mouse event timeout: This should be long enough to
* ignore compat mouse events made by touch.
* @private
* @type {number}
*/
this.dedupTimeout_ = 2500;
}
/**
* @private
* @type {number|undefined}
* @param {Touch} inTouch The in touch.
* @return {boolean} True, if this is the primary touch.
*/
this.firstTouchId_ = undefined;
isPrimaryTouch_(inTouch) {
return this.firstTouchId_ === inTouch.identifier;
}
/**
* Set primary touch if there are no pointers, or the only pointer is the mouse.
* @param {Touch} inTouch The in touch.
* @private
*/
setPrimaryTouch_(inTouch) {
const count = Object.keys(this.pointerMap).length;
if (count === 0 || (count === 1 && POINTER_ID.toString() in this.pointerMap)) {
this.firstTouchId_ = inTouch.identifier;
this.cancelResetClickCount_();
}
}
/**
* @private
* @type {number}
* @param {PointerEvent} inPointer The in pointer object.
*/
this.clickCount_ = 0;
removePrimaryPointer_(inPointer) {
if (inPointer.isPrimary) {
this.firstTouchId_ = undefined;
this.resetClickCount_();
}
}
/**
* @private
* @type {number|undefined}
*/
this.resetId_ = undefined;
resetClickCount_() {
this.resetId_ = setTimeout(
this.resetClickCountHandler_.bind(this),
CLICK_COUNT_TIMEOUT);
}
/**
* Mouse event timeout: This should be long enough to
* ignore compat mouse events made by touch.
* @private
* @type {number}
*/
this.dedupTimeout_ = 2500;
};
resetClickCountHandler_() {
this.clickCount_ = 0;
this.resetId_ = undefined;
}
/**
* @private
*/
cancelResetClickCount_() {
if (this.resetId_ !== undefined) {
clearTimeout(this.resetId_);
}
}
/**
* @private
* @param {TouchEvent} browserEvent Browser event
* @param {Touch} inTouch Touch event
* @return {PointerEvent} A pointer object.
*/
touchToPointer_(browserEvent, inTouch) {
const e = this.dispatcher.cloneEvent(browserEvent, inTouch);
// Spec specifies that pointerId 1 is reserved for Mouse.
// Touch identifiers can start at 0.
// Add 2 to the touch identifier for compatibility.
e.pointerId = inTouch.identifier + 2;
// TODO: check if this is necessary?
//e.target = findTarget(e);
e.bubbles = true;
e.cancelable = true;
e.detail = this.clickCount_;
e.button = 0;
e.buttons = 1;
e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0;
e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0;
e.pressure = inTouch.webkitForce || inTouch.force || 0.5;
e.isPrimary = this.isPrimaryTouch_(inTouch);
e.pointerType = POINTER_TYPE;
// make sure that the properties that are different for
// each `Touch` object are not copied from the BrowserEvent object
e.clientX = inTouch.clientX;
e.clientY = inTouch.clientY;
e.screenX = inTouch.screenX;
e.screenY = inTouch.screenY;
return e;
}
/**
* @private
* @param {TouchEvent} inEvent Touch event
* @param {function(TouchEvent, PointerEvent)} inFunction In function.
*/
processTouches_(inEvent, inFunction) {
const touches = Array.prototype.slice.call(inEvent.changedTouches);
const count = touches.length;
function preventDefault() {
inEvent.preventDefault();
}
for (let i = 0; i < count; ++i) {
const pointer = this.touchToPointer_(inEvent, touches[i]);
// forward touch preventDefaults
pointer.preventDefault = preventDefault;
inFunction.call(this, inEvent, pointer);
}
}
/**
* @private
* @param {TouchList} touchList The touch list.
* @param {number} searchId Search identifier.
* @return {boolean} True, if the `Touch` with the given id is in the list.
*/
findTouch_(touchList, searchId) {
const l = touchList.length;
for (let i = 0; i < l; i++) {
const touch = touchList[i];
if (touch.identifier === searchId) {
return true;
}
}
return false;
}
/**
* In some instances, a touchstart can happen without a touchend. This
* leaves the pointermap in a broken state.
* Therefore, on every touchstart, we remove the touches that did not fire a
* touchend event.
* To keep state globally consistent, we fire a pointercancel for
* this "abandoned" touch
*
* @private
* @param {TouchEvent} inEvent The in event.
*/
vacuumTouches_(inEvent) {
const touchList = inEvent.touches;
// pointerMap.getCount() should be < touchList.length here,
// as the touchstart has not been processed yet.
const keys = Object.keys(this.pointerMap);
const count = keys.length;
if (count >= touchList.length) {
const d = [];
for (let i = 0; i < count; ++i) {
const key = keys[i];
const value = this.pointerMap[key];
// Never remove pointerId == 1, which is mouse.
// Touch identifiers are 2 smaller than their pointerId, which is the
// index in pointermap.
if (key != POINTER_ID && !this.findTouch_(touchList, key - 2)) {
d.push(value.out);
}
}
for (let i = 0; i < d.length; ++i) {
this.cancelOut_(inEvent, d[i]);
}
}
}
/**
* Handler for `touchstart`, triggers `pointerover`,
* `pointerenter` and `pointerdown` events.
*
* @param {TouchEvent} inEvent The in event.
*/
touchstart(inEvent) {
this.vacuumTouches_(inEvent);
this.setPrimaryTouch_(inEvent.changedTouches[0]);
this.dedupSynthMouse_(inEvent);
this.clickCount_++;
this.processTouches_(inEvent, this.overDown_);
}
/**
* @private
* @param {TouchEvent} browserEvent The event.
* @param {PointerEvent} inPointer The in pointer object.
*/
overDown_(browserEvent, inPointer) {
this.pointerMap[inPointer.pointerId] = {
target: inPointer.target,
out: inPointer,
outTarget: inPointer.target
};
this.dispatcher.over(inPointer, browserEvent);
this.dispatcher.enter(inPointer, browserEvent);
this.dispatcher.down(inPointer, browserEvent);
}
/**
* Handler for `touchmove`.
*
* @param {TouchEvent} inEvent The in event.
*/
touchmove(inEvent) {
inEvent.preventDefault();
this.processTouches_(inEvent, this.moveOverOut_);
}
/**
* @private
* @param {TouchEvent} browserEvent The event.
* @param {PointerEvent} inPointer The in pointer.
*/
moveOverOut_(browserEvent, inPointer) {
const event = inPointer;
const pointer = this.pointerMap[event.pointerId];
// a finger drifted off the screen, ignore it
if (!pointer) {
return;
}
const outEvent = pointer.out;
const outTarget = pointer.outTarget;
this.dispatcher.move(event, browserEvent);
if (outEvent && outTarget !== event.target) {
outEvent.relatedTarget = event.target;
event.relatedTarget = outTarget;
// recover from retargeting by shadow
outEvent.target = outTarget;
if (event.target) {
this.dispatcher.leaveOut(outEvent, browserEvent);
this.dispatcher.enterOver(event, browserEvent);
} else {
// clean up case when finger leaves the screen
event.target = outTarget;
event.relatedTarget = null;
this.cancelOut_(browserEvent, event);
}
}
pointer.out = event;
pointer.outTarget = event.target;
}
/**
* Handler for `touchend`, triggers `pointerup`,
* `pointerout` and `pointerleave` events.
*
* @param {TouchEvent} inEvent The event.
*/
touchend(inEvent) {
this.dedupSynthMouse_(inEvent);
this.processTouches_(inEvent, this.upOut_);
}
/**
* @private
* @param {TouchEvent} browserEvent An event.
* @param {PointerEvent} inPointer The inPointer object.
*/
upOut_(browserEvent, inPointer) {
this.dispatcher.up(inPointer, browserEvent);
this.dispatcher.out(inPointer, browserEvent);
this.dispatcher.leave(inPointer, browserEvent);
this.cleanUpPointer_(inPointer);
}
/**
* Handler for `touchcancel`, triggers `pointercancel`,
* `pointerout` and `pointerleave` events.
*
* @param {TouchEvent} inEvent The in event.
*/
touchcancel(inEvent) {
this.processTouches_(inEvent, this.cancelOut_);
}
/**
* @private
* @param {TouchEvent} browserEvent The event.
* @param {PointerEvent} inPointer The in pointer.
*/
cancelOut_(browserEvent, inPointer) {
this.dispatcher.cancel(inPointer, browserEvent);
this.dispatcher.out(inPointer, browserEvent);
this.dispatcher.leave(inPointer, browserEvent);
this.cleanUpPointer_(inPointer);
}
/**
* @private
* @param {PointerEvent} inPointer The inPointer object.
*/
cleanUpPointer_(inPointer) {
delete this.pointerMap[inPointer.pointerId];
this.removePrimaryPointer_(inPointer);
}
/**
* Prevent synth mouse events from creating pointer events.
*
* @private
* @param {TouchEvent} inEvent The in event.
*/
dedupSynthMouse_(inEvent) {
const lts = this.mouseSource.lastTouches;
const t = inEvent.changedTouches[0];
// only the primary finger will synth mouse events
if (this.isPrimaryTouch_(t)) {
// remember x/y of last touch
const lt = [t.clientX, t.clientY];
lts.push(lt);
setTimeout(function() {
// remove touch after timeout
remove(lts, lt);
}, this.dedupTimeout_);
}
}
}
inherits(TouchSource, EventSource);
@@ -105,337 +422,4 @@ const CLICK_COUNT_TIMEOUT = 200;
*/
const POINTER_TYPE = 'touch';
/**
* @private
* @param {Touch} inTouch The in touch.
* @return {boolean} True, if this is the primary touch.
*/
TouchSource.prototype.isPrimaryTouch_ = function(inTouch) {
return this.firstTouchId_ === inTouch.identifier;
};
/**
* Set primary touch if there are no pointers, or the only pointer is the mouse.
* @param {Touch} inTouch The in touch.
* @private
*/
TouchSource.prototype.setPrimaryTouch_ = function(inTouch) {
const count = Object.keys(this.pointerMap).length;
if (count === 0 || (count === 1 && POINTER_ID.toString() in this.pointerMap)) {
this.firstTouchId_ = inTouch.identifier;
this.cancelResetClickCount_();
}
};
/**
* @private
* @param {PointerEvent} inPointer The in pointer object.
*/
TouchSource.prototype.removePrimaryPointer_ = function(inPointer) {
if (inPointer.isPrimary) {
this.firstTouchId_ = undefined;
this.resetClickCount_();
}
};
/**
* @private
*/
TouchSource.prototype.resetClickCount_ = function() {
this.resetId_ = setTimeout(
this.resetClickCountHandler_.bind(this),
CLICK_COUNT_TIMEOUT);
};
/**
* @private
*/
TouchSource.prototype.resetClickCountHandler_ = function() {
this.clickCount_ = 0;
this.resetId_ = undefined;
};
/**
* @private
*/
TouchSource.prototype.cancelResetClickCount_ = function() {
if (this.resetId_ !== undefined) {
clearTimeout(this.resetId_);
}
};
/**
* @private
* @param {TouchEvent} browserEvent Browser event
* @param {Touch} inTouch Touch event
* @return {PointerEvent} A pointer object.
*/
TouchSource.prototype.touchToPointer_ = function(browserEvent, inTouch) {
const e = this.dispatcher.cloneEvent(browserEvent, inTouch);
// Spec specifies that pointerId 1 is reserved for Mouse.
// Touch identifiers can start at 0.
// Add 2 to the touch identifier for compatibility.
e.pointerId = inTouch.identifier + 2;
// TODO: check if this is necessary?
//e.target = findTarget(e);
e.bubbles = true;
e.cancelable = true;
e.detail = this.clickCount_;
e.button = 0;
e.buttons = 1;
e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0;
e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0;
e.pressure = inTouch.webkitForce || inTouch.force || 0.5;
e.isPrimary = this.isPrimaryTouch_(inTouch);
e.pointerType = POINTER_TYPE;
// make sure that the properties that are different for
// each `Touch` object are not copied from the BrowserEvent object
e.clientX = inTouch.clientX;
e.clientY = inTouch.clientY;
e.screenX = inTouch.screenX;
e.screenY = inTouch.screenY;
return e;
};
/**
* @private
* @param {TouchEvent} inEvent Touch event
* @param {function(TouchEvent, PointerEvent)} inFunction In function.
*/
TouchSource.prototype.processTouches_ = function(inEvent, inFunction) {
const touches = Array.prototype.slice.call(inEvent.changedTouches);
const count = touches.length;
function preventDefault() {
inEvent.preventDefault();
}
for (let i = 0; i < count; ++i) {
const pointer = this.touchToPointer_(inEvent, touches[i]);
// forward touch preventDefaults
pointer.preventDefault = preventDefault;
inFunction.call(this, inEvent, pointer);
}
};
/**
* @private
* @param {TouchList} touchList The touch list.
* @param {number} searchId Search identifier.
* @return {boolean} True, if the `Touch` with the given id is in the list.
*/
TouchSource.prototype.findTouch_ = function(touchList, searchId) {
const l = touchList.length;
for (let i = 0; i < l; i++) {
const touch = touchList[i];
if (touch.identifier === searchId) {
return true;
}
}
return false;
};
/**
* In some instances, a touchstart can happen without a touchend. This
* leaves the pointermap in a broken state.
* Therefore, on every touchstart, we remove the touches that did not fire a
* touchend event.
* To keep state globally consistent, we fire a pointercancel for
* this "abandoned" touch
*
* @private
* @param {TouchEvent} inEvent The in event.
*/
TouchSource.prototype.vacuumTouches_ = function(inEvent) {
const touchList = inEvent.touches;
// pointerMap.getCount() should be < touchList.length here,
// as the touchstart has not been processed yet.
const keys = Object.keys(this.pointerMap);
const count = keys.length;
if (count >= touchList.length) {
const d = [];
for (let i = 0; i < count; ++i) {
const key = keys[i];
const value = this.pointerMap[key];
// Never remove pointerId == 1, which is mouse.
// Touch identifiers are 2 smaller than their pointerId, which is the
// index in pointermap.
if (key != POINTER_ID && !this.findTouch_(touchList, key - 2)) {
d.push(value.out);
}
}
for (let i = 0; i < d.length; ++i) {
this.cancelOut_(inEvent, d[i]);
}
}
};
/**
* Handler for `touchstart`, triggers `pointerover`,
* `pointerenter` and `pointerdown` events.
*
* @param {TouchEvent} inEvent The in event.
*/
TouchSource.prototype.touchstart = function(inEvent) {
this.vacuumTouches_(inEvent);
this.setPrimaryTouch_(inEvent.changedTouches[0]);
this.dedupSynthMouse_(inEvent);
this.clickCount_++;
this.processTouches_(inEvent, this.overDown_);
};
/**
* @private
* @param {TouchEvent} browserEvent The event.
* @param {PointerEvent} inPointer The in pointer object.
*/
TouchSource.prototype.overDown_ = function(browserEvent, inPointer) {
this.pointerMap[inPointer.pointerId] = {
target: inPointer.target,
out: inPointer,
outTarget: inPointer.target
};
this.dispatcher.over(inPointer, browserEvent);
this.dispatcher.enter(inPointer, browserEvent);
this.dispatcher.down(inPointer, browserEvent);
};
/**
* Handler for `touchmove`.
*
* @param {TouchEvent} inEvent The in event.
*/
TouchSource.prototype.touchmove = function(inEvent) {
inEvent.preventDefault();
this.processTouches_(inEvent, this.moveOverOut_);
};
/**
* @private
* @param {TouchEvent} browserEvent The event.
* @param {PointerEvent} inPointer The in pointer.
*/
TouchSource.prototype.moveOverOut_ = function(browserEvent, inPointer) {
const event = inPointer;
const pointer = this.pointerMap[event.pointerId];
// a finger drifted off the screen, ignore it
if (!pointer) {
return;
}
const outEvent = pointer.out;
const outTarget = pointer.outTarget;
this.dispatcher.move(event, browserEvent);
if (outEvent && outTarget !== event.target) {
outEvent.relatedTarget = event.target;
event.relatedTarget = outTarget;
// recover from retargeting by shadow
outEvent.target = outTarget;
if (event.target) {
this.dispatcher.leaveOut(outEvent, browserEvent);
this.dispatcher.enterOver(event, browserEvent);
} else {
// clean up case when finger leaves the screen
event.target = outTarget;
event.relatedTarget = null;
this.cancelOut_(browserEvent, event);
}
}
pointer.out = event;
pointer.outTarget = event.target;
};
/**
* Handler for `touchend`, triggers `pointerup`,
* `pointerout` and `pointerleave` events.
*
* @param {TouchEvent} inEvent The event.
*/
TouchSource.prototype.touchend = function(inEvent) {
this.dedupSynthMouse_(inEvent);
this.processTouches_(inEvent, this.upOut_);
};
/**
* @private
* @param {TouchEvent} browserEvent An event.
* @param {PointerEvent} inPointer The inPointer object.
*/
TouchSource.prototype.upOut_ = function(browserEvent, inPointer) {
this.dispatcher.up(inPointer, browserEvent);
this.dispatcher.out(inPointer, browserEvent);
this.dispatcher.leave(inPointer, browserEvent);
this.cleanUpPointer_(inPointer);
};
/**
* Handler for `touchcancel`, triggers `pointercancel`,
* `pointerout` and `pointerleave` events.
*
* @param {TouchEvent} inEvent The in event.
*/
TouchSource.prototype.touchcancel = function(inEvent) {
this.processTouches_(inEvent, this.cancelOut_);
};
/**
* @private
* @param {TouchEvent} browserEvent The event.
* @param {PointerEvent} inPointer The in pointer.
*/
TouchSource.prototype.cancelOut_ = function(browserEvent, inPointer) {
this.dispatcher.cancel(inPointer, browserEvent);
this.dispatcher.out(inPointer, browserEvent);
this.dispatcher.leave(inPointer, browserEvent);
this.cleanUpPointer_(inPointer);
};
/**
* @private
* @param {PointerEvent} inPointer The inPointer object.
*/
TouchSource.prototype.cleanUpPointer_ = function(inPointer) {
delete this.pointerMap[inPointer.pointerId];
this.removePrimaryPointer_(inPointer);
};
/**
* Prevent synth mouse events from creating pointer events.
*
* @private
* @param {TouchEvent} inEvent The in event.
*/
TouchSource.prototype.dedupSynthMouse_ = function(inEvent) {
const lts = this.mouseSource.lastTouches;
const t = inEvent.changedTouches[0];
// only the primary finger will synth mouse events
if (this.isPrimaryTouch_(t)) {
// remember x/y of last touch
const lt = [t.clientX, t.clientY];
lts.push(lt);
setTimeout(function() {
// remove touch after timeout
remove(lts, lt);
}, this.dedupTimeout_);
}
};
export default TouchSource;

View File

@@ -54,232 +54,220 @@ import {METERS_PER_UNIT} from '../proj/Units.js';
* @struct
* @api
*/
const Projection = function(options) {
/**
* @private
* @type {string}
*/
this.code_ = options.code;
class Projection {
constructor(options) {
/**
* @private
* @type {string}
*/
this.code_ = options.code;
/**
* Units of projected coordinates. When set to `TILE_PIXELS`, a
* `this.extent_` and `this.worldExtent_` must be configured properly for each
* tile.
* @private
* @type {module:ol/proj/Units}
*/
this.units_ = /** @type {module:ol/proj/Units} */ (options.units);
/**
* Units of projected coordinates. When set to `TILE_PIXELS`, a
* `this.extent_` and `this.worldExtent_` must be configured properly for each
* tile.
* @private
* @type {module:ol/proj/Units}
*/
this.units_ = /** @type {module:ol/proj/Units} */ (options.units);
/**
* Validity extent of the projection in projected coordinates. For projections
* with `TILE_PIXELS` units, this is the extent of the tile in
* tile pixel space.
* @private
* @type {module:ol/extent~Extent}
*/
this.extent_ = options.extent !== undefined ? options.extent : null;
/**
* Validity extent of the projection in projected coordinates. For projections
* with `TILE_PIXELS` units, this is the extent of the tile in
* tile pixel space.
* @private
* @type {module:ol/extent~Extent}
*/
this.extent_ = options.extent !== undefined ? options.extent : null;
/**
* Extent of the world in EPSG:4326. For projections with
* `TILE_PIXELS` units, this is the extent of the tile in
* projected coordinate space.
* @private
* @type {module:ol/extent~Extent}
*/
this.worldExtent_ = options.worldExtent !== undefined ?
options.worldExtent : null;
/**
* Extent of the world in EPSG:4326. For projections with
* `TILE_PIXELS` units, this is the extent of the tile in
* projected coordinate space.
* @private
* @type {module:ol/extent~Extent}
*/
this.worldExtent_ = options.worldExtent !== undefined ?
options.worldExtent : null;
/**
* @private
* @type {string}
*/
this.axisOrientation_ = options.axisOrientation !== undefined ?
options.axisOrientation : 'enu';
/**
* @private
* @type {string}
*/
this.axisOrientation_ = options.axisOrientation !== undefined ?
options.axisOrientation : 'enu';
/**
* @private
* @type {boolean}
*/
this.global_ = options.global !== undefined ? options.global : false;
/**
* @private
* @type {boolean}
*/
this.global_ = options.global !== undefined ? options.global : false;
/**
* @private
* @type {boolean}
*/
this.canWrapX_ = !!(this.global_ && this.extent_);
/**
* @private
* @type {boolean}
*/
this.canWrapX_ = !!(this.global_ && this.extent_);
/**
* @private
* @type {function(number, module:ol/coordinate~Coordinate):number|undefined}
*/
this.getPointResolutionFunc_ = options.getPointResolution;
/**
* @private
* @type {function(number, module:ol/coordinate~Coordinate):number|undefined}
*/
this.getPointResolutionFunc_ = options.getPointResolution;
/**
* @private
* @type {module:ol/tilegrid/TileGrid}
*/
this.defaultTileGrid_ = null;
/**
* @private
* @type {module:ol/tilegrid/TileGrid}
*/
this.defaultTileGrid_ = null;
/**
* @private
* @type {number|undefined}
*/
this.metersPerUnit_ = options.metersPerUnit;
};
/**
* @private
* @type {number|undefined}
*/
this.metersPerUnit_ = options.metersPerUnit;
}
/**
* @return {boolean} The projection is suitable for wrapping the x-axis
*/
canWrapX() {
return this.canWrapX_;
}
/**
* @return {boolean} The projection is suitable for wrapping the x-axis
*/
Projection.prototype.canWrapX = function() {
return this.canWrapX_;
};
/**
* Get the code for this projection, e.g. 'EPSG:4326'.
* @return {string} Code.
* @api
*/
getCode() {
return this.code_;
}
/**
* Get the validity extent for this projection.
* @return {module:ol/extent~Extent} Extent.
* @api
*/
getExtent() {
return this.extent_;
}
/**
* Get the code for this projection, e.g. 'EPSG:4326'.
* @return {string} Code.
/**
* Get the units of this projection.
* @return {module:ol/proj/Units} Units.
* @api
*/
getUnits() {
return this.units_;
}
/**
* Get the amount of meters per unit of this projection. If the projection is
* not configured with `metersPerUnit` or a units identifier, the return is
* `undefined`.
* @return {number|undefined} Meters.
* @api
*/
getMetersPerUnit() {
return this.metersPerUnit_ || METERS_PER_UNIT[this.units_];
}
/**
* Get the world extent for this projection.
* @return {module:ol/extent~Extent} Extent.
* @api
*/
getWorldExtent() {
return this.worldExtent_;
}
/**
* Get the axis orientation of this projection.
* Example values are:
* enu - the default easting, northing, elevation.
* neu - northing, easting, up - useful for "lat/long" geographic coordinates,
* or south orientated transverse mercator.
* wnu - westing, northing, up - some planetary coordinate systems have
* "west positive" coordinate systems
* @return {string} Axis orientation.
* @api
*/
getAxisOrientation() {
return this.axisOrientation_;
}
/**
* Is this projection a global projection which spans the whole world?
* @return {boolean} Whether the projection is global.
* @api
*/
isGlobal() {
return this.global_;
}
/**
* Set if the projection is a global projection which spans the whole world
* @param {boolean} global Whether the projection is global.
* @api
*/
Projection.prototype.getCode = function() {
return this.code_;
};
setGlobal(global) {
this.global_ = global;
this.canWrapX_ = !!(global && this.extent_);
}
/**
* @return {module:ol/tilegrid/TileGrid} The default tile grid.
*/
getDefaultTileGrid() {
return this.defaultTileGrid_;
}
/**
* Get the validity extent for this projection.
* @return {module:ol/extent~Extent} Extent.
* @api
*/
Projection.prototype.getExtent = function() {
return this.extent_;
};
/**
* @param {module:ol/tilegrid/TileGrid} tileGrid The default tile grid.
*/
setDefaultTileGrid(tileGrid) {
this.defaultTileGrid_ = tileGrid;
}
/**
* Set the validity extent for this projection.
* @param {module:ol/extent~Extent} extent Extent.
* @api
*/
setExtent(extent) {
this.extent_ = extent;
this.canWrapX_ = !!(this.global_ && extent);
}
/**
* Get the units of this projection.
* @return {module:ol/proj/Units} Units.
* @api
*/
Projection.prototype.getUnits = function() {
return this.units_;
};
/**
* Set the world extent for this projection.
* @param {module:ol/extent~Extent} worldExtent World extent
* [minlon, minlat, maxlon, maxlat].
* @api
*/
setWorldExtent(worldExtent) {
this.worldExtent_ = worldExtent;
}
/**
* Set the getPointResolution function (see {@link module:ol/proj~getPointResolution}
* for this projection.
* @param {function(number, module:ol/coordinate~Coordinate):number} func Function
* @api
*/
setGetPointResolution(func) {
this.getPointResolutionFunc_ = func;
}
/**
* Get the amount of meters per unit of this projection. If the projection is
* not configured with `metersPerUnit` or a units identifier, the return is
* `undefined`.
* @return {number|undefined} Meters.
* @api
*/
Projection.prototype.getMetersPerUnit = function() {
return this.metersPerUnit_ || METERS_PER_UNIT[this.units_];
};
/**
* Get the custom point resolution function for this projection (if set).
* @return {function(number, module:ol/coordinate~Coordinate):number|undefined} The custom point
* resolution function (if set).
*/
getPointResolutionFunc() {
return this.getPointResolutionFunc_;
}
}
/**
* Get the world extent for this projection.
* @return {module:ol/extent~Extent} Extent.
* @api
*/
Projection.prototype.getWorldExtent = function() {
return this.worldExtent_;
};
/**
* Get the axis orientation of this projection.
* Example values are:
* enu - the default easting, northing, elevation.
* neu - northing, easting, up - useful for "lat/long" geographic coordinates,
* or south orientated transverse mercator.
* wnu - westing, northing, up - some planetary coordinate systems have
* "west positive" coordinate systems
* @return {string} Axis orientation.
* @api
*/
Projection.prototype.getAxisOrientation = function() {
return this.axisOrientation_;
};
/**
* Is this projection a global projection which spans the whole world?
* @return {boolean} Whether the projection is global.
* @api
*/
Projection.prototype.isGlobal = function() {
return this.global_;
};
/**
* Set if the projection is a global projection which spans the whole world
* @param {boolean} global Whether the projection is global.
* @api
*/
Projection.prototype.setGlobal = function(global) {
this.global_ = global;
this.canWrapX_ = !!(global && this.extent_);
};
/**
* @return {module:ol/tilegrid/TileGrid} The default tile grid.
*/
Projection.prototype.getDefaultTileGrid = function() {
return this.defaultTileGrid_;
};
/**
* @param {module:ol/tilegrid/TileGrid} tileGrid The default tile grid.
*/
Projection.prototype.setDefaultTileGrid = function(tileGrid) {
this.defaultTileGrid_ = tileGrid;
};
/**
* Set the validity extent for this projection.
* @param {module:ol/extent~Extent} extent Extent.
* @api
*/
Projection.prototype.setExtent = function(extent) {
this.extent_ = extent;
this.canWrapX_ = !!(this.global_ && extent);
};
/**
* Set the world extent for this projection.
* @param {module:ol/extent~Extent} worldExtent World extent
* [minlon, minlat, maxlon, maxlat].
* @api
*/
Projection.prototype.setWorldExtent = function(worldExtent) {
this.worldExtent_ = worldExtent;
};
/**
* Set the getPointResolution function (see {@link module:ol/proj~getPointResolution}
* for this projection.
* @param {function(number, module:ol/coordinate~Coordinate):number} func Function
* @api
*/
Projection.prototype.setGetPointResolution = function(func) {
this.getPointResolutionFunc_ = func;
};
/**
* Get the custom point resolution function for this projection (if set).
* @return {function(number, module:ol/coordinate~Coordinate):number|undefined} The custom point
* resolution function (if set).
*/
Projection.prototype.getPointResolutionFunc = function() {
return this.getPointResolutionFunc_;
};
export default Projection;

View File

@@ -12,123 +12,121 @@ import Polygon from '../geom/Polygon.js';
* @extends {module:ol/Disposable}
* @param {string} className CSS class name.
*/
const RenderBox = function(className) {
class RenderBox {
constructor(className) {
/**
* @type {module:ol/geom/Polygon}
* @private
*/
this.geometry_ = null;
/**
* @type {module:ol/geom/Polygon}
* @private
*/
this.geometry_ = null;
/**
* @type {HTMLDivElement}
* @private
*/
this.element_ = /** @type {HTMLDivElement} */ (document.createElement('div'));
this.element_.style.position = 'absolute';
this.element_.className = 'ol-box ' + className;
/**
* @type {HTMLDivElement}
* @private
*/
this.element_ = /** @type {HTMLDivElement} */ (document.createElement('div'));
this.element_.style.position = 'absolute';
this.element_.className = 'ol-box ' + className;
/**
* @private
* @type {module:ol/PluggableMap}
*/
this.map_ = null;
/**
* @private
* @type {module:ol/PluggableMap}
*/
this.map_ = null;
/**
* @private
* @type {module:ol~Pixel}
*/
this.startPixel_ = null;
/**
* @private
* @type {module:ol~Pixel}
*/
this.startPixel_ = null;
/**
* @private
* @type {module:ol~Pixel}
*/
this.endPixel_ = null;
/**
* @private
* @type {module:ol~Pixel}
*/
this.endPixel_ = null;
};
}
/**
* @inheritDoc
*/
disposeInternal() {
this.setMap(null);
}
/**
* @private
*/
render_() {
const startPixel = this.startPixel_;
const endPixel = this.endPixel_;
const px = 'px';
const style = this.element_.style;
style.left = Math.min(startPixel[0], endPixel[0]) + px;
style.top = Math.min(startPixel[1], endPixel[1]) + px;
style.width = Math.abs(endPixel[0] - startPixel[0]) + px;
style.height = Math.abs(endPixel[1] - startPixel[1]) + px;
}
/**
* @param {module:ol/PluggableMap} map Map.
*/
setMap(map) {
if (this.map_) {
this.map_.getOverlayContainer().removeChild(this.element_);
const style = this.element_.style;
style.left = style.top = style.width = style.height = 'inherit';
}
this.map_ = map;
if (this.map_) {
this.map_.getOverlayContainer().appendChild(this.element_);
}
}
/**
* @param {module:ol~Pixel} startPixel Start pixel.
* @param {module:ol~Pixel} endPixel End pixel.
*/
setPixels(startPixel, endPixel) {
this.startPixel_ = startPixel;
this.endPixel_ = endPixel;
this.createOrUpdateGeometry();
this.render_();
}
/**
* Creates or updates the cached geometry.
*/
createOrUpdateGeometry() {
const startPixel = this.startPixel_;
const endPixel = this.endPixel_;
const pixels = [
startPixel,
[startPixel[0], endPixel[1]],
endPixel,
[endPixel[0], startPixel[1]]
];
const coordinates = pixels.map(this.map_.getCoordinateFromPixel, this.map_);
// close the polygon
coordinates[4] = coordinates[0].slice();
if (!this.geometry_) {
this.geometry_ = new Polygon([coordinates]);
} else {
this.geometry_.setCoordinates([coordinates]);
}
}
/**
* @return {module:ol/geom/Polygon} Geometry.
*/
getGeometry() {
return this.geometry_;
}
}
inherits(RenderBox, Disposable);
/**
* @inheritDoc
*/
RenderBox.prototype.disposeInternal = function() {
this.setMap(null);
};
/**
* @private
*/
RenderBox.prototype.render_ = function() {
const startPixel = this.startPixel_;
const endPixel = this.endPixel_;
const px = 'px';
const style = this.element_.style;
style.left = Math.min(startPixel[0], endPixel[0]) + px;
style.top = Math.min(startPixel[1], endPixel[1]) + px;
style.width = Math.abs(endPixel[0] - startPixel[0]) + px;
style.height = Math.abs(endPixel[1] - startPixel[1]) + px;
};
/**
* @param {module:ol/PluggableMap} map Map.
*/
RenderBox.prototype.setMap = function(map) {
if (this.map_) {
this.map_.getOverlayContainer().removeChild(this.element_);
const style = this.element_.style;
style.left = style.top = style.width = style.height = 'inherit';
}
this.map_ = map;
if (this.map_) {
this.map_.getOverlayContainer().appendChild(this.element_);
}
};
/**
* @param {module:ol~Pixel} startPixel Start pixel.
* @param {module:ol~Pixel} endPixel End pixel.
*/
RenderBox.prototype.setPixels = function(startPixel, endPixel) {
this.startPixel_ = startPixel;
this.endPixel_ = endPixel;
this.createOrUpdateGeometry();
this.render_();
};
/**
* Creates or updates the cached geometry.
*/
RenderBox.prototype.createOrUpdateGeometry = function() {
const startPixel = this.startPixel_;
const endPixel = this.endPixel_;
const pixels = [
startPixel,
[startPixel[0], endPixel[1]],
endPixel,
[endPixel[0], startPixel[1]]
];
const coordinates = pixels.map(this.map_.getCoordinateFromPixel, this.map_);
// close the polygon
coordinates[4] = coordinates[0].slice();
if (!this.geometry_) {
this.geometry_ = new Polygon([coordinates]);
} else {
this.geometry_.setCoordinates([coordinates]);
}
};
/**
* @return {module:ol/geom/Polygon} Geometry.
*/
RenderBox.prototype.getGeometry = function() {
return this.geometry_;
};
export default RenderBox;

View File

@@ -25,56 +25,212 @@ import {create as createTransform, compose as composeTransform} from '../transfo
* @param {Object.<string, *>} properties Properties.
* @param {number|string|undefined} id Feature id.
*/
const RenderFeature = function(type, flatCoordinates, ends, properties, id) {
/**
* @private
* @type {module:ol/extent~Extent|undefined}
*/
this.extent_;
class RenderFeature {
constructor(type, flatCoordinates, ends, properties, id) {
/**
* @private
* @type {module:ol/extent~Extent|undefined}
*/
this.extent_;
/**
* @private
* @type {number|string|undefined}
*/
this.id_ = id;
/**
* @private
* @type {number|string|undefined}
*/
this.id_ = id;
/**
* @private
* @type {module:ol/geom/GeometryType}
*/
this.type_ = type;
/**
* @private
* @type {module:ol/geom/GeometryType}
*/
this.type_ = type;
/**
* @private
* @type {Array.<number>}
*/
this.flatCoordinates_ = flatCoordinates;
/**
* @private
* @type {Array.<number>}
*/
this.flatCoordinates_ = flatCoordinates;
/**
* @private
* @type {Array.<number>}
*/
this.flatInteriorPoints_ = null;
/**
* @private
* @type {Array.<number>}
*/
this.flatInteriorPoints_ = null;
/**
* @private
* @type {Array.<number>}
*/
this.flatMidpoints_ = null;
/**
* @private
* @type {Array.<number>}
*/
this.flatMidpoints_ = null;
/**
* @private
* @type {Array.<number>|Array.<Array.<number>>}
*/
this.ends_ = ends;
/**
* @private
* @type {Array.<number>|Array.<Array.<number>>}
*/
this.ends_ = ends;
/**
* @private
* @type {Object.<string, *>}
*/
this.properties_ = properties;
/**
* @private
* @type {Object.<string, *>}
*/
this.properties_ = properties;
};
}
/**
* Get a feature property by its key.
* @param {string} key Key
* @return {*} Value for the requested key.
* @api
*/
get(key) {
return this.properties_[key];
}
/**
* Get the extent of this feature's geometry.
* @return {module:ol/extent~Extent} Extent.
* @api
*/
getExtent() {
if (!this.extent_) {
this.extent_ = this.type_ === GeometryType.POINT ?
createOrUpdateFromCoordinate(this.flatCoordinates_) :
createOrUpdateFromFlatCoordinates(
this.flatCoordinates_, 0, this.flatCoordinates_.length, 2);
}
return this.extent_;
}
/**
* @return {Array.<number>} Flat interior points.
*/
getFlatInteriorPoint() {
if (!this.flatInteriorPoints_) {
const flatCenter = getCenter(this.getExtent());
this.flatInteriorPoints_ = getInteriorPointOfArray(
this.flatCoordinates_, 0, this.ends_, 2, flatCenter, 0);
}
return this.flatInteriorPoints_;
}
/**
* @return {Array.<number>} Flat interior points.
*/
getFlatInteriorPoints() {
if (!this.flatInteriorPoints_) {
const flatCenters = linearRingssCenter(
this.flatCoordinates_, 0, this.ends_, 2);
this.flatInteriorPoints_ = getInteriorPointsOfMultiArray(
this.flatCoordinates_, 0, this.ends_, 2, flatCenters);
}
return this.flatInteriorPoints_;
}
/**
* @return {Array.<number>} Flat midpoint.
*/
getFlatMidpoint() {
if (!this.flatMidpoints_) {
this.flatMidpoints_ = interpolatePoint(
this.flatCoordinates_, 0, this.flatCoordinates_.length, 2, 0.5);
}
return this.flatMidpoints_;
}
/**
* @return {Array.<number>} Flat midpoints.
*/
getFlatMidpoints() {
if (!this.flatMidpoints_) {
this.flatMidpoints_ = [];
const flatCoordinates = this.flatCoordinates_;
let offset = 0;
const ends = this.ends_;
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
const midpoint = interpolatePoint(
flatCoordinates, offset, end, 2, 0.5);
extend(this.flatMidpoints_, midpoint);
offset = end;
}
}
return this.flatMidpoints_;
}
/**
* Get the feature identifier. This is a stable identifier for the feature and
* is set when reading data from a remote source.
* @return {number|string|undefined} Id.
* @api
*/
getId() {
return this.id_;
}
/**
* @return {Array.<number>} Flat coordinates.
*/
getOrientedFlatCoordinates() {
return this.flatCoordinates_;
}
/**
* For API compatibility with {@link module:ol/Feature~Feature}, this method is useful when
* determining the geometry type in style function (see {@link #getType}).
* @return {module:ol/render/Feature} Feature.
* @api
*/
getGeometry() {
return this;
}
/**
* Get the feature properties.
* @return {Object.<string, *>} Feature properties.
* @api
*/
getProperties() {
return this.properties_;
}
/**
* @return {number} Stride.
*/
getStride() {
return 2;
}
/**
* Get the type of this feature's geometry.
* @return {module:ol/geom/GeometryType} Geometry type.
* @api
*/
getType() {
return this.type_;
}
/**
* Transform geometry coordinates from tile pixel space to projected.
* The SRS of the source and destination are expected to be the same.
*
* @param {module:ol/proj~ProjectionLike} source The current projection
* @param {module:ol/proj~ProjectionLike} destination The desired projection.
*/
transform(source, destination) {
source = getProjection(source);
const pixelExtent = source.getExtent();
const projectedExtent = source.getWorldExtent();
const scale = getHeight(projectedExtent) / getHeight(pixelExtent);
composeTransform(tmpTransform,
projectedExtent[0], projectedExtent[3],
scale, -scale, 0,
0, 0);
transform2D(this.flatCoordinates_, 0, this.flatCoordinates_.length, 2,
tmpTransform, this.flatCoordinates_);
}
}
/**
@@ -83,17 +239,6 @@ const RenderFeature = function(type, flatCoordinates, ends, properties, id) {
const tmpTransform = createTransform();
/**
* Get a feature property by its key.
* @param {string} key Key
* @return {*} Value for the requested key.
* @api
*/
RenderFeature.prototype.get = function(key) {
return this.properties_[key];
};
/**
* @return {Array.<number>|Array.<Array.<number>>} Ends or endss.
*/
@@ -103,101 +248,6 @@ RenderFeature.prototype.getEndss = function() {
};
/**
* Get the extent of this feature's geometry.
* @return {module:ol/extent~Extent} Extent.
* @api
*/
RenderFeature.prototype.getExtent = function() {
if (!this.extent_) {
this.extent_ = this.type_ === GeometryType.POINT ?
createOrUpdateFromCoordinate(this.flatCoordinates_) :
createOrUpdateFromFlatCoordinates(
this.flatCoordinates_, 0, this.flatCoordinates_.length, 2);
}
return this.extent_;
};
/**
* @return {Array.<number>} Flat interior points.
*/
RenderFeature.prototype.getFlatInteriorPoint = function() {
if (!this.flatInteriorPoints_) {
const flatCenter = getCenter(this.getExtent());
this.flatInteriorPoints_ = getInteriorPointOfArray(
this.flatCoordinates_, 0, this.ends_, 2, flatCenter, 0);
}
return this.flatInteriorPoints_;
};
/**
* @return {Array.<number>} Flat interior points.
*/
RenderFeature.prototype.getFlatInteriorPoints = function() {
if (!this.flatInteriorPoints_) {
const flatCenters = linearRingssCenter(
this.flatCoordinates_, 0, this.ends_, 2);
this.flatInteriorPoints_ = getInteriorPointsOfMultiArray(
this.flatCoordinates_, 0, this.ends_, 2, flatCenters);
}
return this.flatInteriorPoints_;
};
/**
* @return {Array.<number>} Flat midpoint.
*/
RenderFeature.prototype.getFlatMidpoint = function() {
if (!this.flatMidpoints_) {
this.flatMidpoints_ = interpolatePoint(
this.flatCoordinates_, 0, this.flatCoordinates_.length, 2, 0.5);
}
return this.flatMidpoints_;
};
/**
* @return {Array.<number>} Flat midpoints.
*/
RenderFeature.prototype.getFlatMidpoints = function() {
if (!this.flatMidpoints_) {
this.flatMidpoints_ = [];
const flatCoordinates = this.flatCoordinates_;
let offset = 0;
const ends = this.ends_;
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
const midpoint = interpolatePoint(
flatCoordinates, offset, end, 2, 0.5);
extend(this.flatMidpoints_, midpoint);
offset = end;
}
}
return this.flatMidpoints_;
};
/**
* Get the feature identifier. This is a stable identifier for the feature and
* is set when reading data from a remote source.
* @return {number|string|undefined} Id.
* @api
*/
RenderFeature.prototype.getId = function() {
return this.id_;
};
/**
* @return {Array.<number>} Flat coordinates.
*/
RenderFeature.prototype.getOrientedFlatCoordinates = function() {
return this.flatCoordinates_;
};
/**
* @return {Array.<number>} Flat coordinates.
*/
@@ -205,27 +255,6 @@ RenderFeature.prototype.getFlatCoordinates =
RenderFeature.prototype.getOrientedFlatCoordinates;
/**
* For API compatibility with {@link module:ol/Feature~Feature}, this method is useful when
* determining the geometry type in style function (see {@link #getType}).
* @return {module:ol/render/Feature} Feature.
* @api
*/
RenderFeature.prototype.getGeometry = function() {
return this;
};
/**
* Get the feature properties.
* @return {Object.<string, *>} Feature properties.
* @api
*/
RenderFeature.prototype.getProperties = function() {
return this.properties_;
};
/**
* Get the feature for working with its geometry.
* @return {module:ol/render/Feature} Feature.
@@ -234,46 +263,10 @@ RenderFeature.prototype.getSimplifiedGeometry =
RenderFeature.prototype.getGeometry;
/**
* @return {number} Stride.
*/
RenderFeature.prototype.getStride = function() {
return 2;
};
/**
* @return {undefined}
*/
RenderFeature.prototype.getStyleFunction = UNDEFINED;
/**
* Get the type of this feature's geometry.
* @return {module:ol/geom/GeometryType} Geometry type.
* @api
*/
RenderFeature.prototype.getType = function() {
return this.type_;
};
/**
* Transform geometry coordinates from tile pixel space to projected.
* The SRS of the source and destination are expected to be the same.
*
* @param {module:ol/proj~ProjectionLike} source The current projection
* @param {module:ol/proj~ProjectionLike} destination The desired projection.
*/
RenderFeature.prototype.transform = function(source, destination) {
source = getProjection(source);
const pixelExtent = source.getExtent();
const projectedExtent = source.getWorldExtent();
const scale = getHeight(projectedExtent) / getHeight(pixelExtent);
composeTransform(tmpTransform,
projectedExtent[0], projectedExtent[3],
scale, -scale, 0,
0, 0);
transform2D(this.flatCoordinates_, 0, this.flatCoordinates_.length, 2,
tmpTransform, this.flatCoordinates_);
};
export default RenderFeature;

View File

@@ -6,21 +6,20 @@
* @constructor
* @abstract
*/
const ReplayGroup = function() {};
class ReplayGroup {
/**
* @abstract
* @param {number|undefined} zIndex Z index.
* @param {module:ol/render/ReplayType} replayType Replay type.
* @return {module:ol/render/VectorContext} Replay.
*/
getReplay(zIndex, replayType) {}
/**
* @abstract
* @return {boolean} Is empty.
*/
isEmpty() {}
}
/**
* @abstract
* @param {number|undefined} zIndex Z index.
* @param {module:ol/render/ReplayType} replayType Replay type.
* @return {module:ol/render/VectorContext} Replay.
*/
ReplayGroup.prototype.getReplay = function(zIndex, replayType) {};
/**
* @abstract
* @return {boolean} Is empty.
*/
ReplayGroup.prototype.isEmpty = function() {};
export default ReplayGroup;

View File

@@ -9,124 +9,108 @@
* @struct
* @api
*/
const VectorContext = function() {
};
class VectorContext {
/**
* Render a geometry with a custom renderer.
*
* @param {module:ol/geom/SimpleGeometry} geometry Geometry.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
* @param {Function} renderer Renderer.
*/
drawCustom(geometry, feature, renderer) {}
/**
* Render a geometry.
*
* @param {module:ol/geom/Geometry} geometry The geometry to render.
*/
drawGeometry(geometry) {}
/**
* Render a geometry with a custom renderer.
*
* @param {module:ol/geom/SimpleGeometry} geometry Geometry.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
* @param {Function} renderer Renderer.
*/
VectorContext.prototype.drawCustom = function(geometry, feature, renderer) {};
/**
* Set the rendering style.
*
* @param {module:ol/style/Style} style The rendering style.
*/
setStyle(style) {}
/**
* @param {module:ol/geom/Circle} circleGeometry Circle geometry.
* @param {module:ol/Feature} feature Feature.
*/
drawCircle(circleGeometry, feature) {}
/**
* Render a geometry.
*
* @param {module:ol/geom/Geometry} geometry The geometry to render.
*/
VectorContext.prototype.drawGeometry = function(geometry) {};
/**
* @param {module:ol/Feature} feature Feature.
* @param {module:ol/style/Style} style Style.
*/
drawFeature(feature, style) {}
/**
* @param {module:ol/geom/GeometryCollection} geometryCollectionGeometry Geometry
* collection.
* @param {module:ol/Feature} feature Feature.
*/
drawGeometryCollection(geometryCollectionGeometry, feature) {}
/**
* Set the rendering style.
*
* @param {module:ol/style/Style} style The rendering style.
*/
VectorContext.prototype.setStyle = function(style) {};
/**
* @param {module:ol/geom/LineString|module:ol/render/Feature} lineStringGeometry Line string geometry.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
*/
drawLineString(lineStringGeometry, feature) {}
/**
* @param {module:ol/geom/MultiLineString|module:ol/render/Feature} multiLineStringGeometry MultiLineString geometry.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
*/
drawMultiLineString(multiLineStringGeometry, feature) {}
/**
* @param {module:ol/geom/Circle} circleGeometry Circle geometry.
* @param {module:ol/Feature} feature Feature.
*/
VectorContext.prototype.drawCircle = function(circleGeometry, feature) {};
/**
* @param {module:ol/geom/MultiPoint|module:ol/render/Feature} multiPointGeometry MultiPoint geometry.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
*/
drawMultiPoint(multiPointGeometry, feature) {}
/**
* @param {module:ol/geom/MultiPolygon} multiPolygonGeometry MultiPolygon geometry.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
*/
drawMultiPolygon(multiPolygonGeometry, feature) {}
/**
* @param {module:ol/Feature} feature Feature.
* @param {module:ol/style/Style} style Style.
*/
VectorContext.prototype.drawFeature = function(feature, style) {};
/**
* @param {module:ol/geom/Point|module:ol/render/Feature} pointGeometry Point geometry.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
*/
drawPoint(pointGeometry, feature) {}
/**
* @param {module:ol/geom/Polygon|module:ol/render/Feature} polygonGeometry Polygon geometry.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
*/
drawPolygon(polygonGeometry, feature) {}
/**
* @param {module:ol/geom/GeometryCollection} geometryCollectionGeometry Geometry
* collection.
* @param {module:ol/Feature} feature Feature.
*/
VectorContext.prototype.drawGeometryCollection = function(geometryCollectionGeometry, feature) {};
/**
* @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
*/
drawText(geometry, feature) {}
/**
* @param {module:ol/style/Fill} fillStyle Fill style.
* @param {module:ol/style/Stroke} strokeStyle Stroke style.
*/
setFillStrokeStyle(fillStyle, strokeStyle) {}
/**
* @param {module:ol/geom/LineString|module:ol/render/Feature} lineStringGeometry Line string geometry.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
*/
VectorContext.prototype.drawLineString = function(lineStringGeometry, feature) {};
/**
* @param {module:ol/style/Image} imageStyle Image style.
* @param {module:ol/render/canvas~DeclutterGroup=} opt_declutterGroup Declutter.
*/
setImageStyle(imageStyle, opt_declutterGroup) {}
/**
* @param {module:ol/style/Text} textStyle Text style.
* @param {module:ol/render/canvas~DeclutterGroup=} opt_declutterGroup Declutter.
*/
setTextStyle(textStyle, opt_declutterGroup) {}
}
/**
* @param {module:ol/geom/MultiLineString|module:ol/render/Feature} multiLineStringGeometry MultiLineString geometry.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
*/
VectorContext.prototype.drawMultiLineString = function(multiLineStringGeometry, feature) {};
/**
* @param {module:ol/geom/MultiPoint|module:ol/render/Feature} multiPointGeometry MultiPoint geometry.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
*/
VectorContext.prototype.drawMultiPoint = function(multiPointGeometry, feature) {};
/**
* @param {module:ol/geom/MultiPolygon} multiPolygonGeometry MultiPolygon geometry.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
*/
VectorContext.prototype.drawMultiPolygon = function(multiPolygonGeometry, feature) {};
/**
* @param {module:ol/geom/Point|module:ol/render/Feature} pointGeometry Point geometry.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
*/
VectorContext.prototype.drawPoint = function(pointGeometry, feature) {};
/**
* @param {module:ol/geom/Polygon|module:ol/render/Feature} polygonGeometry Polygon geometry.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
*/
VectorContext.prototype.drawPolygon = function(polygonGeometry, feature) {};
/**
* @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry.
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
*/
VectorContext.prototype.drawText = function(geometry, feature) {};
/**
* @param {module:ol/style/Fill} fillStyle Fill style.
* @param {module:ol/style/Stroke} strokeStyle Stroke style.
*/
VectorContext.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) {};
/**
* @param {module:ol/style/Image} imageStyle Image style.
* @param {module:ol/render/canvas~DeclutterGroup=} opt_declutterGroup Declutter.
*/
VectorContext.prototype.setImageStyle = function(imageStyle, opt_declutterGroup) {};
/**
* @param {module:ol/style/Text} textStyle Text style.
* @param {module:ol/render/canvas~DeclutterGroup=} opt_declutterGroup Declutter.
*/
VectorContext.prototype.setTextStyle = function(textStyle, opt_declutterGroup) {};
export default VectorContext;

View File

@@ -16,218 +16,216 @@ import CanvasReplay from '../canvas/Replay.js';
* @param {?} declutterTree Declutter tree.
* @struct
*/
const CanvasImageReplay = function(
tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) {
CanvasReplay.call(this,
tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree);
class CanvasImageReplay {
constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) {
CanvasReplay.call(this,
tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree);
/**
* @private
* @type {module:ol/render/canvas~DeclutterGroup}
*/
this.declutterGroup_ = null;
/**
* @private
* @type {module:ol/render/canvas~DeclutterGroup}
*/
this.declutterGroup_ = null;
/**
* @private
* @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement}
*/
this.hitDetectionImage_ = null;
/**
* @private
* @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement}
*/
this.hitDetectionImage_ = null;
/**
* @private
* @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement}
*/
this.image_ = null;
/**
* @private
* @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement}
*/
this.image_ = null;
/**
* @private
* @type {number|undefined}
*/
this.anchorX_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.anchorX_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.anchorY_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.anchorY_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.height_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.height_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.opacity_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.opacity_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.originX_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.originX_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.originY_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.originY_ = undefined;
/**
* @private
* @type {boolean|undefined}
*/
this.rotateWithView_ = undefined;
/**
* @private
* @type {boolean|undefined}
*/
this.rotateWithView_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.rotation_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.rotation_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.scale_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.scale_ = undefined;
/**
* @private
* @type {boolean|undefined}
*/
this.snapToPixel_ = undefined;
/**
* @private
* @type {boolean|undefined}
*/
this.snapToPixel_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.width_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.width_ = undefined;
};
}
/**
* @param {Array.<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {number} end End.
* @param {number} stride Stride.
* @private
* @return {number} My end.
*/
drawCoordinates_(flatCoordinates, offset, end, stride) {
return this.appendFlatCoordinates(flatCoordinates, offset, end, stride, false, false);
}
/**
* @inheritDoc
*/
drawPoint(pointGeometry, feature) {
if (!this.image_) {
return;
}
this.beginGeometry(pointGeometry, feature);
const flatCoordinates = pointGeometry.getFlatCoordinates();
const stride = pointGeometry.getStride();
const myBegin = this.coordinates.length;
const myEnd = this.drawCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride);
this.instructions.push([
CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.image_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_,
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_ * this.pixelRatio, this.snapToPixel_, this.width_
]);
this.hitDetectionInstructions.push([
CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_,
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_, this.snapToPixel_, this.width_
]);
this.endGeometry(pointGeometry, feature);
}
/**
* @inheritDoc
*/
drawMultiPoint(multiPointGeometry, feature) {
if (!this.image_) {
return;
}
this.beginGeometry(multiPointGeometry, feature);
const flatCoordinates = multiPointGeometry.getFlatCoordinates();
const stride = multiPointGeometry.getStride();
const myBegin = this.coordinates.length;
const myEnd = this.drawCoordinates_(
flatCoordinates, 0, flatCoordinates.length, stride);
this.instructions.push([
CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.image_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_,
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_ * this.pixelRatio, this.snapToPixel_, this.width_
]);
this.hitDetectionInstructions.push([
CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_,
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_, this.snapToPixel_, this.width_
]);
this.endGeometry(multiPointGeometry, feature);
}
/**
* @inheritDoc
*/
finish() {
this.reverseHitDetectionInstructions();
// FIXME this doesn't really protect us against further calls to draw*Geometry
this.anchorX_ = undefined;
this.anchorY_ = undefined;
this.hitDetectionImage_ = null;
this.image_ = null;
this.height_ = undefined;
this.scale_ = undefined;
this.opacity_ = undefined;
this.originX_ = undefined;
this.originY_ = undefined;
this.rotateWithView_ = undefined;
this.rotation_ = undefined;
this.snapToPixel_ = undefined;
this.width_ = undefined;
}
/**
* @inheritDoc
*/
setImageStyle(imageStyle, declutterGroup) {
const anchor = imageStyle.getAnchor();
const size = imageStyle.getSize();
const hitDetectionImage = imageStyle.getHitDetectionImage(1);
const image = imageStyle.getImage(1);
const origin = imageStyle.getOrigin();
this.anchorX_ = anchor[0];
this.anchorY_ = anchor[1];
this.declutterGroup_ = /** @type {module:ol/render/canvas~DeclutterGroup} */ (declutterGroup);
this.hitDetectionImage_ = hitDetectionImage;
this.image_ = image;
this.height_ = size[1];
this.opacity_ = imageStyle.getOpacity();
this.originX_ = origin[0];
this.originY_ = origin[1];
this.rotateWithView_ = imageStyle.getRotateWithView();
this.rotation_ = imageStyle.getRotation();
this.scale_ = imageStyle.getScale();
this.snapToPixel_ = imageStyle.getSnapToPixel();
this.width_ = size[0];
}
}
inherits(CanvasImageReplay, CanvasReplay);
/**
* @param {Array.<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {number} end End.
* @param {number} stride Stride.
* @private
* @return {number} My end.
*/
CanvasImageReplay.prototype.drawCoordinates_ = function(flatCoordinates, offset, end, stride) {
return this.appendFlatCoordinates(flatCoordinates, offset, end, stride, false, false);
};
/**
* @inheritDoc
*/
CanvasImageReplay.prototype.drawPoint = function(pointGeometry, feature) {
if (!this.image_) {
return;
}
this.beginGeometry(pointGeometry, feature);
const flatCoordinates = pointGeometry.getFlatCoordinates();
const stride = pointGeometry.getStride();
const myBegin = this.coordinates.length;
const myEnd = this.drawCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride);
this.instructions.push([
CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.image_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_,
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_ * this.pixelRatio, this.snapToPixel_, this.width_
]);
this.hitDetectionInstructions.push([
CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_,
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_, this.snapToPixel_, this.width_
]);
this.endGeometry(pointGeometry, feature);
};
/**
* @inheritDoc
*/
CanvasImageReplay.prototype.drawMultiPoint = function(multiPointGeometry, feature) {
if (!this.image_) {
return;
}
this.beginGeometry(multiPointGeometry, feature);
const flatCoordinates = multiPointGeometry.getFlatCoordinates();
const stride = multiPointGeometry.getStride();
const myBegin = this.coordinates.length;
const myEnd = this.drawCoordinates_(
flatCoordinates, 0, flatCoordinates.length, stride);
this.instructions.push([
CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.image_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_,
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_ * this.pixelRatio, this.snapToPixel_, this.width_
]);
this.hitDetectionInstructions.push([
CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_,
this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
this.scale_, this.snapToPixel_, this.width_
]);
this.endGeometry(multiPointGeometry, feature);
};
/**
* @inheritDoc
*/
CanvasImageReplay.prototype.finish = function() {
this.reverseHitDetectionInstructions();
// FIXME this doesn't really protect us against further calls to draw*Geometry
this.anchorX_ = undefined;
this.anchorY_ = undefined;
this.hitDetectionImage_ = null;
this.image_ = null;
this.height_ = undefined;
this.scale_ = undefined;
this.opacity_ = undefined;
this.originX_ = undefined;
this.originY_ = undefined;
this.rotateWithView_ = undefined;
this.rotation_ = undefined;
this.snapToPixel_ = undefined;
this.width_ = undefined;
};
/**
* @inheritDoc
*/
CanvasImageReplay.prototype.setImageStyle = function(imageStyle, declutterGroup) {
const anchor = imageStyle.getAnchor();
const size = imageStyle.getSize();
const hitDetectionImage = imageStyle.getHitDetectionImage(1);
const image = imageStyle.getImage(1);
const origin = imageStyle.getOrigin();
this.anchorX_ = anchor[0];
this.anchorY_ = anchor[1];
this.declutterGroup_ = /** @type {module:ol/render/canvas~DeclutterGroup} */ (declutterGroup);
this.hitDetectionImage_ = hitDetectionImage;
this.image_ = image;
this.height_ = size[1];
this.opacity_ = imageStyle.getOpacity();
this.originX_ = origin[0];
this.originY_ = origin[1];
this.rotateWithView_ = imageStyle.getRotateWithView();
this.rotation_ = imageStyle.getRotation();
this.scale_ = imageStyle.getScale();
this.snapToPixel_ = imageStyle.getSnapToPixel();
this.width_ = size[0];
};
export default CanvasImageReplay;

File diff suppressed because it is too large Load Diff

View File

@@ -16,111 +16,109 @@ import CanvasReplay from '../canvas/Replay.js';
* @param {?} declutterTree Declutter tree.
* @struct
*/
const CanvasLineStringReplay = function(
tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) {
CanvasReplay.call(this,
tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree);
};
class CanvasLineStringReplay {
constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) {
CanvasReplay.call(this,
tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree);
}
/**
* @param {Array.<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {number} end End.
* @param {number} stride Stride.
* @private
* @return {number} end.
*/
drawFlatCoordinates_(flatCoordinates, offset, end, stride) {
const myBegin = this.coordinates.length;
const myEnd = this.appendFlatCoordinates(
flatCoordinates, offset, end, stride, false, false);
const moveToLineToInstruction = [CanvasInstruction.MOVE_TO_LINE_TO, myBegin, myEnd];
this.instructions.push(moveToLineToInstruction);
this.hitDetectionInstructions.push(moveToLineToInstruction);
return end;
}
/**
* @inheritDoc
*/
drawLineString(lineStringGeometry, feature) {
const state = this.state;
const strokeStyle = state.strokeStyle;
const lineWidth = state.lineWidth;
if (strokeStyle === undefined || lineWidth === undefined) {
return;
}
this.updateStrokeStyle(state, this.applyStroke);
this.beginGeometry(lineStringGeometry, feature);
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
state.miterLimit, state.lineDash, state.lineDashOffset
], beginPathInstruction);
const flatCoordinates = lineStringGeometry.getFlatCoordinates();
const stride = lineStringGeometry.getStride();
this.drawFlatCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride);
this.hitDetectionInstructions.push(strokeInstruction);
this.endGeometry(lineStringGeometry, feature);
}
/**
* @inheritDoc
*/
drawMultiLineString(multiLineStringGeometry, feature) {
const state = this.state;
const strokeStyle = state.strokeStyle;
const lineWidth = state.lineWidth;
if (strokeStyle === undefined || lineWidth === undefined) {
return;
}
this.updateStrokeStyle(state, this.applyStroke);
this.beginGeometry(multiLineStringGeometry, feature);
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
state.miterLimit, state.lineDash, state.lineDashOffset
], beginPathInstruction);
const ends = multiLineStringGeometry.getEnds();
const flatCoordinates = multiLineStringGeometry.getFlatCoordinates();
const stride = multiLineStringGeometry.getStride();
let offset = 0;
for (let i = 0, ii = ends.length; i < ii; ++i) {
offset = this.drawFlatCoordinates_(flatCoordinates, offset, ends[i], stride);
}
this.hitDetectionInstructions.push(strokeInstruction);
this.endGeometry(multiLineStringGeometry, feature);
}
/**
* @inheritDoc
*/
finish() {
const state = this.state;
if (state.lastStroke != undefined && state.lastStroke != this.coordinates.length) {
this.instructions.push(strokeInstruction);
}
this.reverseHitDetectionInstructions();
this.state = null;
}
/**
* @inheritDoc.
*/
applyStroke(state) {
if (state.lastStroke != undefined && state.lastStroke != this.coordinates.length) {
this.instructions.push(strokeInstruction);
state.lastStroke = this.coordinates.length;
}
state.lastStroke = 0;
CanvasReplay.prototype.applyStroke.call(this, state);
this.instructions.push(beginPathInstruction);
}
}
inherits(CanvasLineStringReplay, CanvasReplay);
/**
* @param {Array.<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {number} end End.
* @param {number} stride Stride.
* @private
* @return {number} end.
*/
CanvasLineStringReplay.prototype.drawFlatCoordinates_ = function(flatCoordinates, offset, end, stride) {
const myBegin = this.coordinates.length;
const myEnd = this.appendFlatCoordinates(
flatCoordinates, offset, end, stride, false, false);
const moveToLineToInstruction = [CanvasInstruction.MOVE_TO_LINE_TO, myBegin, myEnd];
this.instructions.push(moveToLineToInstruction);
this.hitDetectionInstructions.push(moveToLineToInstruction);
return end;
};
/**
* @inheritDoc
*/
CanvasLineStringReplay.prototype.drawLineString = function(lineStringGeometry, feature) {
const state = this.state;
const strokeStyle = state.strokeStyle;
const lineWidth = state.lineWidth;
if (strokeStyle === undefined || lineWidth === undefined) {
return;
}
this.updateStrokeStyle(state, this.applyStroke);
this.beginGeometry(lineStringGeometry, feature);
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
state.miterLimit, state.lineDash, state.lineDashOffset
], beginPathInstruction);
const flatCoordinates = lineStringGeometry.getFlatCoordinates();
const stride = lineStringGeometry.getStride();
this.drawFlatCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride);
this.hitDetectionInstructions.push(strokeInstruction);
this.endGeometry(lineStringGeometry, feature);
};
/**
* @inheritDoc
*/
CanvasLineStringReplay.prototype.drawMultiLineString = function(multiLineStringGeometry, feature) {
const state = this.state;
const strokeStyle = state.strokeStyle;
const lineWidth = state.lineWidth;
if (strokeStyle === undefined || lineWidth === undefined) {
return;
}
this.updateStrokeStyle(state, this.applyStroke);
this.beginGeometry(multiLineStringGeometry, feature);
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
state.miterLimit, state.lineDash, state.lineDashOffset
], beginPathInstruction);
const ends = multiLineStringGeometry.getEnds();
const flatCoordinates = multiLineStringGeometry.getFlatCoordinates();
const stride = multiLineStringGeometry.getStride();
let offset = 0;
for (let i = 0, ii = ends.length; i < ii; ++i) {
offset = this.drawFlatCoordinates_(flatCoordinates, offset, ends[i], stride);
}
this.hitDetectionInstructions.push(strokeInstruction);
this.endGeometry(multiLineStringGeometry, feature);
};
/**
* @inheritDoc
*/
CanvasLineStringReplay.prototype.finish = function() {
const state = this.state;
if (state.lastStroke != undefined && state.lastStroke != this.coordinates.length) {
this.instructions.push(strokeInstruction);
}
this.reverseHitDetectionInstructions();
this.state = null;
};
/**
* @inheritDoc.
*/
CanvasLineStringReplay.prototype.applyStroke = function(state) {
if (state.lastStroke != undefined && state.lastStroke != this.coordinates.length) {
this.instructions.push(strokeInstruction);
state.lastStroke = this.coordinates.length;
}
state.lastStroke = 0;
CanvasReplay.prototype.applyStroke.call(this, state);
this.instructions.push(beginPathInstruction);
};
export default CanvasLineStringReplay;

View File

@@ -22,203 +22,200 @@ import CanvasReplay from '../canvas/Replay.js';
* @param {?} declutterTree Declutter tree.
* @struct
*/
const CanvasPolygonReplay = function(
tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) {
CanvasReplay.call(this,
tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree);
};
class CanvasPolygonReplay {
constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) {
CanvasReplay.call(this,
tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree);
}
/**
* @param {Array.<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {Array.<number>} ends Ends.
* @param {number} stride Stride.
* @private
* @return {number} End.
*/
drawFlatCoordinatess_(flatCoordinates, offset, ends, stride) {
const state = this.state;
const fill = state.fillStyle !== undefined;
const stroke = state.strokeStyle != undefined;
const numEnds = ends.length;
this.instructions.push(beginPathInstruction);
this.hitDetectionInstructions.push(beginPathInstruction);
for (let i = 0; i < numEnds; ++i) {
const end = ends[i];
const myBegin = this.coordinates.length;
const myEnd = this.appendFlatCoordinates(flatCoordinates, offset, end, stride, true, !stroke);
const moveToLineToInstruction = [CanvasInstruction.MOVE_TO_LINE_TO, myBegin, myEnd];
this.instructions.push(moveToLineToInstruction);
this.hitDetectionInstructions.push(moveToLineToInstruction);
if (stroke) {
// Performance optimization: only call closePath() when we have a stroke.
// Otherwise the ring is closed already (see appendFlatCoordinates above).
this.instructions.push(closePathInstruction);
this.hitDetectionInstructions.push(closePathInstruction);
}
offset = end;
}
if (fill) {
this.instructions.push(fillInstruction);
this.hitDetectionInstructions.push(fillInstruction);
}
if (stroke) {
this.instructions.push(strokeInstruction);
this.hitDetectionInstructions.push(strokeInstruction);
}
return offset;
}
/**
* @inheritDoc
*/
drawCircle(circleGeometry, feature) {
const state = this.state;
const fillStyle = state.fillStyle;
const strokeStyle = state.strokeStyle;
if (fillStyle === undefined && strokeStyle === undefined) {
return;
}
this.setFillStrokeStyles_(circleGeometry);
this.beginGeometry(circleGeometry, feature);
if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE,
asString(defaultFillStyle)
]);
}
if (state.strokeStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
state.miterLimit, state.lineDash, state.lineDashOffset
]);
}
const flatCoordinates = circleGeometry.getFlatCoordinates();
const stride = circleGeometry.getStride();
const myBegin = this.coordinates.length;
this.appendFlatCoordinates(
flatCoordinates, 0, flatCoordinates.length, stride, false, false);
const circleInstruction = [CanvasInstruction.CIRCLE, myBegin];
this.instructions.push(beginPathInstruction, circleInstruction);
this.hitDetectionInstructions.push(beginPathInstruction, circleInstruction);
this.hitDetectionInstructions.push(fillInstruction);
if (state.fillStyle !== undefined) {
this.instructions.push(fillInstruction);
}
if (state.strokeStyle !== undefined) {
this.instructions.push(strokeInstruction);
this.hitDetectionInstructions.push(strokeInstruction);
}
this.endGeometry(circleGeometry, feature);
}
/**
* @inheritDoc
*/
drawPolygon(polygonGeometry, feature) {
const state = this.state;
const fillStyle = state.fillStyle;
const strokeStyle = state.strokeStyle;
if (fillStyle === undefined && strokeStyle === undefined) {
return;
}
this.setFillStrokeStyles_(polygonGeometry);
this.beginGeometry(polygonGeometry, feature);
if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE,
asString(defaultFillStyle)
]);
}
if (state.strokeStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
state.miterLimit, state.lineDash, state.lineDashOffset
]);
}
const ends = polygonGeometry.getEnds();
const flatCoordinates = polygonGeometry.getOrientedFlatCoordinates();
const stride = polygonGeometry.getStride();
this.drawFlatCoordinatess_(flatCoordinates, 0, ends, stride);
this.endGeometry(polygonGeometry, feature);
}
/**
* @inheritDoc
*/
drawMultiPolygon(multiPolygonGeometry, feature) {
const state = this.state;
const fillStyle = state.fillStyle;
const strokeStyle = state.strokeStyle;
if (fillStyle === undefined && strokeStyle === undefined) {
return;
}
this.setFillStrokeStyles_(multiPolygonGeometry);
this.beginGeometry(multiPolygonGeometry, feature);
if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE,
asString(defaultFillStyle)
]);
}
if (state.strokeStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
state.miterLimit, state.lineDash, state.lineDashOffset
]);
}
const endss = multiPolygonGeometry.getEndss();
const flatCoordinates = multiPolygonGeometry.getOrientedFlatCoordinates();
const stride = multiPolygonGeometry.getStride();
let offset = 0;
for (let i = 0, ii = endss.length; i < ii; ++i) {
offset = this.drawFlatCoordinatess_(flatCoordinates, offset, endss[i], stride);
}
this.endGeometry(multiPolygonGeometry, feature);
}
/**
* @inheritDoc
*/
finish() {
this.reverseHitDetectionInstructions();
this.state = null;
// We want to preserve topology when drawing polygons. Polygons are
// simplified using quantization and point elimination. However, we might
// have received a mix of quantized and non-quantized geometries, so ensure
// that all are quantized by quantizing all coordinates in the batch.
const tolerance = this.tolerance;
if (tolerance !== 0) {
const coordinates = this.coordinates;
for (let i = 0, ii = coordinates.length; i < ii; ++i) {
coordinates[i] = snap(coordinates[i], tolerance);
}
}
}
/**
* @private
* @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry.
*/
setFillStrokeStyles_(geometry) {
const state = this.state;
const fillStyle = state.fillStyle;
if (fillStyle !== undefined) {
this.updateFillStyle(state, this.createFill, geometry);
}
if (state.strokeStyle !== undefined) {
this.updateStrokeStyle(state, this.applyStroke);
}
}
}
inherits(CanvasPolygonReplay, CanvasReplay);
/**
* @param {Array.<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {Array.<number>} ends Ends.
* @param {number} stride Stride.
* @private
* @return {number} End.
*/
CanvasPolygonReplay.prototype.drawFlatCoordinatess_ = function(flatCoordinates, offset, ends, stride) {
const state = this.state;
const fill = state.fillStyle !== undefined;
const stroke = state.strokeStyle != undefined;
const numEnds = ends.length;
this.instructions.push(beginPathInstruction);
this.hitDetectionInstructions.push(beginPathInstruction);
for (let i = 0; i < numEnds; ++i) {
const end = ends[i];
const myBegin = this.coordinates.length;
const myEnd = this.appendFlatCoordinates(flatCoordinates, offset, end, stride, true, !stroke);
const moveToLineToInstruction = [CanvasInstruction.MOVE_TO_LINE_TO, myBegin, myEnd];
this.instructions.push(moveToLineToInstruction);
this.hitDetectionInstructions.push(moveToLineToInstruction);
if (stroke) {
// Performance optimization: only call closePath() when we have a stroke.
// Otherwise the ring is closed already (see appendFlatCoordinates above).
this.instructions.push(closePathInstruction);
this.hitDetectionInstructions.push(closePathInstruction);
}
offset = end;
}
if (fill) {
this.instructions.push(fillInstruction);
this.hitDetectionInstructions.push(fillInstruction);
}
if (stroke) {
this.instructions.push(strokeInstruction);
this.hitDetectionInstructions.push(strokeInstruction);
}
return offset;
};
/**
* @inheritDoc
*/
CanvasPolygonReplay.prototype.drawCircle = function(circleGeometry, feature) {
const state = this.state;
const fillStyle = state.fillStyle;
const strokeStyle = state.strokeStyle;
if (fillStyle === undefined && strokeStyle === undefined) {
return;
}
this.setFillStrokeStyles_(circleGeometry);
this.beginGeometry(circleGeometry, feature);
if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE,
asString(defaultFillStyle)
]);
}
if (state.strokeStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
state.miterLimit, state.lineDash, state.lineDashOffset
]);
}
const flatCoordinates = circleGeometry.getFlatCoordinates();
const stride = circleGeometry.getStride();
const myBegin = this.coordinates.length;
this.appendFlatCoordinates(
flatCoordinates, 0, flatCoordinates.length, stride, false, false);
const circleInstruction = [CanvasInstruction.CIRCLE, myBegin];
this.instructions.push(beginPathInstruction, circleInstruction);
this.hitDetectionInstructions.push(beginPathInstruction, circleInstruction);
this.hitDetectionInstructions.push(fillInstruction);
if (state.fillStyle !== undefined) {
this.instructions.push(fillInstruction);
}
if (state.strokeStyle !== undefined) {
this.instructions.push(strokeInstruction);
this.hitDetectionInstructions.push(strokeInstruction);
}
this.endGeometry(circleGeometry, feature);
};
/**
* @inheritDoc
*/
CanvasPolygonReplay.prototype.drawPolygon = function(polygonGeometry, feature) {
const state = this.state;
const fillStyle = state.fillStyle;
const strokeStyle = state.strokeStyle;
if (fillStyle === undefined && strokeStyle === undefined) {
return;
}
this.setFillStrokeStyles_(polygonGeometry);
this.beginGeometry(polygonGeometry, feature);
if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE,
asString(defaultFillStyle)
]);
}
if (state.strokeStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
state.miterLimit, state.lineDash, state.lineDashOffset
]);
}
const ends = polygonGeometry.getEnds();
const flatCoordinates = polygonGeometry.getOrientedFlatCoordinates();
const stride = polygonGeometry.getStride();
this.drawFlatCoordinatess_(flatCoordinates, 0, ends, stride);
this.endGeometry(polygonGeometry, feature);
};
/**
* @inheritDoc
*/
CanvasPolygonReplay.prototype.drawMultiPolygon = function(multiPolygonGeometry, feature) {
const state = this.state;
const fillStyle = state.fillStyle;
const strokeStyle = state.strokeStyle;
if (fillStyle === undefined && strokeStyle === undefined) {
return;
}
this.setFillStrokeStyles_(multiPolygonGeometry);
this.beginGeometry(multiPolygonGeometry, feature);
if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE,
asString(defaultFillStyle)
]);
}
if (state.strokeStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
state.miterLimit, state.lineDash, state.lineDashOffset
]);
}
const endss = multiPolygonGeometry.getEndss();
const flatCoordinates = multiPolygonGeometry.getOrientedFlatCoordinates();
const stride = multiPolygonGeometry.getStride();
let offset = 0;
for (let i = 0, ii = endss.length; i < ii; ++i) {
offset = this.drawFlatCoordinatess_(flatCoordinates, offset, endss[i], stride);
}
this.endGeometry(multiPolygonGeometry, feature);
};
/**
* @inheritDoc
*/
CanvasPolygonReplay.prototype.finish = function() {
this.reverseHitDetectionInstructions();
this.state = null;
// We want to preserve topology when drawing polygons. Polygons are
// simplified using quantization and point elimination. However, we might
// have received a mix of quantized and non-quantized geometries, so ensure
// that all are quantized by quantizing all coordinates in the batch.
const tolerance = this.tolerance;
if (tolerance !== 0) {
const coordinates = this.coordinates;
for (let i = 0, ii = coordinates.length; i < ii; ++i) {
coordinates[i] = snap(coordinates[i], tolerance);
}
}
};
/**
* @private
* @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry.
*/
CanvasPolygonReplay.prototype.setFillStrokeStyles_ = function(geometry) {
const state = this.state;
const fillStyle = state.fillStyle;
if (fillStyle !== undefined) {
this.updateFillStyle(state, this.createFill, geometry);
}
if (state.strokeStyle !== undefined) {
this.updateStrokeStyle(state, this.applyStroke);
}
};
export default CanvasPolygonReplay;

File diff suppressed because it is too large Load Diff

View File

@@ -46,76 +46,366 @@ const BATCH_CONSTRUCTORS = {
* @param {number=} opt_renderBuffer Optional rendering buffer.
* @struct
*/
const CanvasReplayGroup = function(
tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree, opt_renderBuffer) {
ReplayGroup.call(this);
class CanvasReplayGroup {
constructor(
tolerance,
maxExtent,
resolution,
pixelRatio,
overlaps,
declutterTree,
opt_renderBuffer
) {
ReplayGroup.call(this);
/**
* Declutter tree.
* @private
*/
this.declutterTree_ = declutterTree;
/**
* @type {module:ol/render/canvas~DeclutterGroup}
* @private
*/
this.declutterGroup_ = null;
/**
* @private
* @type {number}
*/
this.tolerance_ = tolerance;
/**
* @private
* @type {module:ol/extent~Extent}
*/
this.maxExtent_ = maxExtent;
/**
* @private
* @type {boolean}
*/
this.overlaps_ = overlaps;
/**
* @private
* @type {number}
*/
this.pixelRatio_ = pixelRatio;
/**
* @private
* @type {number}
*/
this.resolution_ = resolution;
/**
* @private
* @type {number|undefined}
*/
this.renderBuffer_ = opt_renderBuffer;
/**
* @private
* @type {!Object.<string, !Object.<module:ol/render/ReplayType, module:ol/render/canvas/Replay>>}
*/
this.replaysByZIndex_ = {};
/**
* @private
* @type {CanvasRenderingContext2D}
*/
this.hitDetectionContext_ = createCanvasContext2D(1, 1);
/**
* @private
* @type {module:ol/transform~Transform}
*/
this.hitDetectionTransform_ = createTransform();
}
/**
* Declutter tree.
* @private
* @param {boolean} group Group with previous replay.
* @return {module:ol/render/canvas~DeclutterGroup} Declutter instruction group.
*/
this.declutterTree_ = declutterTree;
addDeclutter(group) {
let declutter = null;
if (this.declutterTree_) {
if (group) {
declutter = this.declutterGroup_;
/** @type {number} */ (declutter[4])++;
} else {
declutter = this.declutterGroup_ = createEmpty();
declutter.push(1);
}
}
return declutter;
}
/**
* @type {module:ol/render/canvas~DeclutterGroup}
* @private
* @param {CanvasRenderingContext2D} context Context.
* @param {module:ol/transform~Transform} transform Transform.
*/
this.declutterGroup_ = null;
clip(context, transform) {
const flatClipCoords = this.getClipCoords(transform);
context.beginPath();
context.moveTo(flatClipCoords[0], flatClipCoords[1]);
context.lineTo(flatClipCoords[2], flatClipCoords[3]);
context.lineTo(flatClipCoords[4], flatClipCoords[5]);
context.lineTo(flatClipCoords[6], flatClipCoords[7]);
context.clip();
}
/**
* @private
* @type {number}
* @param {Array.<module:ol/render/ReplayType>} replays Replays.
* @return {boolean} Has replays of the provided types.
*/
this.tolerance_ = tolerance;
hasReplays(replays) {
for (const zIndex in this.replaysByZIndex_) {
const candidates = this.replaysByZIndex_[zIndex];
for (let i = 0, ii = replays.length; i < ii; ++i) {
if (replays[i] in candidates) {
return true;
}
}
}
return false;
}
/**
* @private
* @type {module:ol/extent~Extent}
* FIXME empty description for jsdoc
*/
this.maxExtent_ = maxExtent;
finish() {
for (const zKey in this.replaysByZIndex_) {
const replays = this.replaysByZIndex_[zKey];
for (const replayKey in replays) {
replays[replayKey].finish();
}
}
}
/**
* @private
* @type {boolean}
* @param {module:ol/coordinate~Coordinate} coordinate Coordinate.
* @param {number} resolution Resolution.
* @param {number} rotation Rotation.
* @param {number} hitTolerance Hit tolerance in pixels.
* @param {Object.<string, boolean>} skippedFeaturesHash Ids of features to skip.
* @param {function((module:ol/Feature|module:ol/render/Feature)): T} callback Feature callback.
* @param {Object.<string, module:ol/render/canvas~DeclutterGroup>} declutterReplays Declutter replays.
* @return {T|undefined} Callback result.
* @template T
*/
this.overlaps_ = overlaps;
forEachFeatureAtCoordinate(
coordinate,
resolution,
rotation,
hitTolerance,
skippedFeaturesHash,
callback,
declutterReplays
) {
hitTolerance = Math.round(hitTolerance);
const contextSize = hitTolerance * 2 + 1;
const transform = composeTransform(this.hitDetectionTransform_,
hitTolerance + 0.5, hitTolerance + 0.5,
1 / resolution, -1 / resolution,
-rotation,
-coordinate[0], -coordinate[1]);
const context = this.hitDetectionContext_;
if (context.canvas.width !== contextSize || context.canvas.height !== contextSize) {
context.canvas.width = contextSize;
context.canvas.height = contextSize;
} else {
context.clearRect(0, 0, contextSize, contextSize);
}
/**
* @type {module:ol/extent~Extent}
*/
let hitExtent;
if (this.renderBuffer_ !== undefined) {
hitExtent = createEmpty();
extendCoordinate(hitExtent, coordinate);
buffer(hitExtent, resolution * (this.renderBuffer_ + hitTolerance), hitExtent);
}
const mask = getCircleArray(hitTolerance);
let declutteredFeatures;
if (this.declutterTree_) {
declutteredFeatures = this.declutterTree_.all().map(function(entry) {
return entry.value;
});
}
let replayType;
/**
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
* @return {?} Callback result.
*/
function featureCallback(feature) {
const imageData = context.getImageData(0, 0, contextSize, contextSize).data;
for (let i = 0; i < contextSize; i++) {
for (let j = 0; j < contextSize; j++) {
if (mask[i][j]) {
if (imageData[(j * contextSize + i) * 4 + 3] > 0) {
let result;
if (!(declutteredFeatures && (replayType == ReplayType.IMAGE || replayType == ReplayType.TEXT)) ||
declutteredFeatures.indexOf(feature) !== -1) {
result = callback(feature);
}
if (result) {
return result;
} else {
context.clearRect(0, 0, contextSize, contextSize);
return undefined;
}
}
}
}
}
}
/** @type {Array.<number>} */
const zs = Object.keys(this.replaysByZIndex_).map(Number);
zs.sort(numberSafeCompareFunction);
let i, j, replays, replay, result;
for (i = zs.length - 1; i >= 0; --i) {
const zIndexKey = zs[i].toString();
replays = this.replaysByZIndex_[zIndexKey];
for (j = ORDER.length - 1; j >= 0; --j) {
replayType = ORDER[j];
replay = replays[replayType];
if (replay !== undefined) {
if (declutterReplays &&
(replayType == ReplayType.IMAGE || replayType == ReplayType.TEXT)) {
const declutter = declutterReplays[zIndexKey];
if (!declutter) {
declutterReplays[zIndexKey] = [replay, transform.slice(0)];
} else {
declutter.push(replay, transform.slice(0));
}
} else {
result = replay.replayHitDetection(context, transform, rotation,
skippedFeaturesHash, featureCallback, hitExtent);
if (result) {
return result;
}
}
}
}
}
return undefined;
}
/**
* @private
* @type {number}
* @param {module:ol/transform~Transform} transform Transform.
* @return {Array.<number>} Clip coordinates.
*/
this.pixelRatio_ = pixelRatio;
getClipCoords(transform) {
const maxExtent = this.maxExtent_;
const minX = maxExtent[0];
const minY = maxExtent[1];
const maxX = maxExtent[2];
const maxY = maxExtent[3];
const flatClipCoords = [minX, minY, minX, maxY, maxX, maxY, maxX, minY];
transform2D(
flatClipCoords, 0, 8, 2, transform, flatClipCoords);
return flatClipCoords;
}
/**
* @private
* @type {number}
* @inheritDoc
*/
this.resolution_ = resolution;
getReplay(zIndex, replayType) {
const zIndexKey = zIndex !== undefined ? zIndex.toString() : '0';
let replays = this.replaysByZIndex_[zIndexKey];
if (replays === undefined) {
replays = {};
this.replaysByZIndex_[zIndexKey] = replays;
}
let replay = replays[replayType];
if (replay === undefined) {
const Constructor = BATCH_CONSTRUCTORS[replayType];
replay = new Constructor(this.tolerance_, this.maxExtent_,
this.resolution_, this.pixelRatio_, this.overlaps_, this.declutterTree_);
replays[replayType] = replay;
}
return replay;
}
/**
* @private
* @type {number|undefined}
* @return {Object.<string, Object.<module:ol/render/ReplayType, module:ol/render/canvas/Replay>>} Replays.
*/
this.renderBuffer_ = opt_renderBuffer;
getReplays() {
return this.replaysByZIndex_;
}
/**
* @private
* @type {!Object.<string, !Object.<module:ol/render/ReplayType, module:ol/render/canvas/Replay>>}
* @inheritDoc
*/
this.replaysByZIndex_ = {};
isEmpty() {
return isEmpty(this.replaysByZIndex_);
}
/**
* @private
* @type {CanvasRenderingContext2D}
* @param {CanvasRenderingContext2D} context Context.
* @param {module:ol/transform~Transform} transform Transform.
* @param {number} viewRotation View rotation.
* @param {Object.<string, boolean>} skippedFeaturesHash Ids of features to skip.
* @param {Array.<module:ol/render/ReplayType>=} opt_replayTypes Ordered replay types to replay.
* Default is {@link module:ol/render/replay~ORDER}
* @param {Object.<string, module:ol/render/canvas~DeclutterGroup>=} opt_declutterReplays Declutter replays.
*/
this.hitDetectionContext_ = createCanvasContext2D(1, 1);
replay(
context,
transform,
viewRotation,
skippedFeaturesHash,
opt_replayTypes,
opt_declutterReplays
) {
/**
* @private
* @type {module:ol/transform~Transform}
*/
this.hitDetectionTransform_ = createTransform();
};
/** @type {Array.<number>} */
const zs = Object.keys(this.replaysByZIndex_).map(Number);
zs.sort(numberSafeCompareFunction);
// setup clipping so that the parts of over-simplified geometries are not
// visible outside the current extent when panning
context.save();
this.clip(context, transform);
const replayTypes = opt_replayTypes ? opt_replayTypes : ORDER;
let i, ii, j, jj, replays, replay;
for (i = 0, ii = zs.length; i < ii; ++i) {
const zIndexKey = zs[i].toString();
replays = this.replaysByZIndex_[zIndexKey];
for (j = 0, jj = replayTypes.length; j < jj; ++j) {
const replayType = replayTypes[j];
replay = replays[replayType];
if (replay !== undefined) {
if (opt_declutterReplays &&
(replayType == ReplayType.IMAGE || replayType == ReplayType.TEXT)) {
const declutter = opt_declutterReplays[zIndexKey];
if (!declutter) {
opt_declutterReplays[zIndexKey] = [replay, transform.slice(0)];
} else {
declutter.push(replay, transform.slice(0));
}
} else {
replay.replay(context, transform, viewRotation, skippedFeaturesHash);
}
}
}
}
context.restore();
}
}
inherits(CanvasReplayGroup, ReplayGroup);
@@ -217,281 +507,4 @@ export function replayDeclutter(declutterReplays, context, rotation) {
}
/**
* @param {boolean} group Group with previous replay.
* @return {module:ol/render/canvas~DeclutterGroup} Declutter instruction group.
*/
CanvasReplayGroup.prototype.addDeclutter = function(group) {
let declutter = null;
if (this.declutterTree_) {
if (group) {
declutter = this.declutterGroup_;
/** @type {number} */ (declutter[4])++;
} else {
declutter = this.declutterGroup_ = createEmpty();
declutter.push(1);
}
}
return declutter;
};
/**
* @param {CanvasRenderingContext2D} context Context.
* @param {module:ol/transform~Transform} transform Transform.
*/
CanvasReplayGroup.prototype.clip = function(context, transform) {
const flatClipCoords = this.getClipCoords(transform);
context.beginPath();
context.moveTo(flatClipCoords[0], flatClipCoords[1]);
context.lineTo(flatClipCoords[2], flatClipCoords[3]);
context.lineTo(flatClipCoords[4], flatClipCoords[5]);
context.lineTo(flatClipCoords[6], flatClipCoords[7]);
context.clip();
};
/**
* @param {Array.<module:ol/render/ReplayType>} replays Replays.
* @return {boolean} Has replays of the provided types.
*/
CanvasReplayGroup.prototype.hasReplays = function(replays) {
for (const zIndex in this.replaysByZIndex_) {
const candidates = this.replaysByZIndex_[zIndex];
for (let i = 0, ii = replays.length; i < ii; ++i) {
if (replays[i] in candidates) {
return true;
}
}
}
return false;
};
/**
* FIXME empty description for jsdoc
*/
CanvasReplayGroup.prototype.finish = function() {
for (const zKey in this.replaysByZIndex_) {
const replays = this.replaysByZIndex_[zKey];
for (const replayKey in replays) {
replays[replayKey].finish();
}
}
};
/**
* @param {module:ol/coordinate~Coordinate} coordinate Coordinate.
* @param {number} resolution Resolution.
* @param {number} rotation Rotation.
* @param {number} hitTolerance Hit tolerance in pixels.
* @param {Object.<string, boolean>} skippedFeaturesHash Ids of features to skip.
* @param {function((module:ol/Feature|module:ol/render/Feature)): T} callback Feature callback.
* @param {Object.<string, module:ol/render/canvas~DeclutterGroup>} declutterReplays Declutter replays.
* @return {T|undefined} Callback result.
* @template T
*/
CanvasReplayGroup.prototype.forEachFeatureAtCoordinate = function(
coordinate, resolution, rotation, hitTolerance, skippedFeaturesHash, callback, declutterReplays) {
hitTolerance = Math.round(hitTolerance);
const contextSize = hitTolerance * 2 + 1;
const transform = composeTransform(this.hitDetectionTransform_,
hitTolerance + 0.5, hitTolerance + 0.5,
1 / resolution, -1 / resolution,
-rotation,
-coordinate[0], -coordinate[1]);
const context = this.hitDetectionContext_;
if (context.canvas.width !== contextSize || context.canvas.height !== contextSize) {
context.canvas.width = contextSize;
context.canvas.height = contextSize;
} else {
context.clearRect(0, 0, contextSize, contextSize);
}
/**
* @type {module:ol/extent~Extent}
*/
let hitExtent;
if (this.renderBuffer_ !== undefined) {
hitExtent = createEmpty();
extendCoordinate(hitExtent, coordinate);
buffer(hitExtent, resolution * (this.renderBuffer_ + hitTolerance), hitExtent);
}
const mask = getCircleArray(hitTolerance);
let declutteredFeatures;
if (this.declutterTree_) {
declutteredFeatures = this.declutterTree_.all().map(function(entry) {
return entry.value;
});
}
let replayType;
/**
* @param {module:ol/Feature|module:ol/render/Feature} feature Feature.
* @return {?} Callback result.
*/
function featureCallback(feature) {
const imageData = context.getImageData(0, 0, contextSize, contextSize).data;
for (let i = 0; i < contextSize; i++) {
for (let j = 0; j < contextSize; j++) {
if (mask[i][j]) {
if (imageData[(j * contextSize + i) * 4 + 3] > 0) {
let result;
if (!(declutteredFeatures && (replayType == ReplayType.IMAGE || replayType == ReplayType.TEXT)) ||
declutteredFeatures.indexOf(feature) !== -1) {
result = callback(feature);
}
if (result) {
return result;
} else {
context.clearRect(0, 0, contextSize, contextSize);
return undefined;
}
}
}
}
}
}
/** @type {Array.<number>} */
const zs = Object.keys(this.replaysByZIndex_).map(Number);
zs.sort(numberSafeCompareFunction);
let i, j, replays, replay, result;
for (i = zs.length - 1; i >= 0; --i) {
const zIndexKey = zs[i].toString();
replays = this.replaysByZIndex_[zIndexKey];
for (j = ORDER.length - 1; j >= 0; --j) {
replayType = ORDER[j];
replay = replays[replayType];
if (replay !== undefined) {
if (declutterReplays &&
(replayType == ReplayType.IMAGE || replayType == ReplayType.TEXT)) {
const declutter = declutterReplays[zIndexKey];
if (!declutter) {
declutterReplays[zIndexKey] = [replay, transform.slice(0)];
} else {
declutter.push(replay, transform.slice(0));
}
} else {
result = replay.replayHitDetection(context, transform, rotation,
skippedFeaturesHash, featureCallback, hitExtent);
if (result) {
return result;
}
}
}
}
}
return undefined;
};
/**
* @param {module:ol/transform~Transform} transform Transform.
* @return {Array.<number>} Clip coordinates.
*/
CanvasReplayGroup.prototype.getClipCoords = function(transform) {
const maxExtent = this.maxExtent_;
const minX = maxExtent[0];
const minY = maxExtent[1];
const maxX = maxExtent[2];
const maxY = maxExtent[3];
const flatClipCoords = [minX, minY, minX, maxY, maxX, maxY, maxX, minY];
transform2D(
flatClipCoords, 0, 8, 2, transform, flatClipCoords);
return flatClipCoords;
};
/**
* @inheritDoc
*/
CanvasReplayGroup.prototype.getReplay = function(zIndex, replayType) {
const zIndexKey = zIndex !== undefined ? zIndex.toString() : '0';
let replays = this.replaysByZIndex_[zIndexKey];
if (replays === undefined) {
replays = {};
this.replaysByZIndex_[zIndexKey] = replays;
}
let replay = replays[replayType];
if (replay === undefined) {
const Constructor = BATCH_CONSTRUCTORS[replayType];
replay = new Constructor(this.tolerance_, this.maxExtent_,
this.resolution_, this.pixelRatio_, this.overlaps_, this.declutterTree_);
replays[replayType] = replay;
}
return replay;
};
/**
* @return {Object.<string, Object.<module:ol/render/ReplayType, module:ol/render/canvas/Replay>>} Replays.
*/
CanvasReplayGroup.prototype.getReplays = function() {
return this.replaysByZIndex_;
};
/**
* @inheritDoc
*/
CanvasReplayGroup.prototype.isEmpty = function() {
return isEmpty(this.replaysByZIndex_);
};
/**
* @param {CanvasRenderingContext2D} context Context.
* @param {module:ol/transform~Transform} transform Transform.
* @param {number} viewRotation View rotation.
* @param {Object.<string, boolean>} skippedFeaturesHash Ids of features to skip.
* @param {Array.<module:ol/render/ReplayType>=} opt_replayTypes Ordered replay types to replay.
* Default is {@link module:ol/render/replay~ORDER}
* @param {Object.<string, module:ol/render/canvas~DeclutterGroup>=} opt_declutterReplays Declutter replays.
*/
CanvasReplayGroup.prototype.replay = function(context,
transform, viewRotation, skippedFeaturesHash, opt_replayTypes, opt_declutterReplays) {
/** @type {Array.<number>} */
const zs = Object.keys(this.replaysByZIndex_).map(Number);
zs.sort(numberSafeCompareFunction);
// setup clipping so that the parts of over-simplified geometries are not
// visible outside the current extent when panning
context.save();
this.clip(context, transform);
const replayTypes = opt_replayTypes ? opt_replayTypes : ORDER;
let i, ii, j, jj, replays, replay;
for (i = 0, ii = zs.length; i < ii; ++i) {
const zIndexKey = zs[i].toString();
replays = this.replaysByZIndex_[zIndexKey];
for (j = 0, jj = replayTypes.length; j < jj; ++j) {
const replayType = replayTypes[j];
replay = replays[replayType];
if (replay !== undefined) {
if (opt_declutterReplays &&
(replayType == ReplayType.IMAGE || replayType == ReplayType.TEXT)) {
const declutter = opt_declutterReplays[zIndexKey];
if (!declutter) {
opt_declutterReplays[zIndexKey] = [replay, transform.slice(0)];
} else {
declutter.push(replay, transform.slice(0));
}
} else {
replay.replay(context, transform, viewRotation, skippedFeaturesHash);
}
}
}
}
context.restore();
};
export default CanvasReplayGroup;

Some files were not shown because too many files have changed in this diff Show More