Skip to content

Commit 96e338f

Browse files
authored
add GeoJSON.fetch for big geojson files (#2204)
* add GeoJSON.fetch for big geojson files * fix lint * fix code cover test
1 parent de53d7b commit 96e338f

File tree

1 file changed

+150
-1
lines changed

1 file changed

+150
-1
lines changed

src/geometry/GeoJSON.js

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import {
44
parseJSON,
55
isArrayHasData,
66
pushIn,
7-
isNumber
7+
isNumber,
8+
extend,
9+
getAbsoluteURL,
10+
GUID
811
} from '../core/util';
912
import Marker from './Marker';
1013
import LineString from './LineString';
@@ -17,6 +20,8 @@ import Geometry from './Geometry';
1720
import { GEOJSON_TYPES } from '../core/Constants';
1821
import PromisePolyfill from './../core/Promise';
1922
import { runTaskAsync } from '../core/MicroTask';
23+
import Actor from '../core/worker/Actor';
24+
import { registerWorkerAdapter } from '../core/worker/Worker';
2025

2126
const types = {
2227
'Marker': Marker,
@@ -27,6 +32,106 @@ const types = {
2732
'MultiPolygon': MultiPolygon
2833
};
2934

35+
const WORKER_KEY = 'geojson-fetch-worker-page-async';
36+
const WORKER_CODE = `
37+
function (exports) {
38+
const resultMap = {};
39+
40+
function handleResult(msg, postResponse) {
41+
const data = msg.data || {};
42+
const { taskId } = data;
43+
const features = resultMap[taskId];
44+
if (!features) {
45+
postResponse('not find geojson dataset the taskId:' + taskId);
46+
return;
47+
}
48+
if (features.length === 0) {
49+
delete resultMap[taskId];
50+
postResponse(null, []);
51+
return;
52+
}
53+
const pageSize = data.pageSize || 2000;
54+
const pageFeatures = features.slice(0, pageSize);
55+
resultMap[taskId] = features.slice(pageSize, Infinity);
56+
postResponse(null, pageFeatures);
57+
}
58+
//worker init
59+
exports.initialize = function () {
60+
// console.log("geojson fetch init");
61+
};
62+
//recive message
63+
exports.onmessage = function (msg, postResponse) {
64+
const { taskId, type, url } = msg.data || {};
65+
if (!taskId) {
66+
postResponse('not find task id for get geojson dataset,taskId=' + taskId);
67+
return;
68+
}
69+
if (type === 'fetchdata') {
70+
if (!url) {
71+
postResponse('url is null,url=' + url);
72+
return;
73+
}
74+
fetch(url).then(res => res.json()).then(geojson => {
75+
let features;
76+
if (Array.isArray(geojson)) {
77+
features = geojson;
78+
} else if (geojson.features) {
79+
features = geojson.features;
80+
} else {
81+
features = [geojson];
82+
}
83+
resultMap[taskId] = features;
84+
handleResult(msg, postResponse);
85+
}).catch(errror => {
86+
postResponse(errror.message);
87+
});
88+
} else if (type === 'pagefeatures') {
89+
handleResult(msg, postResponse);
90+
} else {
91+
postResponse('not support task type:' + type);
92+
}
93+
};
94+
}`;
95+
96+
class GeoJSONFetchActor extends Actor {
97+
98+
constructor() {
99+
super(WORKER_KEY);
100+
}
101+
102+
_sendMsg(options, featuresList, cb) {
103+
this.send(options, [], (error, data) => {
104+
if (error) {
105+
cb(error);
106+
} else {
107+
this._pageFeatures(options, data, featuresList, cb);
108+
}
109+
}, options.workerId);
110+
}
111+
112+
_fetchGeoJSON(url, options, featuresList = [], cb) {
113+
const opts = extend({}, options);
114+
opts.type = 'fetchdata';
115+
opts.url = url;
116+
this._sendMsg(opts, featuresList, cb);
117+
}
118+
119+
_pageFeatures(options, features, featuresList, cb) {
120+
featuresList.push(features);
121+
if (features.length === 0) {
122+
cb(null, featuresList);
123+
return;
124+
}
125+
const opts = extend({}, options);
126+
opts.type = 'pagefeatures';
127+
this._sendMsg(opts, featuresList, cb);
128+
}
129+
}
130+
131+
registerWorkerAdapter(WORKER_KEY, function () { return WORKER_CODE; });
132+
133+
let fetchActor;
134+
30135
/**
31136
* GeoJSON utilities
32137
* @class
@@ -255,6 +360,50 @@ const GeoJSON = {
255360
}
256361
return false;
257362

363+
},
364+
/**
365+
* Requesting a large volume geojson file.Solve the problem of main thread blocking
366+
* @param {String} url - GeoJSON file path
367+
* @param {Number} [countPerTime=2000] - Number of graphics converted per time
368+
* @return {Promise}
369+
* @example
370+
* GeoJSON.fetch('https://abc.com/file.geojson',2000).then(geojson=>{
371+
* console.log(geojson);
372+
* })
373+
* */
374+
fetch(url, countPerTime = 2000) {
375+
return new PromisePolyfill((resolve, reject) => {
376+
if (!url || !isString(url)) {
377+
reject('url is error,It should be string');
378+
return;
379+
}
380+
const options = extend({ pageSize: 2000 }, { pageSize: countPerTime });
381+
url = getAbsoluteURL(url);
382+
if (!fetchActor) {
383+
fetchActor = new GeoJSONFetchActor();
384+
}
385+
const workerCount = fetchActor.workers.length;
386+
let workerId = Math.floor(Math.random() * workerCount);
387+
workerId = Math.min(workerCount - 1, workerId);
388+
options.workerId = workerId;
389+
options.taskId = GUID();
390+
fetchActor._fetchGeoJSON(url, options, [], (error, featuresList) => {
391+
if (error) {
392+
reject(error);
393+
return;
394+
}
395+
const result = [];
396+
featuresList.forEach(features => {
397+
for (let i = 0, len = features.length; i < len; i++) {
398+
result.push(features[i]);
399+
}
400+
});
401+
resolve({
402+
type: 'FeatureCollection',
403+
features: result
404+
});
405+
});
406+
});
258407
}
259408
};
260409

0 commit comments

Comments
 (0)