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文件转为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 : {};
    // 构建成员变量:点,线,面集合,只有面List有数据
    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的集合数据
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];
  // 循环到d=2
  if (dim.list.length > 0) {
    dim.bboxs = new Array(dim.list.length);
    // 构造一个对象:
    //{
    //  data: {children: Array(0), height: 1, leaf: true, maxX: -Infinity,maxY: -Infinity,minX: Infinity,minY: Infinity}
    //  _maxEntries: 9
    //  _minEntries: 4
    //}
    dim.lookup = rbush();

    // 数组集合:{type: FEATURE, geometry: {type: POLYGON, coordinates}, properties}
    const geoms = dim.list;
    // 21个zise的空数组
    const bboxs = dim.bboxs;
    // 上面构建的对象
    const lookup = dim.lookup;
    
    // 把数组的每个二维元素集合的点的边界框放置到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};
    }
    // 把这些元素组成框,构成r-tree结构的树,到时候查询就会快很多
    lookup.load(bboxs);
  }
}

}

//接受一组要素,计算所有输入要素的bbox,然后返回一个边界框
function bbox(geojson) {
// 新建一个数组
var BBox = [Infinity, Infinity, -Infinity, -Infinity];
meta.coordEach(geojson, function (coord) {
//callback回调函数,取里面二维数组的每一点来与Bbox比较。BBox最后取到二维数组的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); // 结果是9
this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));  // 结果是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 : {};
    // 构建点,线,面集合 这里是3个空数组
    this.pointsList = options.ignorePoints === true ? undefined : [];
    this.linesList = options.ignoreLines === true ? undefined : [];
    this.polygonsList = options.ignorePolygons === true ? undefined : [];
    // 递归的构建不同类型的多边形集合,每个集合都是一个对象。polygonsList这个数组被赋值满
    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);
}
// 第二次调用命中这个case,21个元素,第一个元素有1008个,每个元素构建为对象,加载数据到多边形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));
}
// 第一次调用命中这个case,这个数组就一个元素
case GEOMETRY_COLLECTION: {
// 递归调用这个方法,
return geoJson.geometries.forEach(geometry => this._loadGeneric(geometry, properties));
}
default: break;
}
}

// 加载数据到多边形List
_loadPolygon(coordinates, properties) {
// 构建面集合中的对象
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;
  }
    // 构建一个矩形,现在输入的点。所以矩形的最大和最小的xy值是一样的
  const bbox = tbbox(geometry);
  // 查找这个点是否在r-tree中,返回r-tree中最终命中的叶子节点元素
  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,执行的是下面的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];
            // 如果是叶子节点,那么把叶子节点中的元素集合转化为矩形
            // 如果是非叶子节点,则吧子节点当做childBBox
            childBBox = node.leaf ? toBBox(child) : child;
            // 判断子节点和输入矩形是否相交
            if (intersects(bbox, childBBox)) {
                // 根节点是叶子节点,则把当前子节点放入结果中
                if (node.leaf) result.push(child);
                // 输入的矩形是否包含子节点矩形区域
                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;

}

}

}

你可能感兴趣的:(isSea)