isSea

https://github.com/simonepri/is-sea/tree/master
https://github.com/simonepri/is-sea
https://github.com/simonepri/geo-maps/blob/master/info/earth-seas.md

async function isSea(lat, lng) {
if (landLookup === null) {
// 把json文件轉(zhuǎn)為json格式加載進來
const map = await fetchMapAsync();
landLookup = new GeoJsonLookup(map);
}
return landLookup.hasContainers({type: 'Point', coordinates: [lng, lat]});
}

class GeoJsonGeometriesLookup {
/**

  • Create an instance of the GeoJSON lookup class.
  • @public
  • @param {Object} geoJson The GeoJSON for which create the lookup data.
  • @param {Object} [options] Optional options.
  • @param {boolean} options.ignorePoints If true the extractor will ignore
  • geometries of type Point.
  • @param {boolean} options.ignoreLines If true the extractor will ignore
  • geometries of type LineString.
  • @param {boolean} options.ignorePolygon If true the extractor will ignore
  • geometries of type Polygon.
    */
    constructor(geoJson, options) {
    options = typeof options === 'object' ? options : {};
    // 構(gòu)建成員變量:點农猬,線赡艰,面集合,只有面List有數(shù)據(jù)
    const extracted = new GeoJsonGeometries(geoJson, options);
this.D = new Array(3);
this.D[0] = {list: extracted.points.features, bboxs: null, lookup: null};
this.D[1] = {list: extracted.lines.features, bboxs: null, lookup: null};
// list就是_loadPolygon()方法push的集合數(shù)據(jù)
this.D[2] = {list: extracted.polygons.features, bboxs: null, lookup: null};

for (let d = 0; d < 3; d++) {
    // dim就是D集合中的對象:list,bboxs,lookup
  const dim = this.D[d];
  // 循環(huán)到d=2
  if (dim.list.length > 0) {
    dim.bboxs = new Array(dim.list.length);
    // 構(gòu)造一個對象:
    //{
    //  data: {children: Array(0), height: 1, leaf: true, maxX: -Infinity,maxY: -Infinity,minX: Infinity,minY: Infinity}
    //  _maxEntries: 9
    //  _minEntries: 4
    //}
    dim.lookup = rbush();

    // 數(shù)組集合:{type: FEATURE, geometry: {type: POLYGON, coordinates}, properties}
    const geoms = dim.list;
    // 21個zise的空數(shù)組
    const bboxs = dim.bboxs;
    // 上面構(gòu)建的對象
    const lookup = dim.lookup;
    
    // 把數(shù)組的每個二維元素集合的點的邊界框放置到bboxs[]
    for (let i = 0, len = geoms.length; i < len; i++) {
      const bbox = tbbox(geoms[i]);
      bboxs[i] = {minX: bbox[0], minY: bbox[1], maxX: bbox[2], maxY: bbox[3], id: i};
    }
    // 把這些元素組成框斤葱,構(gòu)成r-tree結(jié)構(gòu)的樹慷垮,到時候查詢就會快很多
    lookup.load(bboxs);
  }
}

}

//接受一組要素揖闸,計算所有輸入要素的bbox,然后返回一個邊界框
function bbox(geojson) {
// 新建一個數(shù)組
var BBox = [Infinity, Infinity, -Infinity, -Infinity];
meta.coordEach(geojson, function (coord) {
//callback回調(diào)函數(shù)料身,取里面二維數(shù)組的每一點來與Bbox比較汤纸。BBox最后取到二維數(shù)組的minX,minY,maxX,maxY
// 取小
if (BBox[0] > coord[0]) BBox[0] = coord[0];
if (BBox[1] > coord[1]) BBox[1] = coord[1];
// 取大
if (BBox[2] < coord[0]) BBox[2] = coord[0];
if (BBox[3] < coord[1]) BBox[3] = coord[1];
});
return BBox;
}

}

function rbush(maxEntries, format) {
if (!(this instanceof rbush)) return new rbush(maxEntries, format);

// max entries in a node is 9 by default; min node fill is 40% for best performance
this._maxEntries = Math.max(4, maxEntries || 9); // 結(jié)果是9
this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));  // 結(jié)果是4

if (format) {
    this._initFormat(format);
}

this.clear();

}

load: function (data) {
if (!(data && data.length)) return this;

    if (data.length < this._minEntries) {
        for (var i = 0, len = data.length; i < len; i++) {
            this.insert(data[i]);
        }
        return this;
    }

    // recursively build the tree with the given data from stratch using OMT algorithm
    // omt算法:r-tree
    var node = this._build(data.slice(), 0, data.length - 1, 0);

    if (!this.data.children.length) {
        // save as is if tree is empty
        this.data = node;

    } else if (this.data.height === node.height) {
        // split root if trees have the same height
        this._splitRoot(this.data, node);

    } else {
        if (this.data.height < node.height) {
            // swap trees if inserted one is bigger
            var tmpNode = this.data;
            this.data = node;
            node = tmpNode;
        }

        // insert the small tree into the large tree at appropriate level
        this._insert(node, this.data.height - node.height - 1, true);
    }

    return this;
},

function multiSelect(arr, left, right, n, compare) {
var stack = [left, right],
mid;

while (stack.length) {
    right = stack.pop();
    left = stack.pop();

    if (right - left <= n) continue;

    mid = left + Math.ceil((right - left) / n / 2) * n;
    quickselect(arr, mid, left, right, compare);

    stack.push(left, mid, mid, right);
}

}

_build: function (items, left, right, height) {

    var N = right - left + 1,
        M = this._maxEntries,
        node;

    if (N <= M) {
        // reached leaf level; return leaf
        node = createNode(items.slice(left, right + 1));
        calcBBox(node, this.toBBox);
        return node;
    }

    if (!height) {
        // target height of the bulk-loaded tree
        height = Math.ceil(Math.log(N) / Math.log(M));

        // target number of root entries to maximize storage utilization
        M = Math.ceil(N / Math.pow(M, height - 1));
    }

    node = createNode([]);
    node.leaf = false;
    node.height = height;

    // split the items into M mostly square tiles

    var N2 = Math.ceil(N / M),
        N1 = N2 * Math.ceil(Math.sqrt(M)),
        i, j, right2, right3;

    multiSelect(items, left, right, N1, this.compareMinX);

    for (i = left; i <= right; i += N1) {

        right2 = Math.min(i + N1 - 1, right);

        multiSelect(items, i, right2, N2, this.compareMinY);

        for (j = i; j <= right2; j += N2) {

            right3 = Math.min(j + N2 - 1, right2);

            // pack each entry recursively
            node.children.push(this._build(items, j, right3, height - 1));
        }
    }

    calcBBox(node, this.toBBox);

    return node;
},

clear: function () {
this.data = createNode([]);
return this;
},

function createNode(children) {
return {
children: children,
height: 1,
leaf: true,
minX: Infinity,
minY: Infinity,
maxX: -Infinity,
maxY: -Infinity
};
}

class GeoJsonGeometries {
/**

  • Create an instance of the geometries extractor.
  • @public
  • @param {Object} geoJson The GeoJSON from which extract the geometries.
  • @param {Object} [options] Optional options.
  • @param {boolean} options.ignorePoints If true the extractor will ignore
  • geometries of type Point.
  • @param {boolean} options.ignoreLines If true the extractor will ignore
  • geometries of type LineString.
  • @param {boolean} options.ignorePolygon If true the extractor will ignore
  • geometries of type Polygon.
    */
    constructor(geoJson, options) {
    options = typeof options === 'object' ? options : {};
    // 構(gòu)建點愉棱,線算墨,面集合 這里是3個空數(shù)組
    this.pointsList = options.ignorePoints === true ? undefined : [];
    this.linesList = options.ignoreLines === true ? undefined : [];
    this.polygonsList = options.ignorePolygons === true ? undefined : [];
    // 遞歸的構(gòu)建不同類型的多邊形集合爹谭,每個集合都是一個對象说贝。polygonsList這個數(shù)組被賦值滿
    this._loadGeneric(geoJson);
    }

_loadGeneric(geoJson, properties) {
if (this.pointsList !== undefined) {
switch (geoJson.type) {
case POINT: {
return this._loadPoint(geoJson.coordinates, properties);
}
case MULTI_POINT: {
return geoJson.coordinates.forEach(coordinates => this._loadPoint(coordinates, properties));
}
default: break;
}
}
if (this.linesList !== undefined) {
switch (geoJson.type) {
case LINE_STRING: {
return this._loadLine(geoJson.coordinates, properties);
}
case MULTI_LINE_STRING: {
return geoJson.coordinates.forEach(coordinates => this._loadLine(coordinates, properties));
}
default: break;
}
}
if (this.polygonsList !== undefined) {
switch (geoJson.type) {
case POLYGON: {
return this._loadPolygon(geoJson.coordinates, properties);
}
// 第二次調(diào)用命中這個case,21個元素乡恕,第一個元素有1008個运杭,每個元素構(gòu)建為對象,加載數(shù)據(jù)到多邊形List
case MULTI_POLYGON: {
return geoJson.coordinates.forEach(coordinates => this._loadPolygon(coordinates, properties));
}
default: break;
}
}
switch (geoJson.type) {
case FEATURE: {
return this._loadGeneric(geoJson.geometry, geoJson.properties);
}
case FEATURE_COLLECTION: {
return geoJson.features.forEach(feature => this._loadGeneric(feature.geometry, feature.properties));
}
// 第一次調(diào)用命中這個case,這個數(shù)組就一個元素
case GEOMETRY_COLLECTION: {
// 遞歸調(diào)用這個方法,
return geoJson.geometries.forEach(geometry => this._loadGeneric(geometry, properties));
}
default: break;
}
}

// 加載數(shù)據(jù)到多邊形List
_loadPolygon(coordinates, properties) {
// 構(gòu)建面集合中的對象
this.polygonsList.push({type: FEATURE, geometry: {type: POLYGON, coordinates}, properties});
}

hasContainers(geometry, options) {
options = typeof options === 'object' ? options : {};

options.limit = 1;
return this.forEachCotainer(geometry, options) === 1;

}

forEachCotainer(geometry, options, func) {
options = typeof options === 'object' ? options : {};
func = typeof func === 'function' ? func : () => {};

let count = 0;
const size = getGeometryDimension(geometry);
const ignores = [options.ignorePoints, options.ignoreLines, options.ignorePolygons];

for (let d = size; d < 3; d++) {
  if (ignores[d] === true) {
    continue;
  }

  const dim = this.D[d];
  if (dim.lookup === null) {
    continue;
  }
    // 構(gòu)建一個矩形血筑,現(xiàn)在輸入的點梆砸。所以矩形的最大和最小的xy值是一樣的
  const bbox = tbbox(geometry);
  // 查找這個點是否在r-tree中,返回r-tree中最終命中的葉子節(jié)點元素
  const bboxs = dim.lookup.search({minX: bbox[0], minY: bbox[1], maxX: bbox[2], maxY: bbox[3]});

  for (let i = 0, len = bboxs.length; i < len; i++) {
    const geom = dim.list[bboxs[i].id];
    // geom是否包含geometry,執(zhí)行的是下面的booleanContains()方法
    if (!tcontains(geom, geometry)) {
      continue;
    }
    func(geom, count);
    count++;
    if (options.limit > 0 && options.limit === count) {
      return count;
    }
  }
}
return count;

}

search: function (bbox) {

    var node = this.data,
        result = [],
        toBBox = this.toBBox;
    // 輸入的矩形與r-tree是否相交
    if (!intersects(bbox, node)) return result;
    // 相交往下走
    var nodesToSearch = [],
        i, len, child, childBBox;

    while (node) {
        for (i = 0, len = node.children.length; i < len; i++) {

            child = node.children[i];
            // 如果是葉子節(jié)點,那么把葉子節(jié)點中的元素集合轉(zhuǎn)化為矩形
            // 如果是非葉子節(jié)點,則吧子節(jié)點當做childBBox
            childBBox = node.leaf ? toBBox(child) : child;
            // 判斷子節(jié)點和輸入矩形是否相交
            if (intersects(bbox, childBBox)) {
                // 根節(jié)點是葉子節(jié)點杨耙,則把當前子節(jié)點放入結(jié)果中
                if (node.leaf) result.push(child);
                // 輸入的矩形是否包含子節(jié)點矩形區(qū)域
                else if (contains(bbox, childBBox)) this._all(child, result);
                
                else nodesToSearch.push(child);
            }
        }
        node = nodesToSearch.pop();
    }

    return result;
},
// 兩個矩形是否相交
function intersects(a, b) {
return b.minX <= a.maxX &&
       b.minY <= a.maxY &&
       b.maxX >= a.minX &&
       b.maxY >= a.minY;
}
// a是否包含b
function contains(a, b) {
return a.minX <= b.minX &&
       a.minY <= b.minY &&
       b.maxX <= a.maxX &&
       b.maxY <= a.maxY;
}

function getGeometryDimension(geometry) {
switch (geometry.type) {
case POINT: return 0;
case LINE_STRING: return 1;
case POLYGON: return 2;
default: throw new TypeError('Unsupported GeoJSON type. Use one of: Point, LineString, Polygon');
}

function booleanContains(feature1, feature2) {
var type1 = invariant.getType(feature1);
var type2 = invariant.getType(feature2);
var geom1 = invariant.getGeom(feature1);
var geom2 = invariant.getGeom(feature2);
var coords1 = invariant.getCoords(feature1);
var coords2 = invariant.getCoords(feature2);

switch (type1) {
case 'Point':
    switch (type2) {
    case 'Point':
        return compareCoords(coords1, coords2);
    default:
        throw new Error('feature2 ' + type2 + ' geometry not supported');
    }
case 'MultiPoint':
    switch (type2) {
    case 'Point':
        return isPointInMultiPoint(geom1, geom2);
    case 'MultiPoint':
        return isMultiPointInMultiPoint(geom1, geom2);
    default:
        throw new Error('feature2 ' + type2 + ' geometry not supported');
    }
case 'LineString':
    switch (type2) {
    case 'Point':
        return isPointOnLine(geom2, geom1, {ignoreEndVertices: true});
    case 'LineString':
        return isLineOnLine(geom1, geom2);
    case 'MultiPoint':
        return isMultiPointOnLine(geom1, geom2);
    default:
        throw new Error('feature2 ' + type2 + ' geometry not supported');
    }
case 'Polygon':
    switch (type2) {
    case 'Point':
        // 點是不是在矩形里面
        return booleanPointInPolygon(geom2, geom1, {ignoreBoundary: true});
    case 'LineString':
        return isLineInPoly(geom1, geom2);
    case 'Polygon':
        return isPolyInPoly(geom1, geom2);
    case 'MultiPoint':
        return isMultiPointInPoly(geom1, geom2);
    default:
        throw new Error('feature2 ' + type2 + ' geometry not supported');
    }
default:
    throw new Error('feature1 ' + type1 + ' geometry not supported');
}

}

function booleanPointInPolygon(point, polygon, options) {
// Optional parameters
options = options || {};
if (typeof options !== 'object') throw new Error('options is invalid');
var ignoreBoundary = options.ignoreBoundary;

// validation
if (!point) throw new Error('point is required');
if (!polygon) throw new Error('polygon is required');

var pt = invariant.getCoord(point);
var polys = invariant.getCoords(polygon);
var type = (polygon.geometry) ? polygon.geometry.type : polygon.type;
var bbox = polygon.bbox;

// Quick elimination if point is not inside bbox
if (bbox && inBBox(pt, bbox) === false) return false;

// normalize to multipolygon
if (type === 'Polygon') polys = [polys];

for (var i = 0, insidePoly = false; i < polys.length && !insidePoly; i++) {
    // check if it is in the outer ring first
    if (inRing(pt, polys[i][0], ignoreBoundary)) {
        var inHole = false;
        var k = 1;
        // check for the point in any of the holes
        while (k < polys[i].length && !inHole) {
            if (inRing(pt, polys[i][k], !ignoreBoundary)) {
                inHole = true;
            }
            k++;
        }
        if (!inHole) insidePoly = true;
    }
}
return insidePoly;

}

function inRing(pt, ring, ignoreBoundary) {
var isInside = false;
if (ring[0][0] === ring[ring.length - 1][0] && ring[0][1] === ring[ring.length - 1][1]) ring = ring.slice(0, ring.length - 1);

for (var i = 0, j = ring.length - 1; i < ring.length; j = i++) {
    var xi = ring[i][0], yi = ring[i][1];
    var xj = ring[j][0], yj = ring[j][1];
    var onBoundary = (pt[1] * (xi - xj) + yi * (xj - pt[0]) + yj * (pt[0] - xi) === 0) &&
        ((xi - pt[0]) * (xj - pt[0]) <= 0) && ((yi - pt[1]) * (yj - pt[1]) <= 0);
    if (onBoundary) return !ignoreBoundary;
    var intersect = ((yi > pt[1]) !== (yj > pt[1])) &&
    (pt[0] < (xj - xi) * (pt[1] - yi) / (yj - yi) + xi);
    if (intersect) isInside = !isInside;
}
return isInside;

}

}

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蜡励,一起剝皮案震驚了整個濱河市彭则,隨后出現(xiàn)的幾起案子瓦胎,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件莽龟,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進店門左刽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捺信,“玉大人迄靠,你說我怎么就攤上這事。” “怎么了抽米?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我闺兢,道長茂缚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮凑术,結(jié)果婚禮上翩蘸,老公的妹妹穿的比我還像新娘。我一直安慰自己淮逊,他們只是感情好催首,可當我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著泄鹏,像睡著了一般郎任。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上备籽,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天舶治,我揣著相機與錄音,去河邊找鬼车猬。 笑死霉猛,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的珠闰。 我是一名探鬼主播惜浅,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼伏嗜!你這毒婦竟也來了坛悉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤承绸,失蹤者是張志新(化名)和其女友劉穎裸影,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體军熏,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡轩猩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了羞迷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片界轩。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡画饥,死狀恐怖衔瓮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情抖甘,我是刑警寧澤热鞍,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響薇宠,放射性物質(zhì)發(fā)生泄漏偷办。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一澄港、第九天 我趴在偏房一處隱蔽的房頂上張望椒涯。 院中可真熱鬧,春花似錦回梧、人聲如沸废岂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽湖苞。三九已至,卻和暖如春详囤,著一層夾襖步出監(jiān)牢的瞬間财骨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工藏姐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留隆箩,地道東北人。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓羔杨,卻偏偏與公主長得像摘仅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子问畅,可洞房花燭夜當晚...
    茶點故事閱讀 43,554評論 2 349

推薦閱讀更多精彩內(nèi)容