Support formats that read ArrayBuffer sources

Drag-and-Drop support for formats that read arraybuffer sources
Add TextDecoder to polyfills
Add custom Drag-and-Drop examples for KMZ and MVT
Add sample download and KML styles option to existing example
Test arraybuffer and constructed formats
This commit is contained in:
mike-000
2020-11-06 16:22:34 +00:00
parent bdd343e87b
commit a6860118c7
17 changed files with 5280 additions and 34 deletions

View File

@@ -19,7 +19,7 @@ OpenLayers is available as [`ol` npm package](https://npmjs.com/package/ol), whi
By default, OpenLayers uses a performance optimized Canvas renderer.
OpenLayers runs on all modern browsers that support [HTML5](https://html.spec.whatwg.org/multipage/) and [ECMAScript 5](http://www.ecma-international.org/ecma-262/5.1/). This includes Chrome, Firefox, Safari and Edge. For older browsers and platforms like Internet Explorer (down to version 9) and Android 4.x, [polyfills](http://polyfill.io), the application bundle needs to be transpiled (e.g. using [Babel](https://babeljs.io)) and bundled with polyfills for `requestAnimationFrame`, `Element.prototype.classList` and `URL`.
OpenLayers runs on all modern browsers that support [HTML5](https://html.spec.whatwg.org/multipage/) and [ECMAScript 5](http://www.ecma-international.org/ecma-262/5.1/). This includes Chrome, Firefox, Safari and Edge. For older browsers and platforms like Internet Explorer (down to version 9) and Android 4.x, [polyfills](http://polyfill.io), the application bundle needs to be transpiled (e.g. using [Babel](https://babeljs.io)) and bundled with polyfills for `fetch`, `requestAnimationFrame`, `Element.prototype.classList`, `URL` and `TextDecoder`.
The library is intended for use on both desktop/laptop and mobile devices, and supports pointer and touch interactions.

View File

@@ -11,6 +11,7 @@
"GyroNorm": false,
"jsPDF": false,
"jsts": false,
"JSZip": false,
"mapboxgl": false,
"saveAs": false,
"toastr": false,

4661
examples/data/kml/states.kml Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
#info {
width: 100%;
height: 24rem;
overflow: scroll;
display: flex;
align-items: baseline;
border: 1px solid black;
justify-content: flex-start;
}

View File

@@ -0,0 +1,22 @@
---
layout: example.html
title: Custom Drag-and-Drop (KMZ)
shortdesc: Example of using the drag-and-drop interaction with a custom format to handle KMZ files.
docs: >
Example of using the drag-and-drop interaction with a custom format to handle KMZ files.
In addition to the formats used in the [Drag-and-Drop](drag-and-drop.html) example a custom format (subclassing KML) is used to handle KMZ files.
KML and icons must be extracted from the KMZ array buffer synchronously.
<a href="https://stuk.github.io/jszip/documentation/upgrade_guide.html" target="_blank">JSZip 2.x</a> is used as it has better browser compatibility and is simpler to code than the more recent <a href="https://github.com/ericvergnaud/jszip#readme" target="_blank">JSZip-sync</a>.
There is no projection transform support, so this will only work with data in EPSG:4326 and EPSG:3857.
tags: "drag-and-drop, kml, kmz"
resources:
- https://cdnjs.cloudflare.com/ajax/libs/jszip/2.6.1/jszip.min.js
---
<div id="map" class="map"></div>
<br />
<div>
<a id="download" download></a>
<button id="download-kmz">Download sample</button>
</div>
<br />
<div id="info">&nbsp;</div>

View File

@@ -0,0 +1,155 @@
import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js';
import {
DragAndDrop,
defaults as defaultInteractions,
} from '../src/ol/interaction.js';
import {GPX, GeoJSON, IGC, KML, TopoJSON} from '../src/ol/format.js';
import {OSM, Vector as VectorSource} from '../src/ol/source.js';
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
// Create functions to extract KML and icons from KMZ array buffer,
// which must be done synchronously.
const zip = new JSZip();
function getKMLData(buffer) {
let kmlData;
zip.load(buffer);
const kmlFile = zip.file(/.kml$/i)[0];
if (kmlFile) {
kmlData = kmlFile.asText();
}
return kmlData;
}
function getKMLImage(href) {
let url = href;
let path = window.location.href;
path = path.slice(0, path.lastIndexOf('/') + 1);
if (href.indexOf(path) === 0) {
const regexp = new RegExp(href.replace(path, '') + '$', 'i');
const kmlFile = zip.file(regexp)[0];
if (kmlFile) {
url = URL.createObjectURL(new Blob([kmlFile.asArrayBuffer()]));
}
}
return url;
}
// Define a KMZ format class by subclassing ol/format/KML
class KMZ extends KML {
constructor(opt_options) {
const options = opt_options || {};
options.iconUrlFunction = getKMLImage;
super(options);
}
getType() {
return 'arraybuffer';
}
readFeature(source, options) {
const kmlData = getKMLData(source);
return super.readFeature(kmlData, options);
}
readFeatures(source, options) {
const kmlData = getKMLData(source);
return super.readFeatures(kmlData, options);
}
}
// Set up map with Drag and Drop interaction
const dragAndDropInteraction = new DragAndDrop({
formatConstructors: [KMZ, GPX, GeoJSON, IGC, KML, TopoJSON],
});
const map = new Map({
interactions: defaultInteractions().extend([dragAndDropInteraction]),
layers: [
new TileLayer({
source: new OSM(),
}),
],
target: 'map',
view: new View({
center: [0, 0],
zoom: 2,
}),
});
dragAndDropInteraction.on('addfeatures', function (event) {
const vectorSource = new VectorSource({
features: event.features,
});
map.addLayer(
new VectorLayer({
source: vectorSource,
})
);
map.getView().fit(vectorSource.getExtent());
});
const displayFeatureInfo = function (pixel) {
const features = [];
map.forEachFeatureAtPixel(pixel, function (feature) {
features.push(feature);
});
if (features.length > 0) {
const info = [];
let i, ii;
for (i = 0, ii = features.length; i < ii; ++i) {
const description =
features[i].get('description') ||
features[i].get('name') ||
features[i].get('_name') ||
features[i].get('layer');
if (description) {
info.push(description);
}
}
document.getElementById('info').innerHTML = info.join('<br/>') || '&nbsp';
} else {
document.getElementById('info').innerHTML = '&nbsp;';
}
};
map.on('pointermove', function (evt) {
if (evt.dragging) {
return;
}
const pixel = map.getEventPixel(evt.originalEvent);
displayFeatureInfo(pixel);
});
map.on('click', function (evt) {
displayFeatureInfo(evt.pixel);
});
// Sample data download
const link = document.getElementById('download');
function download(fullpath, filename) {
fetch(fullpath)
.then(function (response) {
return response.blob();
})
.then(function (blob) {
if (navigator.msSaveBlob) {
// link download attribuute does not work on MS browsers
navigator.msSaveBlob(blob, filename);
} else {
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
}
});
}
document.getElementById('download-kmz').addEventListener('click', function () {
download('data/kmz/iceland.kmz', 'iceland.kmz');
});

View File

@@ -0,0 +1,3 @@
.tileCoord input {
width: 60px;
}

View File

@@ -0,0 +1,23 @@
---
layout: example.html
title: Custom Drag-and-Drop (MVT preview)
shortdesc: Example of using the drag-and-drop interaction with a custom format to preview MVT tiles.
docs: >
Example of using the drag-and-drop interaction with a custom format to preview MVT tiles.
In addition to the formats used in the [Drag-and-Drop](drag-and-drop.html) example individual MVT tiles can be previewed.
There is no projection transform support, so this will only work with data in EPSG:4326 and EPSG:3857.
tags: "drag-and-drop, mvt"
---
<div id="map" class="map"></div>
<br />
<div class="tileCoord">
<a id="download" download></a>
<span>Tile coordinate&nbsp;&nbsp;</span>
<span>&nbsp;z: <input type="number" id="tileCoordZ" value="6" /></span>
<span>&nbsp;x: <input type="number" id="tileCoordX" value="30" /></span>
<span>&nbsp;y: <input type="number" id="tileCoordY" value="20" /></span>
<span>&nbsp;&nbsp;</span>
<button id="download-mvt">Download sample</button>
</div>
<br />
<div id="info">&nbsp;</div>

View File

@@ -0,0 +1,133 @@
import Feature from '../src/ol/Feature.js';
import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js';
import {
DragAndDrop,
defaults as defaultInteractions,
} from '../src/ol/interaction.js';
import {GPX, GeoJSON, IGC, KML, MVT, TopoJSON} from '../src/ol/format.js';
import {OSM, Vector as VectorSource} from '../src/ol/source.js';
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
import {createXYZ} from '../src/ol/tilegrid.js';
// Define a custom MVT format as ol/format/MVT requires an extent
const tileCoordZ = document.getElementById('tileCoordZ');
const tileCoordX = document.getElementById('tileCoordX');
const tileCoordY = document.getElementById('tileCoordY');
class customMVT extends MVT {
constructor() {
super({featureClass: Feature});
}
readFeatures(source, options) {
options.extent = createXYZ().getTileCoordExtent([
parseInt(tileCoordZ.value),
parseInt(tileCoordX.value),
parseInt(tileCoordY.value),
]);
return super.readFeatures(source, options);
}
}
// Set up map with Drag and Drop interaction
const dragAndDropInteraction = new DragAndDrop({
formatConstructors: [customMVT, GPX, GeoJSON, IGC, KML, TopoJSON],
});
const map = new Map({
interactions: defaultInteractions().extend([dragAndDropInteraction]),
layers: [
new TileLayer({
source: new OSM(),
}),
],
target: 'map',
view: new View({
center: [0, 0],
zoom: 2,
}),
});
dragAndDropInteraction.on('addfeatures', function (event) {
const vectorSource = new VectorSource({
features: event.features,
});
map.addLayer(
new VectorLayer({
source: vectorSource,
})
);
map.getView().fit(vectorSource.getExtent());
});
const displayFeatureInfo = function (pixel) {
const features = [];
map.forEachFeatureAtPixel(pixel, function (feature) {
features.push(feature);
});
if (features.length > 0) {
const info = [];
let i, ii;
for (i = 0, ii = features.length; i < ii; ++i) {
const description =
features[i].get('name') ||
features[i].get('_name') ||
features[i].get('layer');
if (description) {
info.push(description);
}
}
document.getElementById('info').innerHTML = info.join(', ') || '&nbsp';
} else {
document.getElementById('info').innerHTML = '&nbsp;';
}
};
map.on('pointermove', function (evt) {
if (evt.dragging) {
return;
}
const pixel = map.getEventPixel(evt.originalEvent);
displayFeatureInfo(pixel);
});
map.on('click', function (evt) {
displayFeatureInfo(evt.pixel);
});
// Sample data download
const link = document.getElementById('download');
function download(fullpath, filename) {
fetch(fullpath)
.then(function (response) {
return response.blob();
})
.then(function (blob) {
if (navigator.msSaveBlob) {
// link download attribuute does not work on MS browsers
navigator.msSaveBlob(blob, filename);
} else {
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
}
});
}
document.getElementById('download-mvt').addEventListener('click', function () {
const fullpath =
'https://basemaps.arcgis.com/v1/arcgis/rest/services/World_Basemap/VectorTileServer/tile/' +
tileCoordZ.value +
'/' +
tileCoordY.value +
'/' +
tileCoordX.value +
'.pbf';
const filename =
tileCoordZ.value + '-' + tileCoordX.value + '-' + tileCoordY.value + '.mvt';
download(fullpath, filename);
});

View File

@@ -10,4 +10,20 @@ cloak:
value: Get your own API key at https://www.maptiler.com/cloud/
---
<div id="map" class="map"></div>
<div><input type="checkbox" id="extractstyles" checked /> Extract styles from KML</div>
<br />
<div>
<a id="download" download></a>
Download samples:&nbsp;&nbsp;
<button id="download-gpx">GPX</button>
&nbsp;
<button id="download-geojson">GeoJSON</button>
&nbsp;
<button id="download-igc">IGC</button>
&nbsp;
<button id="download-kml">KML</button>
&nbsp;
<button id="download-topojson">TopoJSON</button>
</div>
<br />
<div id="info">&nbsp;</div>

View File

@@ -1,24 +1,16 @@
import DragAndDrop from '../src/ol/interaction/DragAndDrop.js';
import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js';
import {
DragAndDrop,
defaults as defaultInteractions,
} from '../src/ol/interaction.js';
import {GPX, GeoJSON, IGC, KML, TopoJSON} from '../src/ol/format.js';
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
import {Vector as VectorSource, XYZ} from '../src/ol/source.js';
const dragAndDropInteraction = new DragAndDrop({
formatConstructors: [GPX, GeoJSON, IGC, KML, TopoJSON],
});
const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
const attributions =
'<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
'<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';
const map = new Map({
interactions: defaultInteractions().extend([dragAndDropInteraction]),
layers: [
new TileLayer({
source: new XYZ({
@@ -36,17 +28,39 @@ const map = new Map({
}),
});
dragAndDropInteraction.on('addfeatures', function (event) {
const vectorSource = new VectorSource({
features: event.features,
const extractStyles = document.getElementById('extractstyles');
let dragAndDropInteraction;
function setInteraction() {
if (dragAndDropInteraction) {
map.removeInteraction(dragAndDropInteraction);
}
dragAndDropInteraction = new DragAndDrop({
formatConstructors: [
GPX,
GeoJSON,
IGC,
// use constructed format to set options
new KML({extractStyles: extractStyles.checked}),
TopoJSON,
],
});
map.addLayer(
new VectorLayer({
source: vectorSource,
})
);
map.getView().fit(vectorSource.getExtent());
});
dragAndDropInteraction.on('addfeatures', function (event) {
const vectorSource = new VectorSource({
features: event.features,
});
map.addLayer(
new VectorLayer({
source: vectorSource,
})
);
map.getView().fit(vectorSource.getExtent());
});
map.addInteraction(dragAndDropInteraction);
}
setInteraction();
extractStyles.addEventListener('change', setInteraction);
const displayFeatureInfo = function (pixel) {
const features = [];
@@ -76,3 +90,48 @@ map.on('pointermove', function (evt) {
map.on('click', function (evt) {
displayFeatureInfo(evt.pixel);
});
// Sample data downloads
const link = document.getElementById('download');
function download(fullpath, filename) {
fetch(fullpath)
.then(function (response) {
return response.blob();
})
.then(function (blob) {
if (navigator.msSaveBlob) {
// link download attribuute does not work on MS browsers
navigator.msSaveBlob(blob, filename);
} else {
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
}
});
}
document.getElementById('download-gpx').addEventListener('click', function () {
download('data/gpx/fells_loop.gpx', 'fells_loop.gpx');
});
document
.getElementById('download-geojson')
.addEventListener('click', function () {
download('data/geojson/roads-seoul.geojson', 'roads-seoul.geojson');
});
document.getElementById('download-igc').addEventListener('click', function () {
download('data/igc/Ulrich-Prinz.igc', 'Ulrich-Prinz.igc');
});
document.getElementById('download-kml').addEventListener('click', function () {
download('data/kml/states.kml', 'states.kml');
});
document
.getElementById('download-topojson')
.addEventListener('click', function () {
download('data/topojson/fr-departments.json', 'fr-departments.json');
});

View File

@@ -9,7 +9,7 @@
function fetchResource(resource) {
return new Promise(function (resolve, reject) {
const isImage = /\.(png|jpe?g|gif|tiff|svg)$/.test(resource);
const isImage = /\.(png|jpe?g|gif|tiff|svg|kmz)$/.test(resource);
if (isImage) {
resolve ({
isBinary: true,

View File

@@ -68,7 +68,7 @@
<link rel="stylesheet" href="./css/ol.css" type="text/css">
<link rel="stylesheet" href="./resources/layout.css" type="text/css">
<script src="https://unpkg.com/elm-pep"></script>
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch,requestAnimationFrame,Element.prototype.classList,URL"></script>
<script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=fetch,requestAnimationFrame,Element.prototype.classList,URL,TextDecoder"></script>
{{{ extraHead.local }}}
{{{ css.tag }}}
<title>{{ title }}</title>
@@ -194,7 +194,7 @@
&lt;!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer --&gt;
&lt;script src="https://unpkg.com/elm-pep"&gt;&lt;/script&gt;{{#if extraHead.remote}}
&lt;!-- The line below is only needed for old environments like Internet Explorer and Android 4.x --&gt;
&lt;script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch,requestAnimationFrame,Element.prototype.classList,URL"&gt;&lt;/script&gt;
&lt;script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=fetch,requestAnimationFrame,Element.prototype.classList,URL,TextDecoder"&gt;&lt;/script&gt;
{{ indent extraHead.remote spaces=4 }}{{/if}}
&lt;style&gt;
.map {

View File

@@ -5,6 +5,7 @@
import Event from '../events/Event.js';
import EventType from '../events/EventType.js';
import FormatType from '../format/FormatType.js';
import Interaction from './Interaction.js';
import {TRUE} from '../functions.js';
import {get as getProjection} from '../proj.js';
@@ -12,7 +13,8 @@ import {listen, unlistenByKey} from '../events.js';
/**
* @typedef {Object} Options
* @property {Array<typeof import("../format/Feature.js").default>} [formatConstructors] Format constructors.
* @property {Array<typeof import("../format/Feature.js").default|import("../format/Feature.js").default>} [formatConstructors] Format constructors
* (and/or formats pre-constructed with options).
* @property {import("../source/Vector.js").default} [source] Optional vector source where features will be added. If a source is provided
* all existing features will be removed and new features will be added when
* they are dropped on the target. If you want to add features to a vector
@@ -75,6 +77,11 @@ export class DragAndDropEvent extends Event {
/**
* @classdesc
* Handles input of vector data by drag and drop.
*
* Note that the DragAndDrop interaction uses the TextDecoder() constructor if the supplied
* combinnation of formats read both text string and ArrayBuffer sources. Older browsers such
* as IE which do not support this will need a TextDecoder polyfill to be loaded before use.
*
* @api
*
* @fires DragAndDropEvent
@@ -92,11 +99,27 @@ class DragAndDrop extends Interaction {
/**
* @private
* @type {Array<typeof import("../format/Feature.js").default>}
* @type {boolean}
*/
this.formatConstructors_ = options.formatConstructors
this.readAsBuffer_ = false;
/**
* @private
* @type {Array<import("../format/Feature.js").default>}
*/
this.formats_ = [];
const formatConstructors = options.formatConstructors
? options.formatConstructors
: [];
for (let i = 0, ii = formatConstructors.length; i < ii; ++i) {
let format = formatConstructors[i];
if (typeof format === 'function') {
format = new format();
}
this.formats_.push(format);
this.readAsBuffer_ =
this.readAsBuffer_ || format.getType() === FormatType.ARRAY_BUFFER;
}
/**
* @private
@@ -139,10 +162,18 @@ class DragAndDrop extends Interaction {
projection = view.getProjection();
}
const formatConstructors = this.formatConstructors_;
for (let i = 0, ii = formatConstructors.length; i < ii; ++i) {
const format = new formatConstructors[i]();
const features = this.tryReadFeatures_(format, result, {
let text;
const formats = this.formats_;
for (let i = 0, ii = formats.length; i < ii; ++i) {
const format = formats[i];
let input = result;
if (this.readAsBuffer_ && format.getType() !== FormatType.ARRAY_BUFFER) {
if (text === undefined) {
text = new TextDecoder().decode(result);
}
input = text;
}
const features = this.tryReadFeatures_(format, input, {
featureProjection: projection,
});
if (features && features.length > 0) {
@@ -249,7 +280,11 @@ class DragAndDrop extends Interaction {
EventType.LOAD,
this.handleResult_.bind(this, file)
);
reader.readAsText(file);
if (this.readAsBuffer_) {
reader.readAsArrayBuffer(file);
} else {
reader.readAsText(file);
}
}
}

View File

@@ -2,6 +2,7 @@ import DragAndDrop from '../../../../src/ol/interaction/DragAndDrop.js';
import Event from '../../../../src/ol/events/Event.js';
import EventTarget from '../../../../src/ol/events/Target.js';
import GeoJSON from '../../../../src/ol/format/GeoJSON.js';
import MVT from '../../../../src/ol/format/MVT.js';
import VectorSource from '../../../../src/ol/source/Vector.js';
import View from '../../../../src/ol/View.js';
@@ -29,8 +30,8 @@ where('FileReader').describe('ol.interaction.DragAndDrop', function () {
expect(interaction).to.be.an(DragAndDrop);
});
it('sets formatConstructors on the instance', function () {
expect(interaction.formatConstructors_).to.have.length(1);
it('sets formats on the instance', function () {
expect(interaction.formats_).to.have.length(1);
});
it('accepts a source option', function () {
@@ -88,18 +89,28 @@ where('FileReader').describe('ol.interaction.DragAndDrop', function () {
describe('#handleDrop_', function () {
let OrigFileReader;
let mockReadAsText;
let mockReadAsArrayBuffer;
beforeEach(function () {
OrigFileReader = FileReader;
mockReadAsText = false;
mockReadAsArrayBuffer = false;
class MockFileReader extends EventTarget {
constructor() {
super(...arguments);
}
readAsText(file) {
mockReadAsText = true;
this.result = file;
this.dispatchEvent('load');
}
readAsArrayBuffer(file) {
mockReadAsArrayBuffer = true;
this.result = new TextEncoder().encode(file).buffer;
this.dispatchEvent('load');
}
}
FileReader = MockFileReader;
});
@@ -108,9 +119,11 @@ where('FileReader').describe('ol.interaction.DragAndDrop', function () {
FileReader = OrigFileReader;
});
it('reads dropped files', function (done) {
it('reads dropped files as text', function (done) {
interaction.on('addfeatures', function (evt) {
expect(evt.features.length).to.be(1);
expect(mockReadAsText).to.be(true);
expect(mockReadAsArrayBuffer).to.be(false);
done();
});
interaction.setMap(map);
@@ -135,6 +148,121 @@ where('FileReader').describe('ol.interaction.DragAndDrop', function () {
expect(event.propagationStopped).to.be(true);
});
it('reads dropped files as arraybuffer', function (done) {
const drop = new DragAndDrop({
formatConstructors: [GeoJSON, MVT],
});
drop.setMap(map);
drop.on('addfeatures', function (evt) {
expect(evt.features.length).to.be(1);
expect(mockReadAsText).to.be(false);
expect(mockReadAsArrayBuffer).to.be(true);
done();
});
const event = new Event();
event.dataTransfer = {};
event.type = 'dragenter';
viewport.dispatchEvent(event);
event.type = 'dragover';
viewport.dispatchEvent(event);
event.type = 'drop';
event.dataTransfer.files = {
length: 1,
item: function () {
return JSON.stringify({
type: 'FeatureCollection',
features: [{type: 'Feature', id: '1'}],
});
},
};
viewport.dispatchEvent(event);
expect(event.dataTransfer.dropEffect).to.be('copy');
expect(event.propagationStopped).to.be(true);
});
it('reads using constructed formats', function (done) {
const drop = new DragAndDrop({
formatConstructors: [new GeoJSON()],
});
drop.setMap(map);
drop.on('addfeatures', function (evt) {
expect(evt.features.length).to.be(1);
expect(mockReadAsText).to.be(true);
expect(mockReadAsArrayBuffer).to.be(false);
done();
});
const event = new Event();
event.dataTransfer = {};
event.type = 'dragenter';
viewport.dispatchEvent(event);
event.type = 'dragover';
viewport.dispatchEvent(event);
event.type = 'drop';
event.dataTransfer.files = {
length: 1,
item: function () {
return JSON.stringify({
type: 'FeatureCollection',
features: [{type: 'Feature', id: '1'}],
});
},
};
viewport.dispatchEvent(event);
expect(event.dataTransfer.dropEffect).to.be('copy');
expect(event.propagationStopped).to.be(true);
});
it('reads using arraybuffer formats', function (done) {
class binaryGeoJSON extends GeoJSON {
constructor(options) {
super(options);
}
getType() {
return 'arraybuffer';
}
readFeatures(source, options) {
const data = new TextDecoder().decode(source);
return super.readFeatures(data, options);
}
}
const drop = new DragAndDrop({
formatConstructors: [binaryGeoJSON],
});
drop.setMap(map);
drop.on('addfeatures', function (evt) {
expect(evt.features.length).to.be(1);
expect(mockReadAsText).to.be(false);
expect(mockReadAsArrayBuffer).to.be(true);
done();
});
const event = new Event();
event.dataTransfer = {};
event.type = 'dragenter';
viewport.dispatchEvent(event);
event.type = 'dragover';
viewport.dispatchEvent(event);
event.type = 'drop';
event.dataTransfer.files = {
length: 1,
item: function () {
return JSON.stringify({
type: 'FeatureCollection',
features: [{type: 'Feature', id: '1'}],
});
},
};
viewport.dispatchEvent(event);
expect(event.dataTransfer.dropEffect).to.be('copy');
expect(event.propagationStopped).to.be(true);
});
it('adds dropped features to a source', function (done) {
const source = new VectorSource();
const drop = new DragAndDrop({