diff --git a/api/Map.js b/api/Map.js new file mode 100644 index 000000000000..79b858975133 --- /dev/null +++ b/api/Map.js @@ -0,0 +1,191 @@ +// see https://github.com/camptocamp/cgxp/blob/master/core/src/script/CGXP/api/Map.js + +import OLMap from 'ol/Map.js'; +import Feature from 'ol/Feature.js'; +import Point from 'ol/geom/Point.js'; +import {Icon, Style} from 'ol/style.js'; +import View from 'ol/View.js'; +import VectorSource from 'ol/source/Vector.js'; +import VectorLayer from 'ol/layer/Vector.js'; + +import {get as getProjection} from 'ol/proj.js'; + +import * as constants from './constants.js'; + + +class Map { + + /** + * @param {Object} options + * @property {string} div + * @property {ol.Coordinate} center + * @property {number} [zoom=10] + * TODO: more options + */ + constructor(options) { + /** + * @private + * @type {View} + */ + this.view_ = new View({ + projection: getProjection(constants.projection), + resolutions: constants.resolutions, + zoom: options.zoom, + center: options.center + }); + + /** + * @private + * @type {OLMap} + */ + this.map_ = new OLMap({ + target: options.div, + view: this.view_ + }); + + /** + * @private + * @type {VectorSource} + */ + this.vectorSource_ = new VectorSource(); + + /** + * @private + * @type {VectorLayer} + */ + this.vectorLayer_ = new VectorLayer({ + source: this.vectorSource_ + }); + + this.map_.addLayer(this.vectorLayer_); + } + + /** + * @param {ol.Coordinate} center + * @param {number} zoom + */ + recenter(center, zoom) { + this.view_.setCenter(center); + this.view_.setZoom(zoom); + } + + /** + * @param {Object} options + * @property {ol.Coordinate} position + * @property {string} [icon] + * @property {ol.Size} [size] + */ + addMarker(options = {}) { + const marker = new Feature({ + geometry: new Point(options.position ? options.position : this.view_.getCenter()) + }); + if (options.icon) { + // FIXME: use size? + marker.setStyle(new Style({ + image: new Icon({ + src: options.icon + }) + })); + } + this.vectorSource_.addFeature(marker); + } + + /** + * @param {string} layer + * @param {Array.} layer + * @param {boolean} [highlight=false] + */ + recenterOnObjects(layer, ids, highlight = false) { + + } + + /** + * @param {string} type + * @param {string} name + * @param {string} url + * @param {Object} [options] + * @property {Array.} [attr=['title', 'description']] + * @property {function()} [success] + * @property {function()} [error] + */ + addCustomLayer(type, name, url, options = {}) { + if (type === 'text') { + fetch(url) + .then((response) => response.text()) + .then((text) => { + const attr = options.attr || ['title', 'description']; + const lines = text.split(/\r\n|\r|\n/); + const columns = lines.shift().split('\t'); + for (const line of lines) { + if (line) { + const values = zip(columns, line.split('\t')); + const marker = new Feature({ + geometry: new Point(values.point.split(',').map(parseFloat)) + }); + marker.setProperties(filterByKeys(values, attr)); + marker.setId(values.id); + // FIXME: handle values.iconSize + // FIXME: handle values.iconOffset + marker.setStyle(new Style({ + image: new Icon({ + src: values.icon + }) + })); + this.vectorSource_.addFeature(marker); + } + } + this.view_.fit(this.vectorSource_.getExtent()); + }) + .then(() => { + if (options.success) { + options.success(); + } + }) + .catch(() => { + if (options.error) { + options.error(); + } + }) + } + } + + /** + * @param {string} id + */ + selectObject(id) { + const feature = this.vectorSource_.getFeatureById(id); + if (feature) { + // TODO + } + } + +} + + +/** + * @param {Array.} keys + * @param {Array.<*>} values + */ +function zip(keys, values) { + const obj = {}; + keys.forEach((key, index) => { + obj[key] = values[index]; + }); + return obj; +} + + +/** + * @param {Object.} obj + * @param {Array.} keys + */ +function filterByKeys(obj, keys) { + const filtered = {}; + keys.forEach((key) => { + filtered[key] = obj[key]; + }); + return filtered; +} + + +export default Map; diff --git a/api/constants.js b/api/constants.js new file mode 100644 index 000000000000..35c895dd85d5 --- /dev/null +++ b/api/constants.js @@ -0,0 +1,16 @@ +/** + * @type {string} + */ +export const projection = 'EPSG:2056'; + + +/** + * @type {Array.} + */ +export const resolutions = [250, 100, 50, 20, 10, 5, 2, 1, 0.5, 0.25, 0.1, 0.05]; + + +/** + * @type {string} + */ +export const backgroundLayer = 'plan'; diff --git a/api/index.js b/api/index.js new file mode 100644 index 000000000000..5fcbf2f074cb --- /dev/null +++ b/api/index.js @@ -0,0 +1,7 @@ +import Map from './Map.js'; + +const lib = { + Map +}; + +export default lib; diff --git a/buildtools/webpack.api.js b/buildtools/webpack.api.js new file mode 100644 index 000000000000..71422180e852 --- /dev/null +++ b/buildtools/webpack.api.js @@ -0,0 +1,13 @@ +module.exports = (env, argv) => { + const library = argv.library ? argv.library : 'demo'; + return { + entry: './api/index.js', + output: { + filename: 'api.js', + libraryTarget: 'umd', + globalObject: 'this', + libraryExport: 'default', + library: library + } + }; +}; diff --git a/package.json b/package.json index 207cb60ce10e..4347acb7e1e5 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,8 @@ "build-ngeo-examples": "TARGET=ngeo-examples webpack --progress --debug", "build-gmf-examples": "TARGET=gmf-examples webpack --progress --debug", "build-gmf-apps": "TARGET=gmf-apps webpack --progress --debug", + "build-api": "webpack --config buildtools/webpack.api.js --mode development --library demo", + "serve-api": "webpack-dev-server --mode development --content-base dist/ --config buildtools/webpack.api.js --port 3000 --progress --watch --bail --debug", "serve-ngeo-examples": "DEV_SERVER=1 TARGET=ngeo-examples webpack-dev-server --port 3000 --progress --watch --bail --debug", "serve-gmf-examples": "DEV_SERVER=1 TARGET=gmf-examples webpack-dev-server --port 3000 --progress --watch --bail --debug", "serve-gmf-apps": "DEV_SERVER=1 TARGET=gmf-apps webpack-dev-server --port 3000 --progress --watch --bail --debug"