Adds a Google Map with eBird alert locations as markers.
当前为
// ==UserScript==
// @name eBird Alerts Map
// @namespace http://tampermonkey.net/
// @version 2024-04-19_1.4
// @description Adds a Google Map with eBird alert locations as markers.
// @author Ruslan Balagansky
// @license MIT
// @match https://ebird.org/alert/needs/*
// @match https://ebird.org/alert/rba/*
// @match https://ebird.org/alert/summary?sid=*
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @icon https://www.google.com/s2/favicons?sz=64&domain=tampermonkey.net
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// ==/UserScript==
(function() {
'use strict';
const mapDivId = 'userscript-map';
// load api key from storage or use default
const defaultApiKey = "AIzaSyCNhkdcs7rdwXoaSpqDzNLBnA-4Tu_7v-4" // restricted to ebird.org
var apiKey = GM_getValue("apiKey", defaultApiKey);
// handle case when stored value is an empty string
if (!apiKey) {
apiKey = defaultApiKey;
}
// allow user to set a custom key via script's menu (via Tampermonkey extension icon)
function promptForApiKey() {
apiKey = prompt("Enter a Google Maps API key or accept the author-provided one: ", defaultApiKey);
GM_setValue("apiKey", apiKey);
}
GM_registerMenuCommand("Change Google Maps API Key", promptForApiKey);
// initializes the map (called by legacy API callback)
function initMap() {
// Collect locations data
var locations = {};
const mapRegex = /Map: (.+), (.+)/;
const observations = document.getElementsByClassName("Observation");
for (const obs of observations) {
const species = obs.getElementsByClassName("Observation-species")[0];
const specRef = species.getElementsByTagName("a")[0];
const speciesCode = specRef.getAttribute("data-species-code");
const fourLetterSpecies = speciesCode.slice(0, 2) + speciesCode.slice(3, 5)
const meta = obs.getElementsByClassName("Observation-meta")[0];
let coords;
let key;
for (const a of meta.getElementsByTagName("a")) {
const title = a.getAttribute("title");
const mapMatch = title.match(mapRegex);
if (mapMatch) {
key = title;
coords = { lat: Number(mapMatch[1]), lng: Number(mapMatch[2]) };
}
}
if (!(key in locations)) {
locations[key] = {
labels:new Set(),
html:[],
speciesHtml:{}
};
}
var loc = locations[key];
loc.coords = coords;
loc.labels.add(fourLetterSpecies);
loc.html.push(obs.innerHTML);
loc.speciesHtml[speciesCode] = species.innerHTML;
}
// compute the center based on alert locations
var mapCenter = { lat: 32.92, lng: -116.85 }; // default to San Diego
let minLat = Infinity, minLng = Infinity, maxLat = -Infinity, maxLng = -Infinity;
for (const loc of Object.values(locations)) {
const lat = loc.coords.lat;
const lng = loc.coords.lng;
minLat = Math.min(minLat, lat);
maxLat = Math.max(maxLat, lat);
minLng = Math.min(minLng, lng);
maxLng = Math.max(maxLng, lng);
}
mapCenter = { lat: (maxLat + minLat) / 2, lng: (maxLng + minLng) / 2 };
// Create the map object
var mapOptions = {
center: mapCenter,
zoom: 9 // Set the initial zoom level
};
var map = new google.maps.Map(document.getElementById(mapDivId), mapOptions);
// Create an InfoWindow for markers
const infoWindow = new google.maps.InfoWindow();
// Create location markers
for (const location of Object.values(locations)) {
var label = location.labels.values().next().value;
if (location.labels.size > 1) {
label = (location.labels.size).toString();
}
const marker = new google.maps.Marker({
map: map,
position: location.coords,
label: {
text: label,
fontFamily: 'Arial Narrow',
color: 'white',
fontSize: '12px'
}
});
marker.addListener("click", () => {
infoWindow.close();
infoWindow.setContent(Object.values(location.speciesHtml).join("") + "<hr>" + location.html.join("<hr>"));
infoWindow.open(marker.getMap(), marker);
});
}
}
// adds map div and sets up the callback to initialize the map
function embedGoogleMap() {
// Create a div element to hold the map
var mapDiv = document.createElement('div');
mapDiv.id = mapDivId; // Set the ID for the div
// Set the size and position of the map div
mapDiv.style.width = '800px';
mapDiv.style.height = '400px';
mapDiv.style.margin = 'auto';
// add map div above the list section
var pageSectionInner = document.getElementsByClassName("Page-section--primaryLight")[0];
pageSectionInner.appendChild(mapDiv);
// Load the Google Maps JavaScript API
var script = document.createElement('script');
script.src = 'https://maps.googleapis.com/maps/api/js?key=' + apiKey;
script.defer = true;
document.head.appendChild(script);
// Call the initMap function once the API script is loaded
script.onload = function() {
initMap();
};
}
// do it!
embedGoogleMap();
})();