您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Determines which geographical divisions are in a Viewport intersect with the given BBOX.
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/542477/1623829/wmeGisLBBOX.js
// ==UserScript== // @name wmeGisLBBOX // @namespace https://github.com/JS55CT/ // @author JS55CT // @description Determines which geographical divisions are in a Viewport intersect with the given BBOX. // @version 2026.07.13.00 // @license MIT // @grant GM_xmlhttpRequest // @connect github.io // @match *://this-library-is-not-supposed-to-run.com/* // @contributionURL https://github.com/WazeDev/Thank-The-Authors // ==/UserScript== var wmeGisLBBOX = (function () { // Constructor for the wmeGisLBBOX class const funcName = "wmeGisLBBOX"; const BASE_URL = `https://WazeDev.github.io/wmeGisLBBOX/`; const BASE_URL_BBOX = `${BASE_URL}BBOX%20JSON/`; const BASE_URL_GEOJSON = `${BASE_URL}GEOJSON/`; function wmeGisLBBOX() { // Ensure class instantiation with 'new' if (!(this instanceof wmeGisLBBOX)) { return new wmeGisLBBOX(); } this.cache = {}; // Cache for storing fetched JSON / geoJSON data } /** * Fetches JSON data from a specified URL, utilizing caching to store and reuse fetched data. * * This function makes an HTTP GET request to retrieve data from a given URL and employs caching * to improve efficiency by storing previously fetched results. It uses `GM_xmlhttpRequest` for * cross-domain requests when running in a userscript environment. * * Process Overview: * 1. Checks the cache for existing data associated with the URL. * 2. If cached data is available, returns it immediately. * 3. If no cache is found, performs an HTTP request to fetch the data. * 4. On successful fetch, parses the JSON, stores it in cache, and resolves with the data. * 5. Handles errors, rejecting the Promise with an appropriate error message if the fetch fails. * * @param {string} url - The URL from which to fetch JSON data. * @returns {Promise<Object>} - A Promise resolving to the parsed JSON data. **/ wmeGisLBBOX.prototype.fetchJsonWithCache = function (url) { if (this.cache[url]) { return Promise.resolve(this.cache[url]); } // Fetch data using GM_xmlhttpRequest return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: url, onload: (response) => { if (response.status >= 200 && response.status < 300) { // Parse and store fetched data in cache const data = JSON.parse(response.responseText); this.cache[url] = data; resolve(data); } else { reject(new Error(`${funcName}: Failed to fetch data from ${url}, status: ${response.status}`)); } }, onerror: (error) => { reject(new Error(`${funcName}: Failed to fetch data from ${url}, error: ${error}`)); }, }); }); }; /** * Determines whether two bounding boxes intersect. * * This function checks for intersection between two bounding boxes `bbox1` and `bbox2` * by evaluating both latitude and longitude overlapping scenarios. Special handling is included * to manage cases where bounding boxes wrap the antimeridian (180 degrees longitude). * * Process Overview: * 1. Checks latitude intersection by evaluating if latitude ranges overlap. * 2. Checks longitude intersection, considering whether a bounding box wraps the antimeridian. * 3. Returns true if both latitude and longitude intersections occur; false otherwise. * * Longitude Intersection Scenarios: * - Standard check if neither bounding box wraps the antimeridian. * - Special checks for when one or both bounding boxes wrap the antimeridian. * * @param {Object} bbox1 - The first bounding box with properties minLat, maxLat, minLon, maxLon. * @param {Object} bbox2 - The second bounding box with properties minLat, maxLat, minLon, maxLon. * @returns {boolean} - True if the bounding boxes intersect, false otherwise. **/ function checkIntersection(bbox1, bbox2) { // Check for Latitude Intersection const latIntersects = !(bbox1.maxLat < bbox2.minLat || bbox1.minLat > bbox2.maxLat); if (!latIntersects) { return false; } // Check for Longitude Intersection const bbox1Wraps = bbox1.minLon > bbox1.maxLon; const bbox2Wraps = bbox2.minLon > bbox2.maxLon; let lonIntersects = false; if (!bbox1Wraps && !bbox2Wraps) { // Neither box wraps the antimeridian - standard overlap check lonIntersects = !(bbox1.maxLon < bbox2.minLon || bbox1.minLon > bbox2.maxLon); } else if (bbox1Wraps && !bbox2Wraps) { // bbox1 wraps and bbox2 does not const separated = bbox1.maxLon < bbox2.minLon && bbox1.minLon > bbox2.maxLon; lonIntersects = !separated; } else if (!bbox1Wraps && bbox2Wraps) { // bbox2 wraps and bbox1 does not const separated = bbox2.maxLon < bbox1.minLon && bbox2.minLon > bbox1.maxLon; lonIntersects = !separated; } else { lonIntersects = true; // Both boxes wrap the antimeridian } return lonIntersects; // Return true if both latitude and longitude intersect } /** * Identifies countries intersecting with the specified viewport bounding box. * * This function fetches a list of countries defined by their bounding boxes, * checks for intersection with the current viewport, and returns details for * countries that intersect. * * Process Overview: * 1. Fetches country bounding box data from a specified JSON URL. * 2. Iterates over country data, checking each one's bounding boxes against the viewport. * 3. Collects information on countries that intersect, including ISO codes and names. * * viewportBbox: {minLon: number, minLat: number, maxLon: number, maxLat: number} * * Error Handling: * - Logs errors during the fetch process and returns an empty array if issues occur. * * @param {Object} viewportBbox - The bounding box defining the current viewport. * @returns {Array} - An array of objects representing countries intersecting with the viewport. **/ wmeGisLBBOX.prototype.getIntersectingCountries = async function (viewportBbox) { const url = `${BASE_URL_BBOX}COUNTRIES_BBOX_ESPG4326.json`; return this.fetchJsonWithCache(url) .then((COUNTRY_DATA) => { const intersectingCountries = Object.keys(COUNTRY_DATA).flatMap((code) => { const countryData = COUNTRY_DATA[code]; for (const bbox of countryData.bbox) { if (checkIntersection(bbox, viewportBbox)) { return [ { ISO_ALPHA2: countryData["ISO_ALPHA2"], ISO_ALPHA3: countryData["ISO_ALPHA3"], name: countryData["name"], Sub_level: countryData["Sub_level"], source: "BBOX", }, ]; } } return []; }); return intersectingCountries; }) .catch((error) => { console.error(`${funcName}: Error fetching country data:`, error); return []; }); }; /** * Fetches and augments country data with subdivision information. * * This asynchronous function retrieves base country information and then * iteratively fetches additional subdivision data for each country, augmenting * the country data with its respective subdivisions. * * @returns {Object} - A comprehensive data object containing country details and * corresponding first-level subdivisions. * * Process Overview: * 1. Fetches country data from a static JSON file. * 2. Iterates through each country and retrieves its subdivision JSON data. * 3. Adds the fetched subdivisions data to the country information structure. * * Error Handling: * - Logs an error if fetching either the country or subdivision data fails. * - Returns an empty object if the initial fetch fails. * * URLs: * - Subdivision Data: Dynamic URL based on country ISO_ALPHA3 code from the base URL. **/ wmeGisLBBOX.prototype.getCountriesAndSubsJson = async function () { const url = `${BASE_URL_BBOX}COUNTRIES_BBOX_ESPG4326.json`; const funcName = "getCountriesAndSubsJson"; try { // Fetch the country data const COUNTRY_DATA = await this.fetchJsonWithCache(url); if (!COUNTRY_DATA) { console.warn(`${funcName}: No country data found.`); return {}; } // Iterate over each country to fetch and add subdivision data for (const countryCode in COUNTRY_DATA) { if (COUNTRY_DATA.hasOwnProperty(countryCode)) { // Fetch the first-level subdivision data for the country const isoAlpha3 = COUNTRY_DATA[countryCode].ISO_ALPHA3; const subL1Url = `${BASE_URL_BBOX}${isoAlpha3}/${isoAlpha3}_BBOX_ESPG4326.json`; try { const subL1Data = await this.fetchJsonWithCache(subL1Url); if (!subL1Data) { COUNTRY_DATA[countryCode].subL1 = {}; // Initialize as empty object if no data is found } else { // Add subdivisions to the country data COUNTRY_DATA[countryCode].subL1 = subL1Data; } } catch (subError) { COUNTRY_DATA[countryCode].subL1 = {}; // Safely initialize as empty object in case of error } } } return COUNTRY_DATA; } catch (error) { console.warn(`${funcName}: Error fetching country data:`, error); return {}; } }; /** * Cleans intersecting country data by pruning empty subdivisions across different levels. * * This function processes country data that intersects with a given geographic area, * handling hierarchical subdivision structures and removing any empty or null subdivisions. * It dynamically adapts to the subdivision level identified for each country. * * Process Overview: * 1. Iterates through intersecting country data. * 2. Applies cleaning logic based on subdivision levels (subLevel) present. * 3. Specifically handles multi-level subdivisions, including special logic for deep levels. * 4. Recursively prunes subdivisions that lack content, ensuring only relevant data remains. * * Cleaning Logic by Subdivision Level: * - Sub_Level = 1: Retains countries and sub-level 1 if they contain valid subdivisions. * - Sub_Level = 2: Further inspects sub-level 2; if empty, removes it and reveals sub-level 1 if needed. * - Sub_Level = 3: Examines sub-level 3 within applicable countries, removing them if empty and ensuring sub-level 2 remains relevant. * - Undetected or Sub_Level = 0: Completely removes countries with unidentified subdivisions. * * @param {Object} intersectingCountries - Object containing intersecting country data. * * Modifications: * - Directly alters the `intersectingCountries` object by removing empty subdivisions. * - Ensures structural integrity by retaining populated subdivisions and appropriate country entries. */ wmeGisLBBOX.prototype.cleanIntersectingData = function (intersectingCountries) { for (const countryName in intersectingCountries) { const country = intersectingCountries[countryName]; const subLevel = country.Sub_level || 0; // Default to 0 if Sub_level is not provided // Handle Sub_level = 0: Return the country directly if (subLevel === 0) { continue; // This means we should retain the country as is without further checks } // Proceed with cleanup based on sub-level hierarchy if (subLevel >= 3) { for (const sub1Name in country.subL1) { const subL1 = country.subL1[sub1Name]; for (const sub2Name in subL1.subL2) { const subL2 = subL1.subL2[sub2Name]; if (!subL2.subL3 || Object.keys(subL2.subL3).length === 0) { delete subL1.subL2[sub2Name]; } } } } if (subLevel >= 2) { for (const sub1Name in country.subL1) { const subL1 = country.subL1[sub1Name]; if (!subL1.subL2 || Object.keys(subL1.subL2).length === 0) { delete country.subL1[sub1Name]; } } } if (subLevel >= 1) { if (!country.subL1 || Object.keys(country.subL1).length === 0) { delete intersectingCountries[countryName]; } } } }; /** * Fetches GeoJSON data for a specified region and checks for intersection with a given viewport. * * This asynchronous function retrieves GeoJSON data based on the specified country, subdivision, * and sub-subdivision codes. It then checks whether the geometries in the GeoJSON intersect * with the provided viewport bounding box, returning intersection results or the full GeoJSON data. * * Process Overview: * 1. Constructs the URL for the GeoJSON file based on provided codes. * 2. Fetches the GeoJSON data using the constructed URL. * 3. Defines the viewport area as a polygon using the specified bounding box coordinates. * 4. Iterates through GeoJSON features, checking if their geometries intersect with the viewport. * 5. Returns either the full GeoJSON data or a boolean indicating intersection presence based on a flag. * * Supported Geometry Types: * - Polygon: Directly checks for intersection with the viewport polygon. * - MultiPolygon: Iterates through component polygons to check for intersection. * - Logs a warning if any unsupported geometry types are encountered. * * Error Handling: * - Logs an error message if fetching or processing the GeoJSON fails, returns `false`. * * viewportBbox: {minLon: number, minLat: number, maxLon: number, maxLat: number} * * @param {string} countyCode - The country code corresponding to the GeoJSON data. * @param {string} subCode - The subdivision code for the GeoJSON data. * @param {string} subSubCode - The sub-subdivision code for the GeoJSON data. * @param {Object} viewportBbox - The bounding box of the viewport. * @param {boolean} [returnGeoJson=false] - Flag to determine if the full GeoJSON data should be returned. * @returns {boolean|Object} - Returns true or false for intersection presence, or the full GeoJSON data. **/ wmeGisLBBOX.prototype.fetchAndCheckGeoJsonIntersection = async function (countyCode, subCode, subSubCode, viewportBbox, returnGeoJson = false) { const url = `${BASE_URL_GEOJSON}${countyCode}/${subCode}/${countyCode}-${subCode}-${subSubCode}_EPSG4326.geojson`; try { const geoJsonData = await this.fetchJsonWithCache(url); // Define the viewport as a polygon. const viewportPolygon = [ [viewportBbox.minLon, viewportBbox.minLat], [viewportBbox.minLon, viewportBbox.maxLat], [viewportBbox.maxLon, viewportBbox.maxLat], [viewportBbox.maxLon, viewportBbox.minLat], [viewportBbox.minLon, viewportBbox.minLat], ]; // Iterate through each feature in the GeoJSON data for (const feature of geoJsonData.features) { const featureGeometry = feature.geometry; // Check if the geometry type is Polygon or MultiPolygon if (featureGeometry.type === "Polygon") { for (const polygon of featureGeometry.coordinates) { if (hasIntersection(polygon, viewportPolygon)) { return returnGeoJson ? geoJsonData : true; // Return the GeoJSON data or intersection boolean } } } else if (featureGeometry.type === "MultiPolygon") { for (const multiPolygon of featureGeometry.coordinates) { for (const polygon of multiPolygon) { if (hasIntersection(polygon, viewportPolygon)) { return returnGeoJson ? geoJsonData : true; // Return the GeoJSON data or intersection boolean } } } } else { console.warn(`${funcName}: Unsupported geometry type:`, featureGeometry.type); continue; // Skip unsupported geometry types } } return false; // No intersection found } catch (error) { console.error(`${funcName}: Error fetching or processing GeoJSON from ${url}:`, error); return false; } }; /** * Identifies US states and counties that intersect with the given viewport bounding box. * * This asynchronous function fetches state and county bounding box data, identifies which states * and counties intersect with a specified viewport, and optionally checks for more precise * intersections using GeoJSON data. * * Process Overview: * 1. Retrieves bounding box data for US states. * 2. Checks each state's bounding box against the viewport for intersections. * 3. For intersecting states, fetches county bounding box data and identifies intersecting counties. * 4. If `highPrecision` is true, uses GeoJSON data to refine intersection checks. * 5. Constructs an object that maps intersecting states to their respective intersecting counties and subdivisions. * * Optional Features: * - High precision mode uses GeoJSON data to confirm intersections and update data sources. * - Supports returning GeoJSON data if intersections occur and `returnGeoJson` is true. * * viewportBbox: {minLon: number, minLat: number, maxLon: number, maxLat: number} * * Error Handling: * - Logs a message if fetching or processing bounding box or GeoJSON data fails. * * @param {Object} viewportBbox - The bounding box of the viewport. * @param {boolean} [highPrecision=false] - Flag to activate high precision intersection checks using GeoJSON. * @param {boolean} [returnGeoJson=false] - Flag to indicate if GeoJSON data should be returned when intersecting. * @returns {Object} - A structured object detailing intersecting states, counties, and subdivisions. **/ wmeGisLBBOX.prototype.getIntersectingStatesAndCounties = async function (viewportBbox, highPrecision = false, returnGeoJson = false) { const STATES_URL = `${BASE_URL_BBOX}USA/USA_BBOX_ESPG4326.json`; const intersectingRegions = {}; try { const US_States = await this.fetchJsonWithCache(STATES_URL); for (const stateCode in US_States) { const stateData = US_States[stateCode]; if (checkIntersection(stateData.bbox, viewportBbox)) { const intersectingCounties = {}; const countiesUrl = `${BASE_URL_BBOX}USA/USA-${stateCode}_BBOX_ESPG4326.json`; const countiesData = await this.fetchJsonWithCache(countiesUrl); for (const countyEntry of countiesData) { const countyName = Object.keys(countyEntry)[0]; const countyData = countyEntry[countyName]; if (checkIntersection(countyData.bbox, viewportBbox)) { const intersectingSubCounties = countyData.subdivisions .filter((sub) => sub.bbox && checkIntersection(sub.bbox, viewportBbox)) .reduce((acc, sub) => { acc[sub.name] = { subL3_num: sub.sub_num, source: "BBOX", }; return acc; }, {}); if (Object.keys(intersectingSubCounties).length > 0) { let source = "BBOX"; let geoJsonData = null; if (highPrecision) { const intersectsGeoJson = await this.fetchAndCheckGeoJsonIntersection("USA", stateCode, countyData.sub_num, viewportBbox, returnGeoJson); if (returnGeoJson && intersectsGeoJson) { geoJsonData = intersectsGeoJson; source = "GEOJSON"; } else if (intersectsGeoJson) { source = "GEOJSON"; } else { continue; } } intersectingCounties[countyName] = { subL2_num: countyData.sub_num, subL3: intersectingSubCounties, source: source, }; if (geoJsonData) { intersectingCounties[countyName].geoJsonData = geoJsonData; } } } } if (Object.keys(intersectingCounties).length > 0) { let stateSource = "BBOX"; if (highPrecision) { const anyCountyWithGeoJson = Object.values(intersectingCounties).some((county) => county.source === "GEOJSON"); if (anyCountyWithGeoJson) { stateSource = "GEOJSON"; } } intersectingRegions[stateData.name] = { subL1_id: stateData.sub_id, subL1_num: stateData.sub_num, subL2: intersectingCounties, source: stateSource, }; } } } } catch (error) { console.error(`${funcName}: Failed to fetch or process data:`, error); } return intersectingRegions; }; /** * Retrieves and identifies subdivisions that intersect with a given viewport bounding box. * * This asynchronous function fetches hierarchical subdivision data for a specified country based * on the subdivisions level (`Sub_level`). It checks for intersections at both first and second subdivision levels * (if applicable) and returns structured data detailing the intersecting areas. * * Process Overview: * 1. Constructs the URL for the country's first-level subdivision data. * 2. Fetches and checks first-level subdivisions for intersection with the viewport. * 3. For intersecting first-level subdivisions and if `Sub_level` is 2, constructs URLs for second-level data. * 4. Checks second-level subdivisions for intersection and integrate results into the output. * 5. Builds and returns a comprehensive object of intersecting subdivisions. * * Parameters: * - `viewportBbox`: {minLon: number, minLat: number, maxLon: number, maxLat: number} * Represents the geographic viewport for intersection checks. * * Error Handling: * - Logs warnings if no first-level or second-level data are found. * - Logs errors for issues with fetching or processing subdivisions. * * @param {Object} countryObj - An object containing details about the country, including ISO code and Sub_level. * @param {Object} viewportBbox - The bounding box of the viewport to check against. * @returns {Object} - An object containing details of intersecting subdivisions, organized by hierarchy. */ wmeGisLBBOX.prototype.getIntersectingSubdivisions = async function (countryObj, viewportBbox) { const subdivisionsResult = {}; const countryCode = countryObj.ISO_ALPHA3; const subL1Url = `${BASE_URL_BBOX}${countryCode}/${countryCode}_BBOX_ESPG4326.json`; const funcName = "getIntersectingSubdivisions"; try { // Check Sub_Level to decide how to handle subdivisions if (!countryObj.Sub_level) { return subdivisionsResult; // Return {} if Sub_Level is 0 or undefined } if (countryObj.Sub_level >= 1) { // Fetch first-level subdivision data const subL1Data = await this.fetchJsonWithCache(subL1Url); if (!subL1Data) { console.warn(`${funcName}: No first-level subdivision data found for country code: ${countryCode}`); return subdivisionsResult; } for (const subdivisionID in subL1Data) { const subdivision = subL1Data[subdivisionID]; // Check intersection for first-level subdivisions if (checkIntersection(subdivision.bbox, viewportBbox)) { const subdivisionName = subdivision["name"]; // Initialize data for this subdivision subdivisionsResult[subdivisionName] = { subL1_num: subdivision["sub_num"], subL1_id: subdivisionID, source: "BBOX", subL2: {}, // Placeholder for second-level subdivisions }; // If Sub_Level is 2, fetch second-level subdivision data if (countryObj.Sub_level === 2) { const subL2Url = `${BASE_URL_BBOX}${countryCode}/${countryCode}-${subdivisionID}_BBOX_ESPG4326.json`; try { const subL2Data = await this.fetchJsonWithCache(subL2Url); if (!subL2Data) { console.warn(`${funcName}: No second-level subdivision data found for ${subdivisionName} (${subdivisionID}) in country: ${countryCode}`); } else { // Process second-level subdivisions for (const subsubDivision of subL2Data) { const subsubID = Object.keys(subsubDivision)[0]; const subsub = subsubDivision[subsubID]; // Check intersection for second-level subdivisions if (checkIntersection(subsub.bbox, viewportBbox)) { const subsubName = subsub["name"]; // Add intersecting second-level subdivisions subdivisionsResult[subdivisionName].subL2[subsubName] = { subL2_num: subsub["sub_num"], source: "BBOX", }; } } } } catch (subError) { console.warn(`${funcName}: Error fetching second-level data for ${subdivisionName} (${subdivisionID}) in country: ${countryCode} - ${subError.message}`); } } // End of Sub_Level 2 check } } } } catch (error) { console.error(`${funcName}: Error fetching or processing subdivisions for country code ${countryCode} - ${error.message}`); } return subdivisionsResult; }; /** * Determines regions within the viewport by identifying intersecting countries and their subdivisions. * * This asynchronous function analyzes a given viewport bounding box to determine which countries * and internal subdivisions, such as states and counties, are visible. It accommodates the USA * specifically and uses different handling for other countries. * * Process Overview: * 1. Fetches countries that intersect with the viewport. * 2. If no intersecting countries are found, logs a warning. * 3. For the USA, retrieves intersecting states and counties; for other countries, retrieves subdivisions. * 4. Cleans up intersecting data to ensure only relevant information is retained. * 5. Constructs a results object that maps visible countries to their intersecting subdivisions. * * Features: * - Supports high precision checks using GeoJSON data if specified. * - Integration of subdivision logic for both the USA and other countries. * * viewportBbox: {minLon: number, minLat: number, maxLon: number, maxLat: number} * * Error Handling: * - Logs errors encountered during the fetching and processing of intersecting regions. * * @param {Object} viewportBbox - The bounding box defining the current viewport. * @param {boolean} [highPrecision=false] - Flag to use high precision intersection checks. * @param {boolean} [returnGeoJson=false] - Flag to determine if GeoJSON data should be returned. * @returns {Object} - Structured results identifying regions within the viewport. **/ wmeGisLBBOX.prototype.whatsInView = async function (viewportBbox, highPrecision = false, returnGeoJson = false) { const results = {}; const funcName = "whatsInView"; try { const countries = await this.getIntersectingCountries(viewportBbox); if (!countries || Object.keys(countries).length === 0) { console.warn(`${funcName}: Viewport does not intersect with any known countries.`); return results; } for (const countryCode in countries) { const country = countries[countryCode]; // Handle the case when Sub_level is 0 if (country.Sub_level === 0) { results[country.name] = { ISO_ALPHA2: country.ISO_ALPHA2, ISO_ALPHA3: country.ISO_ALPHA3, Sub_level: country.Sub_level, source: country.source, subL1: {}, // Leave subL1 empty when Sub_level is 0 }; continue; } if (country.ISO_ALPHA3 === "USA") { const statesAndCounties = await this.getIntersectingStatesAndCounties(viewportBbox, highPrecision, returnGeoJson); if (statesAndCounties && Object.keys(statesAndCounties).length > 0) { results[country.name] = { ISO_ALPHA2: country.ISO_ALPHA2, ISO_ALPHA3: country.ISO_ALPHA3, Sub_level: country.Sub_level, subL1: statesAndCounties, }; this.cleanIntersectingData(results); } } else { const subdivisions = await this.getIntersectingSubdivisions(country, viewportBbox); if (subdivisions && Object.keys(subdivisions).length > 0) { results[country.name] = { ISO_ALPHA2: country.ISO_ALPHA2, ISO_ALPHA3: country.ISO_ALPHA3, Sub_level: country.Sub_level, subL1: subdivisions, }; this.cleanIntersectingData(results); } } } } catch (error) { console.error(`${funcName}: Error during finding intersecting regions:`, error); } return results; }; /** * Determines if a given point is inside a polygon using the ray-casting algorithm. * * This function checks whether a point, defined by its coordinates, is inside a polygon. * The polygon is represented by an array of vertices (points), and the function uses the * ray-casting technique to toggle the state whenever the ray crosses a polygon edge. * * Process Overview: * 1. Iterates over each edge of the polygon using vertex pairs (xi, yi) and (xj, yj). * 2. Uses logical conditions to check if a horizontal ray extending from the point intersects * with the polygon's edge. * 3. Toggles 'inside' state whenever an intersection is detected, flipping between true and false. * 4. Returns the final 'inside' state, indicating whether the point lies within the polygon. * * @param {Array} point - An array [x, y] representing the coordinates of the point to test. * @param {Array} vs - An array of vertices, where each vertex is represented as [x, y]. * @returns {boolean} - True if the point is inside the polygon, false otherwise. **/ function isPointInPolygon(point, vs) { const [x, y] = point; let inside = false; for (let i = 0, j = vs.length - 1; i < vs.length; j = i++) { const [xi, yi] = vs[i]; const [xj, yj] = vs[j]; const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi; if (intersect) inside = !inside; } return inside; } /** * Calculates the intersection point, if any, between two line segments defined by endpoints. * * This function checks whether two line segments, one defined by (p1, p2) and the other by (q1, q2), * intersect. The method utilizes linear algebra to derive intersection coordinates and subsequently * verifies that the intersection falls within the bounds of both segments. * * Process Overview: * 1. Computes coefficients for the line equations using segment endpoints. * 2. Determines if the lines are parallel (denominator = 0), in which case no intersection exists. * 3. Calculates potential intersection coordinates using determinant-based formulae. * 4. Verifies if the intersection coordinates lie within the bounds of both line segments. * 5. Returns the intersection point if valid, or null if no intersection exists within segment limits. * * @param {Array} p1 - [x, y] coordinates of the first endpoint of the first segment. * @param {Array} p2 - [x, y] coordinates of the second endpoint of the first segment. * @param {Array} q1 - [x, y] coordinates of the first endpoint of the second segment. * @param {Array} q2 - [x, y] coordinates of the second endpoint of the second segment. * @returns {Array|null} - [x, y] coordinates of the intersection point, or null if no intersection. **/ function segmentIntersection(p1, p2, q1, q2) { // Calculate coefficients const a1 = p2[1] - p1[1]; const b1 = p1[0] - p2[0]; const c1 = a1 * p1[0] + b1 * p1[1]; const a2 = q2[1] - q1[1]; const b2 = q1[0] - q2[0]; const c2 = a2 * q1[0] + b2 * q1[1]; const denominator = a1 * b2 - a2 * b1; if (denominator === 0) { return null; // Parallel lines } const intersectX = (b2 * c1 - b1 * c2) / denominator; const intersectY = (a1 * c2 - a2 * c1) / denominator; // Check if the intersection is within the bounds of the line segments const withinBounds = (value, end1, end2) => Math.min(end1, end2) <= value && value <= Math.max(end1, end2); if (withinBounds(intersectX, p1[0], p2[0]) && withinBounds(intersectY, p1[1], p2[1]) && withinBounds(intersectX, q1[0], q2[0]) && withinBounds(intersectY, q1[1], q2[1])) { return [intersectX, intersectY]; } return null; // Intersection point is not within the line segments } /** * Checks for intersection between two polygons. * * This function determines if two polygons intersect by examining: * 1. If any edges from `polygon1` intersect with any edges from `polygon2`. * 2. If any point from `polygon1` is contained within `polygon2`. * 3. If any point from `polygon2` is contained within `polygon1`. * * The function uses helper methods `segmentIntersection` to check edge intersections * and `isPointInPolygon` to evaluate point containment. * * Process Overview: * - Iterates over edges of both polygons, checking for intersection using the `segmentIntersection` function. * - Verifies point containment using the `isPointInPolygon` function to see if any point from one polygon is inside the other. * - Returns true if any intersection or containment is found; otherwise, returns false. * * @param {Array} polygon1 - An array of points defining the first polygon, where each point is an {x, y} object. * @param {Array} polygon2 - An array of points defining the second polygon, where each point is an {x, y} object. * @returns {boolean} - True if an intersection or containment is found between the polygons, false otherwise. **/ function hasIntersection(polygon1, polygon2) { // Check each edge of polygon1 against each edge of polygon2 for (let i = 0; i < polygon1.length - 1; i++) { for (let j = 0; j < polygon2.length - 1; j++) { const intersection = segmentIntersection(polygon1[i], polygon1[i + 1], polygon2[j], polygon2[j + 1]); if (intersection) { return true; // An intersection is found } } } // Check if any point of polygon1 is inside polygon2 for (const point of polygon1) { if (isPointInPolygon(point, polygon2)) { return true; // A contained point is found } } // Check if any point of polygon2 is inside polygon1 for (const point of polygon2) { if (isPointInPolygon(point, polygon1)) { return true; // A contained point is found } } return false; // No intersection found } return wmeGisLBBOX; })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址