feat: archive CW-033 deployed sign map prototype v0.1.0
This commit is contained in:
commit
2f1c437a55
17 files changed
+1562
No files matched your search
@@ -0,0 +1,3 @@
|
|||||||
|
RTC_NATIVE_API_BASE=
|
||||||
|
RTC_NATIVE_API_KEY=
|
||||||
|
RTC_CARTO_BASEMAP_KEY=
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
*.key
|
||||||
|
*key.txt
|
||||||
|
__pycache__/
|
||||||
|
input/
|
||||||
|
dist/
|
||||||
|
private/
|
||||||
|
sample-work/
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# CW-033 development notes
|
||||||
|
|
||||||
|
Version 0.1.0 is an archived tool prototype. No installable skill, plugin manifest, or new MCP server exists yet.
|
||||||
|
|
||||||
|
The data pipeline is OnSign native stop IDs → agency native API source-stop IDs → verified GTFS stops → active trips/routes → ordered GTFS shapes. GTFS coordinates remain authoritative; native coordinates are comparison evidence. Never join a native route ID to GTFS route_id by numerical equality.
|
||||||
|
|
||||||
|
The viewer supports online/offline signs, kiosks, excluded records, mapped-bay detail, Apple/Google navigation, vector basemap options, and credential-free fallback. It is a captured snapshot viewer, not live dispatch software.
|
||||||
|
|
||||||
|
Before another agency is supported, parameterize the RTC-specific dates/feed copy and add configuration/adapter contracts. Missing ID, name, station-parent, shape, or service-date evidence must be reported instead of guessed. A future predeployment spreadsheet/list input must use the same ID and provenance rules.
|
||||||
|
|
||||||
|
Credentials are supplied through the environment for acquisition. The renderer excludes a CARTO key unless explicitly asked to embed an approved browser key. Keep input/, dist/, private/, and all operational/customer sample-work out of the public repository. Upstream license notices remain under vendor/; no license has been selected for MPM-authored code.
|
||||||
|
|
||||||
|
Pending decisions: skill versus plugin/service boundary; multi-agency configuration; supported native adapters; ID-review workflow; refresh/access/hosting model; browser-key management; physical mobile navigation acceptance; and publication license. The full operational handoff and pilot evidence reside in the CW-033 Coordination Drive folder.
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
# Deployed Sign Map
|
||||||
|
|
||||||
|
Map deployed transit signs and kiosks against verified GTFS stops and routes, with player health and phone navigation.
|
||||||
|
|
||||||
|
**CoWork project:** CW-033 — Deployed Sign Map
|
||||||
|
**Version:** 0.1.0
|
||||||
|
**Type / status:** Tool / Beta prototype
|
||||||
|
**Owner:** Bryan Gilliom
|
||||||
|
**Contributor:** Codex
|
||||||
|
**Updated:** October 28, 2026
|
||||||
|
**Git repository:** https://git2.mpm.to/mpm/deployed-sign-map
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This working prototype combines a read-only OnSign player snapshot, an official public GTFS feed, and a native CAD/AVL stop-ID crosswalk. It supports service technicians, salespeople, and implementation teams who need to see deployed devices alongside their stops and scheduled routes. It preserves the difference between a bus-stop coordinate and the surveyed location of a physical sign. It does not modify OnSign or the CAD/AVL system.
|
||||||
|
|
||||||
|
The captured RTC Washoe pilot is complete as a snapshot. Conversion into an installable skill or plugin, live refresh, and other-agency support remain future work. There is no SKILL.md, plugin manifest, or new MCP server in this package.
|
||||||
|
|
||||||
|
## Capabilities and planned skill entry points
|
||||||
|
|
||||||
|
| Capability | Current behavior | Potential trigger |
|
||||||
|
|---|---|---|
|
||||||
|
| Deployment map | Shows field signs and kiosks with online/offline icons and excluded test/showroom records | “Map the deployed signs for this agency” |
|
||||||
|
| Native stop crosswalk | Resolves each OnSign `-S` ID through native `SrcStopID` to GTFS `stop_id`; retains comma-separated kiosk IDs individually | “Crosswalk the player stop IDs to GTFS” |
|
||||||
|
| Route geometry | Joins resolved stops to active GTFS trips; renders ordered shapes using `route_color` | “Show the routes serving these signs” |
|
||||||
|
| Service navigation | Provides Apple Maps and Google Maps driving links using GTFS coordinates | “Navigate to this stop” |
|
||||||
|
| Quiet basemaps | CARTO Positron vector, labels or no labels; hidden POIs; Esri gray, original streets, and plain background | “Use a quieter basemap” |
|
||||||
|
| Predeployment planning | Not built; planned spreadsheet/list intake with the same reconciliation rules | “Map these planned stop installations” |
|
||||||
|
|
||||||
|
These are prototype capabilities and proposed skill triggers, not already installed tools.
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
- `source/`: Python preparation, read-only native API fetch, renderer, integrity checks, HTML template, and pinned third-party map libraries.
|
||||||
|
- `sample-work/rtc-washoe/`: credential-free HTML map, source snapshots, public GTFS ZIP, resolved crosswalk, verification results, and PNG previews. Keep operational sample data in Coordination Drive, not in the public Git repository.
|
||||||
|
- `HANDOFF-BRIEF.md`: accepted decisions, evidence, open work, and exact continuation steps.
|
||||||
|
- `Wiki V2.md`: source for the native Google Docs wiki.
|
||||||
|
- `file-manifest.json`: file sizes and SHA-256 values for the archived package.
|
||||||
|
|
||||||
|
## Tools and interfaces used
|
||||||
|
|
||||||
|
| System | Read operations used in this work |
|
||||||
|
|---|---|
|
||||||
|
| Existing OnSign MCP (CW-032) | `find_players`, `account_summary`, `get_player`; investigation also used `get_data_feed` and `whats_playing` |
|
||||||
|
| Native Vontas traveler API | `GetStop`; `GetStopRouteTrip` was used to establish stop/trip identifier evidence; `?WSDL` exposed the read operation schema |
|
||||||
|
| Public GTFS | `feed_info`, `calendar`, `calendar_dates`, `stops`, `stop_times`, `trips`, `routes`, `shapes` |
|
||||||
|
| Browser map | Leaflet 1.9.4, MapLibre GL JS 5.6.2, MapLibre–Leaflet integration 0.1.3 |
|
||||||
|
|
||||||
|
## Reproduce the captured snapshot
|
||||||
|
|
||||||
|
Python 3 is sufficient for the data scripts. Third-party map libraries and their license notices are included under `source/vendor/`.
|
||||||
|
|
||||||
|
From the package root, with the source and sample-work archives extracted into sibling directories:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 source/scripts/prepare_data.py \
|
||||||
|
--players sample-work/rtc-washoe/onsign-players.json \
|
||||||
|
--native sample-work/rtc-washoe/native-stops.json \
|
||||||
|
--gtfs sample-work/rtc-washoe/rtc-gtfs.zip \
|
||||||
|
--service-date 2026-10-28 \
|
||||||
|
--output-dir dist
|
||||||
|
|
||||||
|
python3 source/scripts/verify_snapshot.py \
|
||||||
|
--data dist/rtc-map-data.json \
|
||||||
|
--native sample-work/rtc-washoe/native-stops.json \
|
||||||
|
--gtfs sample-work/rtc-washoe/rtc-gtfs.zip
|
||||||
|
|
||||||
|
python3 source/scripts/render_map.py \
|
||||||
|
--data dist/rtc-map-data.json \
|
||||||
|
--output dist/rtc-washoe-sign-map.html
|
||||||
|
|
||||||
|
python3 -m http.server 8765 --bind 127.0.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Open `http://127.0.0.1:8765/dist/rtc-washoe-sign-map.html`. The archive contains no API key. It defaults to Esri Gray Canvas; selecting a CARTO style asks for a basemap key held in that tab only. Rendering with `--include-carto-key` explicitly embeds `RTC_CARTO_BASEMAP_KEY`; use that only for an approved browser-key distribution.
|
||||||
|
|
||||||
|
The snapshot feed covers September 5, 2026–January 1, 2027. Replaying the captured date is intentional. A live rebuild requires a newly downloaded official feed, a fresh OnSign snapshot, and fresh native responses; do not present this capture as current health.
|
||||||
|
|
||||||
|
### Read native stops for a new snapshot
|
||||||
|
|
||||||
|
Set `RTC_NATIVE_API_BASE` to the approved HTTPS ASMX service URL and `RTC_NATIVE_API_KEY` through a secure local environment. Do not put credentials in a command, Git file, report, or handoff.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 source/scripts/fetch_native_stops.py \
|
||||||
|
--players input/onsign-players.json \
|
||||||
|
--output input/native-stops.json
|
||||||
|
```
|
||||||
|
|
||||||
|
The script calls only `GetStop`, validates the returned `StopID`, stores credential-free responses, throttles requests, and exits with an error if any requested ID fails. It does not fetch player records itself; the host must supply the existing OnSign MCP export.
|
||||||
|
|
||||||
|
### Setup checklist
|
||||||
|
|
||||||
|
- [ ] Extract source and private sample-work packages into the expected directory structure.
|
||||||
|
- [ ] Use Python 3 and a browser with WebGL for vector basemaps.
|
||||||
|
- [ ] Run preparation and integrity verification before rendering.
|
||||||
|
- [ ] Choose an approved CARTO basemap key or a key-free alternative.
|
||||||
|
- [ ] Preserve attribution for CARTO/OpenStreetMap or Esri and the bundled library notices.
|
||||||
|
- [ ] For live work, refresh inputs and label the OnSign/native/feed timestamps separately.
|
||||||
|
|
||||||
|
## Connection and credential model
|
||||||
|
|
||||||
|
| Field | Behavior |
|
||||||
|
|---|---|
|
||||||
|
| OnSign | Existing account-scoped, read-only MCP; no new direct OnSign API key is required by this tool |
|
||||||
|
| Native stop API | HTTPS GET; key stays in the acquisition process; authenticated URLs are not printed |
|
||||||
|
| Basemaps | Online CARTO vector service uses a browser basemap key; Esri/OSM raster and plain alternatives are available |
|
||||||
|
| Map data | Stops, routes, shapes, crosswalk, and player health are embedded as a captured snapshot |
|
||||||
|
| Remote writes | None to OnSign or native CAD/AVL |
|
||||||
|
|
||||||
|
## Workflow rules
|
||||||
|
|
||||||
|
1. Verify the agency's current official feed and service-date coverage before matching stops.
|
||||||
|
2. Extract native `-S` IDs, including each comma-separated kiosk ID. Preserve excluded records without inventing positions.
|
||||||
|
3. Use the native API's explicit `SrcStopID` crosswalk. Never assume a numeric native stop ID equals GTFS `stop_id`.
|
||||||
|
4. Use GTFS latitude/longitude for all markers and navigation. OnSign GPS, manually placed positions, and IP geolocation are not coordinate authority.
|
||||||
|
5. Derive route associations from resolved stop IDs, `stop_times`, and active `trips`; derive line geometry from ordered `shapes`. Never infer a route from a nearby line or equate native and GTFS route IDs.
|
||||||
|
6. For kiosks, use a clearly labeled parent-station anchor; derive routes from the resolved bay list, not every bay at the station.
|
||||||
|
7. Report ID failures, name conflicts, and native-versus-GTFS coordinate differences separately. The current 25 m flag is a review threshold, not a measured GPS accuracy claim.
|
||||||
|
8. Preserve keys outside published maps by default. Maintain private customer snapshots separately from public code.
|
||||||
|
|
||||||
|
## Known limits and troubleshooting
|
||||||
|
|
||||||
|
| Symptom / limit | Action |
|
||||||
|
|---|---|
|
||||||
|
| Source-stop ID is missing from current GTFS | Stop the build and obtain an agency crosswalk; do not guess coordinates |
|
||||||
|
| Kiosk bays do not share one GTFS parent station | Require explicit review; the current prototype stops rather than selecting an arbitrary anchor |
|
||||||
|
| CARTO background is missing | Check basemap key, connectivity, WebGL, and attribution; choose Esri or plain background |
|
||||||
|
| Feed has expired | Download the official current feed for a live rebuild; use the captured service date only to replay this sample |
|
||||||
|
| Source template has RTC-specific dates and copy | Generalize these before using another agency; current data scripts expose a service-date argument but the UI is still a pilot template |
|
||||||
|
| Rapid basemap switching logs an abort | A removed vector map can cancel an outstanding request; verify the selected basemap finishes loading |
|
||||||
|
| Phone navigation | Links and destinations were verified in the browser; physical iPhone/Android launch was not tested |
|
||||||
|
| Live device health / detours / arrivals | Not implemented; captured health and static scheduled service only |
|
||||||
|
|
||||||
|
## Requirements and licenses
|
||||||
|
|
||||||
|
Python 3 standard library; Leaflet 1.9.4; MapLibre GL JS 5.6.2; MapLibre–Leaflet integration 0.1.3; optional CARTO basemap access; authorized existing OnSign MCP and native API access for acquisition. Bundled upstream licenses are in `source/vendor/`. No redistribution license has been selected for MPM-authored source; creating a public repository does not by itself grant an open-source license.
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Read-only Vontas GetStop adapter. Never logs authenticated request URLs."""
|
||||||
|
import argparse,datetime,json,os,pathlib,re,time,urllib.parse,urllib.request
|
||||||
|
p=argparse.ArgumentParser();p.add_argument('--players',type=pathlib.Path,required=True);p.add_argument('--output',type=pathlib.Path,required=True);a=p.parse_args()
|
||||||
|
base=os.environ.get('RTC_NATIVE_API_BASE','').rstrip('/');key=os.environ.get('RTC_NATIVE_API_KEY','')
|
||||||
|
if not base or not key:raise SystemExit('Set RTC_NATIVE_API_BASE (ASMX service URL) and RTC_NATIVE_API_KEY securely.')
|
||||||
|
url=urllib.parse.urlsplit(base)
|
||||||
|
if url.scheme!='https' or url.query or url.fragment:raise SystemExit('API base must be HTTPS with no credential query or fragment.')
|
||||||
|
raw=json.loads(a.players.read_text());players=raw['rows'] if isinstance(raw,dict) else raw;ids=set()
|
||||||
|
for player in players:
|
||||||
|
m=re.search(r'-\s*S(\d+(?:\s*,\s*\d+)*)\s*$',player.get('player',''),re.I)
|
||||||
|
if m:ids.update(re.findall(r'\d+',m[1]))
|
||||||
|
results={};errors={}
|
||||||
|
for sid in sorted(ids,key=int):
|
||||||
|
try:
|
||||||
|
request=base+'/GetStop?'+urllib.parse.urlencode({'apikey':key,'stopid':sid})
|
||||||
|
with urllib.request.urlopen(request,timeout=25) as response:stop=json.load(response)
|
||||||
|
if not isinstance(stop,dict) or str(stop.get('StopID'))!=sid or stop.get('SrcStopID') is None:errors[sid]='Invalid stop response'
|
||||||
|
else:results[sid]=stop
|
||||||
|
except Exception as exc:errors[sid]=type(exc).__name__
|
||||||
|
time.sleep(.1)
|
||||||
|
report={'endpoint':base+'/GetStop','retrieved_at_utc':datetime.datetime.now(datetime.timezone.utc).isoformat(),'stops':results,'errors':errors}
|
||||||
|
a.output.parent.mkdir(parents=True,exist_ok=True);a.output.write_text(json.dumps(report,indent=2));print('Read',len(results),'stops;',len(errors),'errors. Credentials excluded.')
|
||||||
|
if errors:raise SystemExit(2)
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Build normalized map data from read-only OnSign export, native stops, and a dated GTFS ZIP."""
|
||||||
|
import argparse,collections,csv,datetime,difflib,hashlib,io,json,math,pathlib,re,zipfile
|
||||||
|
p=argparse.ArgumentParser();p.add_argument('--players',type=pathlib.Path,required=True);p.add_argument('--native',type=pathlib.Path,required=True);p.add_argument('--gtfs',type=pathlib.Path,required=True);p.add_argument('--service-date',type=datetime.date.fromisoformat,required=True);p.add_argument('--output-dir',type=pathlib.Path,required=True);a=p.parse_args()
|
||||||
|
z=zipfile.ZipFile(a.gtfs)
|
||||||
|
def rows(name):return list(csv.DictReader(io.TextIOWrapper(z.open(name+'.txt'),encoding='utf-8-sig')))
|
||||||
|
stops={r['stop_id']:r for r in rows('stops')};trips={r['trip_id']:r for r in rows('trips')};routes=rows('routes');feed=rows('feed_info')[0];ds=a.service_date.strftime('%Y%m%d');day=a.service_date.strftime('%A').lower()
|
||||||
|
if not feed.get('feed_start_date','00000000')<=ds<=feed.get('feed_end_date','99999999'):raise SystemExit('Requested date is outside feed validity. Download the current official feed before a live rebuild.')
|
||||||
|
active={r['service_id'] for r in rows('calendar') if r['start_date']<=ds<=r['end_date'] and r[day]=='1'}
|
||||||
|
for c in rows('calendar_dates'):
|
||||||
|
if c['date']==ds:
|
||||||
|
if c['exception_type']=='1':active.add(c['service_id'])
|
||||||
|
else:active.discard(c['service_id'])
|
||||||
|
if not active:raise SystemExit('No service active on requested date.')
|
||||||
|
sr=collections.defaultdict(set);rs=collections.defaultdict(set);shapes=collections.defaultdict(list)
|
||||||
|
for st in rows('stop_times'):
|
||||||
|
t=trips[st['trip_id']]
|
||||||
|
if t['service_id'] in active:sr[st['stop_id']].add(t['route_id'])
|
||||||
|
for t in trips.values():
|
||||||
|
if t['service_id'] in active and t['shape_id']:rs[t['route_id']].add(t['shape_id'])
|
||||||
|
for sh in rows('shapes'):shapes[sh['shape_id']].append((int(sh['shape_pt_sequence']),float(sh['shape_pt_lat']),float(sh['shape_pt_lon'])))
|
||||||
|
payload=json.loads(a.players.read_text());native=json.loads(a.native.read_text());players=payload['rows'];audit=[];used=set()
|
||||||
|
def distance(coords,s):
|
||||||
|
lat,lon=coords.get('latitude'),coords.get('longitude')
|
||||||
|
if lat is None or lon is None:return None
|
||||||
|
x,y=float(s['stop_lat']),float(s['stop_lon']);rad=math.pi/180;h=math.sin((lat-x)*rad/2)**2+math.cos(lat*rad)*math.cos(x*rad)*math.sin((lon-y)*rad/2)**2
|
||||||
|
return round(6371000*2*math.asin(min(1,math.sqrt(h))),2)
|
||||||
|
for player in players:
|
||||||
|
m=re.search(r'-\s*S(\d+(?:\s*,\s*\d+)*)\s*$',player['player'],re.I);ids=re.findall(r'\d+',m[1]) if m else []
|
||||||
|
player.update(source_ids=ids,kind='kiosk' if 'kiosk' in player['player'].lower() else 'sign',excluded=not bool(m),exclude_reason='Detached test player' if player.get('detached') else ('Showroom or unassigned player; no field stop ID' if not m else ''),crosswalk=[])
|
||||||
|
for sid in ids:
|
||||||
|
n=native['stops'].get(sid);target=str(n.get('SrcStopID')) if n else ''
|
||||||
|
if not n or str(n.get('StopID'))!=sid or target not in stops:raise SystemExit('Unresolved native stop '+sid+'; do not guess an ID, coordinate, or route.')
|
||||||
|
s=stops[target];geo=n.get('GeoLoc') or {};delta=distance(geo,s);norm=lambda v:re.sub('[^a-z0-9]','',v.lower());ratio=difflib.SequenceMatcher(None,norm(n.get('Name','')),norm(s['stop_name'])).ratio()
|
||||||
|
c={'native_stop_id':sid,'gtfs_stop_id':target,'native_name':n.get('Name'),'gtfs_name':s['stop_name'],'native_coordinates':[geo.get('latitude'),geo.get('longitude')],'gtfs_coordinates':[float(s['stop_lat']),float(s['stop_lon'])],'coordinate_delta_m':delta,'coordinate_review':delta is not None and delta>25,'name_review':ratio<.85,'routes':sorted(sr[target]),'basis':'Native GetStop SrcStopID → GTFS stop_id'};player['crosswalk'].append(c)
|
||||||
|
player['resolved_stop_ids']=[c['gtfs_stop_id'] for c in player['crosswalk']]
|
||||||
|
player['candidate_ids']=player['resolved_stop_ids'][:];player['match']='excluded' if player['excluded'] else 'API verified'
|
||||||
|
if player['kind']=='kiosk':
|
||||||
|
parents={stops[t]['parent_station'] for t in player['resolved_stop_ids']}
|
||||||
|
if len(parents)!=1 or not next(iter(parents)) or next(iter(parents)) not in stops:raise SystemExit('Kiosk bays do not resolve to exactly one GTFS parent station; manual review required.')
|
||||||
|
player['candidate_ids']=sorted(parents);player['match']='API verified · station reference'
|
||||||
|
player['routes']=sorted(set().union(*(sr[t] for t in player['resolved_stop_ids']))) if ids else [];player['coordinate_review']=any(c['coordinate_review'] for c in player['crosswalk']);used.update(player['candidate_ids']+player['resolved_stop_ids'])
|
||||||
|
for c in player['crosswalk']:
|
||||||
|
audit.append({'player_id':player['player_id'],'player_name':player['player'],'status':player['status'],'native_id':c['native_stop_id'],'id_result':'resolved via native API','gtfs_stop_id':c['gtfs_stop_id'],'gtfs_name':c['gtfs_name'],'native_name':c['native_name'],'mapping_basis':c['basis'],'gtfs_lat':c['gtfs_coordinates'][0],'gtfs_lon':c['gtfs_coordinates'][1],'coordinate_delta_m':c['coordinate_delta_m'],'coordinate_review':c['coordinate_review'],'name_review':c['name_review'],'station_reference':player['candidate_ids'][0] if player['kind']=='kiosk' else ''})
|
||||||
|
route_data=[{'id':r['route_id'],'short':r['route_short_name'],'name':r['route_long_name'],'color':'#'+r['route_color'],'shape_ids':sorted(rs[r['route_id']]),'lines':[[[lat,lon] for _,lat,lon in sorted(shapes[sh])] for sh in sorted(rs[r['route_id']])]} for r in routes]
|
||||||
|
meta={'account':players[0].get('account_id') if players else None,'as_of':payload.get('as_of'),'cache_age_s':payload.get('cache_age_s'),'service_date':a.service_date.isoformat(),'feed_info':feed,'feed_url':'https://data.trilliumtransit.com/gtfs/rtc-nv-us/rtc-nv-us.zip','official_page':'https://rtcwashoe.com/resources-and-reports/','sha256':hashlib.sha256(a.gtfs.read_bytes()).hexdigest(),'active_services':sorted(active),'native_retrieved_at_utc':native['retrieved_at_utc'],'resolved_native_ids':len(audit),'coordinate_reviews':sum(c['coordinate_review'] for c in audit),'unmatched':0,'ambiguous_signs':0,'coordinate_authority':'GTFS stops.txt only; OnSign location unused'}
|
||||||
|
d={'meta':meta,'players':players,'stops':{sid:{'id':sid,'name':stops[sid]['stop_name'],'lat':float(stops[sid]['stop_lat']),'lon':float(stops[sid]['stop_lon']),'routes':sorted(sr[sid]),'headings':[]} for sid in used},'routes':route_data,'audit':audit}
|
||||||
|
a.output_dir.mkdir(parents=True,exist_ok=True);(a.output_dir/'rtc-map-data.json').write_text(json.dumps(d,separators=(',',':')))
|
||||||
|
with (a.output_dir/'resolved-stop-crosswalk.csv').open('w') as f:
|
||||||
|
w=csv.DictWriter(f,fieldnames=list(audit[0]));w.writeheader();w.writerows(audit)
|
||||||
|
print('Prepared',len(players),'players,',len(audit),'native stop mappings,',len(route_data),'GTFS routes')
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Render the captured map from validated normalized data. No credentials are bundled by default."""
|
||||||
|
import argparse,json,os,pathlib
|
||||||
|
ROOT=pathlib.Path(__file__).resolve().parents[1]
|
||||||
|
p=argparse.ArgumentParser();p.add_argument('--data',type=pathlib.Path,required=True);p.add_argument('--output',type=pathlib.Path,required=True);p.add_argument('--include-carto-key',action='store_true');a=p.parse_args()
|
||||||
|
s=(ROOT/'templates/map.html').read_text();key=os.environ.get('RTC_CARTO_BASEMAP_KEY','') if a.include_carto_key else ''
|
||||||
|
# Retain the prototype's RTC-specific UI; this renderer does not claim multi-agency support.
|
||||||
|
s=s.replace('const CARTO_BASEMAP_KEY=','let CARTO_BASEMAP_KEY=')
|
||||||
|
s=s.replace("baseMode=mode;$('basemap').value=mode;","if(mode.startsWith('positron')&&!CARTO_BASEMAP_KEY){const k=window.prompt('Enter your CARTO basemap key (kept in this tab only):');if(!k){$('basemap').value=baseMode;return;}CARTO_BASEMAP_KEY=k.trim();}baseMode=mode;$('basemap').value=mode;")
|
||||||
|
if not key:
|
||||||
|
s=s.replace("let savedBase='positron';","let savedBase='esri';").replace("||savedBase}catch{}setBasemap(","||savedBase}catch{}if(savedBase.startsWith('positron')&&!CARTO_BASEMAP_KEY)savedBase='esri';setBasemap(")
|
||||||
|
files={'/*LEAFLET_CSS*/':'leaflet.css','/*MAPLIBRE_CSS*/':'maplibre-gl.css','/*LEAFLET_JS*/':'leaflet.js','/*MAPLIBRE_JS*/':'maplibre-gl.js','/*MAPLIBRE_LEAFLET_JS*/':'leaflet-maplibre-gl.js'}
|
||||||
|
for token,name in files.items():s=s.replace(token,(ROOT/'vendor'/name).read_text().replace('</script>','<\\/script>'))
|
||||||
|
s=s.replace('/*DATA*/',json.dumps(json.loads(a.data.read_text()),separators=(',',':')).replace('</script>','<\\/script>')).replace('/*CARTO_KEY*/',json.dumps(key))
|
||||||
|
a.output.parent.mkdir(parents=True,exist_ok=True);a.output.write_text(s);print('Rendered',a.output,'with embedded CARTO key' if key else 'without embedded credentials')
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Validate map coordinate, ID, service and shape integrity against the captured source inputs."""
|
||||||
|
import argparse,collections,csv,io,json,pathlib,zipfile
|
||||||
|
p=argparse.ArgumentParser();p.add_argument('--data',type=pathlib.Path,required=True);p.add_argument('--native',type=pathlib.Path,required=True);p.add_argument('--gtfs',type=pathlib.Path,required=True);a=p.parse_args();d=json.loads(a.data.read_text());n=json.loads(a.native.read_text());z=zipfile.ZipFile(a.gtfs)
|
||||||
|
def rows(name):return list(csv.DictReader(io.TextIOWrapper(z.open(name+'.txt'),encoding='utf-8-sig')))
|
||||||
|
stops={r['stop_id']:r for r in rows('stops')};trips={r['trip_id']:r for r in rows('trips')};routes={r['route_id']:r for r in rows('routes')};active=set(d['meta']['active_services']);sr=collections.defaultdict(set);sh=collections.defaultdict(list);rs=collections.defaultdict(set)
|
||||||
|
for r in rows('stop_times'):
|
||||||
|
if trips[r['trip_id']]['service_id'] in active:sr[r['stop_id']].add(trips[r['trip_id']]['route_id'])
|
||||||
|
for t in trips.values():
|
||||||
|
if t['service_id'] in active and t['shape_id']:rs[t['route_id']].add(t['shape_id'])
|
||||||
|
for r in rows('shapes'):sh[r['shape_id']].append((int(r['shape_pt_sequence']),float(r['shape_pt_lat']),float(r['shape_pt_lon'])))
|
||||||
|
for player in d['players']:
|
||||||
|
assert player['resolved_stop_ids']==[str(n['stops'][sid]['SrcStopID']) for sid in player['source_ids']]
|
||||||
|
assert set(player['routes'])==(set().union(*(sr[s] for s in player['resolved_stop_ids'])) if player['source_ids'] else set())
|
||||||
|
if player['kind']=='kiosk':assert all(stops[s]['parent_station']==player['candidate_ids'][0] for s in player['resolved_stop_ids'])
|
||||||
|
for sid,s in d['stops'].items():assert [s['lat'],s['lon']]==[float(stops[sid]['stop_lat']),float(stops[sid]['stop_lon'])]
|
||||||
|
for r in d['routes']:
|
||||||
|
assert r['color']=='#'+routes[r['id']]['route_color'] and set(r['shape_ids'])==rs[r['id']]
|
||||||
|
for sid,line in zip(r['shape_ids'],r['lines']):assert line==[[lat,lon] for _,lat,lon in sorted(sh[sid])]
|
||||||
|
assert len(d['audit'])==sum(len(p['source_ids']) for p in d['players'])
|
||||||
|
print('PASS: native ID joins, GTFS coordinates, mapped-bay routes, active-service geometry and route colors')
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Deployed Sign Map</title><style>/*LEAFLET_CSS*/</style><style>/*MAPLIBRE_CSS*/</style><style>
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:#f3f5f7;color:#162a38;font:14px/1.45 -apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif}button,input,select{font:inherit}button,a{cursor:pointer}button{border:1px solid #ced8df;background:white;border-radius:7px;padding:7px 10px;color:#19384b}button:hover{background:#e7f0f5}button:focus-visible,input:focus-visible,select:focus-visible{outline:3px solid #0673b5}header{padding:14px 22px;background:#122f40;color:white;display:flex;justify-content:space-between;align-items:center;gap:15px}h1{margin:0;font-size:22px;font-weight:650}header p{margin:3px 0 0;color:#c7d8e2;font-size:12px}.topstats{display:flex;gap:18px;white-space:nowrap}.topstats b{font-size:22px;display:block}.topstats span{color:#c7d8e2;font-size:12px}.notice{padding:10px 22px;background:#fff2cf;color:#624a0d;border-bottom:1px solid #e7d4a1;font-size:13px}.shell{display:grid;grid-template-columns:350px minmax(0,1fr);height:calc(100vh - 145px);min-height:550px}aside{background:#fff;overflow:auto;border-right:1px solid #d3dce2}.controls{padding:13px;position:sticky;top:0;background:white;z-index:3;border-bottom:1px solid #dae1e6}.controls input{width:100%;padding:8px;border:1px solid #c9d5de;border-radius:6px;margin-bottom:8px}.controls select{padding:6px;border:1px solid #c9d5de;border-radius:5px;width:100%}.listcount{font-size:12px;color:#566e7e;margin-top:8px}.player{display:flex;gap:10px;width:100%;text-align:left;border:0;border-radius:0;border-bottom:1px solid #e5ebef;padding:12px 14px;background:white}.player.selected{background:#e5f1f8;border-left:4px solid #137daf;padding-left:10px}.player strong{font-size:13px;display:block;font-weight:600}.player small{display:block;color:#596b76;font-size:12px;margin-top:3px}.statusicon{display:inline-flex;flex:0 0 auto;width:26px;height:26px;align-items:center;justify-content:center;border:2px solid white;box-shadow:0 1px 4px #0006;background:#17815a;color:white;border-radius:50%;font-size:16px;font-weight:bold}.statusicon.offline{background:#b7303f}.statusicon.kiosk{border-radius:5px;background:#226baf;position:relative}.statusicon.kiosk::after{content:"";position:absolute;right:-3px;bottom:-3px;width:9px;height:9px;background:#17815a;border:2px solid white;border-radius:50%}.statusicon.kiosk.offline::after{background:#b7303f}.statusicon.excluded{background:#6c7781;border-radius:5px}.statusicon.ambiguous{background:white;color:#986609;border:3px dashed #b37a12}.statusicon.ambiguous.offline{color:#b7303f;border-color:#b7303f}.markerwrap{background:none;border:none}.mapcol{position:relative;min-width:0;display:flex;flex-direction:column}#map{flex:1;min-height:350px;background:#e5ebef}.mapbar{display:flex;gap:7px;padding:9px 12px;background:#fff;border-bottom:1px solid #dae1e6;flex-wrap:wrap;align-items:center}.legend{font-size:12px;color:#3f5868;margin-left:auto}.legend i{display:inline-block;width:9px;height:9px;border-radius:50%;background:#17815a;margin-left:10px}.legend .red{background:#b7303f}.legend .blue{background:#226baf;border-radius:1px}.legend .amber{background:white;border:2px dashed #b37a12;width:12px;height:12px}.routepanel{padding:10px 12px;background:#fff;max-height:150px;overflow:auto;border-top:1px solid #dae1e6}.routepanel summary{cursor:pointer;font-weight:600}.routegrid{display:flex;flex-wrap:wrap;gap:5px;margin-top:8px}.routechip{display:flex;align-items:center;gap:5px;font-size:12px;padding:3px 6px;border:1px solid #dce3e7;border-radius:5px}.routechip input{margin:0}.swatch{height:4px;width:20px;display:inline-block}.detail{position:absolute;right:14px;top:66px;width:370px;max-width:calc(100% - 28px);max-height:calc(100% - 135px);overflow:auto;background:white;box-shadow:0 8px 30px #1236;z-index:600;border-radius:10px;padding:18px}.detail h2{font-size:17px;margin:0 28px 10px 0}.detail h3{font-size:14px;margin:17px 0 5px}.detail p{margin:8px 0;font-size:13px}.detail dl{display:grid;grid-template-columns:105px 1fr;gap:6px;font-size:12px}.detail dt{color:#5b707e}.detail dd{margin:0;overflow-wrap:anywhere}.close{position:absolute;right:8px;top:8px;border:0;padding:3px 8px;font-size:21px}.badge{padding:3px 6px;background:#fff1c8;border-radius:4px;display:inline-block;font-size:12px}.stopcard{border-top:1px solid #e0e6eb;padding-top:8px;margin-top:8px;font-size:13px}.routebadge{display:inline-block;border-bottom:3px solid var(--route);margin:2px 5px 2px 0;padding:2px;color:#263f50;font-size:12px}dialog{border:0;border-radius:10px;width:1000px;max-width:94vw;max-height:90vh;padding:24px;color:#163043}dialog::backdrop{background:#102c40aa}dialog h2{margin:0 30px 12px 0}table{border-collapse:collapse;font-size:12px;width:100%}th,td{border-bottom:1px solid #dce3e7;text-align:left;padding:8px;vertical-align:top}th{background:#edf2f5;position:sticky;top:0}.scrolltable{max-height:52vh;overflow:auto}.muted{color:#617583;font-size:12px}.leaflet-tooltip{font:12px/1.4 -apple-system,sans-serif;max-width:300px;white-space:normal}.leaflet-controlLine truncated
|
||||||
|
.navigation{display:flex;flex-direction:column;gap:7px;margin:10px 0}.navigation a{display:block;padding:10px 12px;border:1px solid #b8cedb;border-radius:6px;text-decoration:none;color:#075c8e;background:#f1f8fc;font-size:13px;min-height:42px}.navigation a:hover{background:#dfedf5}.basemaplabel{display:flex;align-items:center;gap:7px;font-size:12px;font-weight:600}.basemaplabel select{padding:7px;border:1px solid #b8ccd9;border-radius:6px;background:white;color:#163c51;font-size:13px}.leaflet-gl-layer{pointer-events:none}.maplibregl-control-container{display:none}</style></head><body>
|
||||||
|
<header><div><h1>Deployed Sign Map</h1><p>Captured transit-sign deployment snapshot</p></div><div class="topstats"><div><b id="stat-field">—</b><span>field players</span></div><div><b id="stat-health">—</b><span>online / offline in field</span></div><div><b id="stat-excluded">—</b><span>excluded</span></div></div></header>
|
||||||
|
<div class="notice"><strong>Location evidence:</strong> Native stop IDs resolved through CAD/AVL → GTFS. Marker and navigation coordinates come only from GTFS. Two kiosks use station reference points; their bay IDs are resolved.</div>
|
||||||
|
<div class="shell"><aside><div class="controls"><input id="search" aria-label="Search players, stop IDs or GTFS names" placeholder="Search players, stop IDs or names…"><select id="filter" aria-label="Filter players"><option value="field">All field players</option><option value="offline">Offline field players</option><option value="kiosk">Kiosks</option><option value="review">Coordinate differences to review</option><option value="excluded">Excluded test / showroom</option><option value="all">All account players</option></select><div id="listcount" class="listcount"></div></div><div id="players"></div></aside>
|
||||||
|
<main class="mapcol"><div class="mapbar"><label class="basemaplabel">Basemap <select id="basemap" aria-label="Basemap"><option value="positron">Positron · street labels</option><option value="positron-nolabels">Positron · no labels</option><option value="esri">Esri Gray Canvas</option><option value="osm">Original street map</option><option value="plain">Plain background</option></select></label><button id="fit">Fit field locations</button><button id="clear">Clear selection</button><button id="report">ID report</button><button id="sources">Sources & dates</button><div class="legend"><i></i> online <i class="red"></i> offline <i class="blue"></i> kiosk ▧ </div></div><div id="map" role="region" aria-label="Interactive map of RTC Washoe deployed sign reference locations"></div><div id="basehint">Street tiles need internet. Embedded routes and markers work offline.</div><section id="detail" class="detail hidden" aria-label="Selected player details"></section><details class="routepanel"><summary>Route lines · GTFS colors · service October 28, 2026</summary><div><button id="routesall">All routes</button> <button id="routesnone">Hide routes</button> <span class="muted">Select a player to emphasize routes serving its resolved stop(s). Kiosk routes come from the mapped bays only.</span></div><div id="routegrid" class="routegrid"></div></details></main></div>
|
||||||
|
<dialog id="reportdialog"><button class="close" aria-label="Close report">×</button><h2>Resolved native stop-ID crosswalk</h2><p><strong>Native stop IDs resolved by explicit source IDs.</strong> Each native GetStop response supplies SrcStopID, which joins to current GTFS stop_id. Names and coordinate differences are included for review. Native IDs must not be assumed to share the GTFS identifier namespace.</p><p>Marker and navigation coordinates always use GTFS stops.txt. Native API coordinates are retained only for comparison; OnSign locations are unused. Native stops that differ from GTFS by more than 25 metres are flagged for coordinate review. This does not invalidate their explicit ID mapping. Kiosk device positions remain unknown; station reference points and mapped bays are clearly labeled.</p><button id="csvdownload">Download full CSV crosswalk</button><div class="scrolltable"><table><thead><tr><th>Player / native ID</th><th>Verified GTFS stop</th><th>Coordinate evidence</th></tr></thead><tbody id="auditbody"></tbody></table></div></dialog>
|
||||||
|
<dialog id="sourcesdialog"><button class="close" aria-label="Close sources">×</button><h2>Sources, dates & limits</h2><div id="sourcebody"></div></dialog>
|
||||||
|
<script>/*LEAFLET_JS*/</script><script>/*MAPLIBRE_JS*/</script><script>/*MAPLIBRE_LEAFLET_JS*/</script><script>const DATA=/*DATA*/;
|
||||||
|
const $=id=>document.getElementById(id),esc=x=>String(x??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
||||||
|
const map=L.map('map',{preferCanvas:true,minZoom:1,maxZoom:20}).setView([39.519,-119.79],12), routeLayers={}, markerItems=[];let selected=null;const enabled=new Set(DATA.routes.map(r=>r.id));
|
||||||
|
const CARTO_BASEMAP_KEY=/*CARTO_KEY*/;
|
||||||
|
const cartoAttribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors · © <a href="https://carto.com/attribution/">CARTO</a>';
|
||||||
|
map.createPane('vectorBasemap');map.getPane('vectorBasemap').style.zIndex=200;map.getPane('vectorBasemap').style.pointerEvents='none';
|
||||||
|
let activeBasemap=null,baseMode='positron',vectorMap=null;
|
||||||
|
function setBasemap(mode){
|
||||||
|
baseMode=mode;$('basemap').value=mode;
|
||||||
|
if(activeBasemap){map.removeLayer(activeBasemap);activeBasemap=null;}vectorMap=null;
|
||||||
|
$('basehint').textContent=mode==='plain'?'Plain background · embedded routes and stops':mode.startsWith('positron')?'CARTO vector · loading basemap…':'Raster basemap · loading…';
|
||||||
|
if(mode==='plain'){try{localStorage.setItem('rtc-basemap',mode)}catch{}return;}
|
||||||
|
if(mode.startsWith('positron')){
|
||||||
|
try{
|
||||||
|
const layer=L.maplibreGL({style:'https://basemaps.cartocdn.com/gl/'+mode+'-gl-style/style.json?key='+encodeURIComponent(CARTO_BASEMAP_KEY),pane:'vectorBasemap',interactive:false,attribution:cartoAttribution,attributionControl:{customAttribution:cartoAttribution}});
|
||||||
|
activeBasemap=layer;layer.addTo(map);const gl=layer.getMaplibreMap();vectorMap=gl;
|
||||||
|
gl.on('style.load',()=>{if(activeBasemap!==layer)return;gl.getStyle().layers.forEach(l=>{if(l.type==='symbol'&&(mode.endsWith('nolabels')||/poi|housenumber/i.test(l.id)||/poi/i.test(l['source-layer']||'')))gl.setLayoutProperty(l.id,'visibility','none');});});
|
||||||
|
gl.on('idle',()=>{if(activeBasemap===layer)$('basehint').textContent=mode==='positron'?'CARTO vector · street labels · POIs hidden':'CARTO vector · no labels';});
|
||||||
|
gl.on('error',()=>{if(activeBasemap===layer)$('basehint').textContent='Vector basemap could not fully load. Routes and stops remain usable; choose another basemap if needed.';});
|
||||||
|
}catch{ $('basehint').textContent='Vector rendering unavailable. Choose Esri, original streets, or plain background.';}
|
||||||
|
}else{
|
||||||
|
const esri=mode==='esri',layer=L.tileLayer(esri?'https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{z}/{y}/{x}':'https://tile.openstreetmap.org/{z}/{x}/{y}.png',{maxZoom:20,maxNativeZoom:esri?16:19,attribution:esri?'Tiles © Esri · Esri, DeLorme, NAVTEQ':'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'});
|
||||||
|
activeBasemap=layer;layer.on('load',()=>{if(activeBasemap===layer)$('basehint').textContent=esri?'Esri Gray Canvas · raster tiles':'Original OpenStreetMap · raster tiles';});layer.on('tileerror',()=>{if(activeBasemap===layer)$('basehint').textContent='Basemap tiles unavailable. Embedded routes and stops remain usable.';});layer.addTo(map);
|
||||||
|
}
|
||||||
|
try{localStorage.setItem('rtc-basemap',mode)}catch{}
|
||||||
|
}
|
||||||
|
$('basemap').onchange=()=>setBasemap($('basemap').value);
|
||||||
|
let savedBase='positron';try{savedBase=localStorage.getItem('rtc-basemap')||savedBase}catch{}setBasemap(['positron','positron-nolabels','esri','osm','plain'].includes(savedBase)?savedBase:'positron');
|
||||||
|
L.control.scale({imperial:true,metric:true}).addTo(map);const markerGroup=L.layerGroup().addTo(map);
|
||||||
|
DATA.routes.forEach(r=>{routeLayers[r.id]=L.polyline(r.lines,{color:r.color,weight:3,opacity:.65}).addTo(map).bindTooltip(esc(r.short+' · '+r.name));routeLayers[r.id].on('click',()=>{if(r.lines.length)map.fitBounds(routeLayers[r.id].getBounds(),{padding:[30,30]});});});
|
||||||
|
function icon(p){let symbol=p.excluded?'×':p.kind==='kiosk'?'▧':p.status==='offline'?'!':'✓';return `<span class="statusicon ${p.status} ${p.kind} ${p.excluded?'excluded':''} ${p.match==='ambiguous'?'ambiguous':''}">${symbol}</span>`;}
|
||||||
|
DATA.players.filter(p=>!p.excluded).forEach(p=>p.candidate_ids.forEach(sid=>{const s=DATA.stops[sid],m=L.marker([s.lat,s.lon],{icon:L.divIcon({html:icon(p),className:'markerwrap',iconSize:[28,28],iconAnchor:[14,14]}),title:p.player+' · '+p.status+' · '+p.match,keyboard:true}).bindTooltip(esc(p.player.replace(/ - S[\d,]+$/,''))+`<br>${esc(p.status)} · ${esc(p.match)}<br>GTFS ${sid} · ${esc(s.name)}`).on('click',()=>selectPlayer(p.player_id));markerItems.push({p,sid,m});}));
|
||||||
|
function matches(p){const f=$('filter').value,q=$('search').value.toLowerCase();let yes=f==='all'||f==='field'&&!p.excluded||f==='offline'&&!p.excluded&&p.status==='offline'||f==='kiosk'&&p.kind==='kiosk'||f==='review'&&p.coordinate_review||f==='excluded'&&p.excluded;return yes&&(!q||[p.player,p.player_id,...p.candidate_ids.map(s=>s+' '+DATA.stops[s].name)].join(' ').toLowerCase().includes(q));}
|
||||||
|
function render(){const ps=DATA.players.filter(matches);$('listcount').textContent=`${ps.length} players · click a player for evidence and details`;$('players').innerHTML=ps.map(p=>`<button class="player ${selected===p.player_id?'selected':''}" data-player="${p.player_id}">${icon(p)}<span><strong>${esc(p.player.replace(/ - S[\d,]+$/,''))}</strong><small>${esc(p.source_ids.length?'S'+p.source_ids.join(', '):p.exclude_reason)}</small><small>${esc(p.status)} · ${esc(p.match)}${p.coordinate_review?' · coordinate review':''}</small></span></button>`).join('');$('players').querySelectorAll('[data-player]').forEach(b=>b.onclick=()=>selectPlayer(b.dataset.player));markerGroup.clearLayers();markerItems.filter(x=>matches(x.p)).forEach(x=>x.m.addTo(markerGroup));}
|
||||||
|
function badges(ids){return ids.map(id=>{const r=DATA.routes.find(r=>r.id===id);return `<span class="routebadge" style="--route:${r.color}">${esc(r.short)}</span>`}).join('')||'<span class="muted">No trips on selected service date</span>';}
|
||||||
|
function styleRoutes(){const p=DATA.players.find(x=>x.player_id===selected);DATA.routes.forEach(r=>{const l=routeLayers[r.id];if(enabled.has(r.id)){if(!map.hasLayer(l))l.addTo(map);const emphasized=!p||p.routes.includes(r.id);l.setStyle({color:r.color,weight:emphasized?(p?5:3):2,opacity:emphasized?.83:.13});if(emphasized)l.bringToFront();}else map.removeLayer(l);});}
|
||||||
|
function navigation(s){const coords=s.lat+','+s.lon;return `<div class="navigation"><a target="_blank" rel="noopener" href="https://maps.apple.com/?daddr=${encodeURIComponent(coords)}&dirflg=d">Navigate to this stop via Apple Maps ↗</a><a target="_blank" rel="noopener" href="https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent(coords)}&travelmode=driving">Navigate to this stop via Google Maps ↗</a></div>`;}
|
||||||
|
function stopCard(sid,c){const s=DATA.stops[sid];return `<div class="stopcard"><strong>${c?'Native S'+esc(c.native_stop_id)+' → ':''}GTFS ${sid} · ${esc(s.name)}</strong><p>GTFS ${s.lat}, ${s.lon}${c?' · native API difference '+c.coordinate_delta_m+' m':''}${c&&c.coordinate_review?' <span class="badge">Coordinate review</span>':''}</p>${badges(s.routes)}${navigation(s)}</div>`;}
|
||||||
|
function selectPlayer(id){selected=id;const p=DATA.players.find(x=>x.player_id===id);$('detail').classList.remove('hidden');$('detail').innerHTML=`<button class="close" aria-label="Close player details">×</button><h2>${esc(p.player.replace(/ - S[\d,]+$/,''))}</h2><span class="badge">${esc(p.match)}</span><dl><dt>Player ID</dt><dd>${esc(p.player_id)}</dd><dt>Status</dt><dd>${esc(p.status)}${p.detached?' · detached':''}</dd><dt>Last seen</dt><dd>${esc(p.last_seen)}</dd><dt>App / model</dt><dd>${esc(p.app_version)} · ${esc(p.model)}</dd><dt>Errors / warnings</dt><dd>${p.errors_24h} / ${p.warnings_24h} in 24h</dd><dt>Native stop IDs</dt><dd>${esc(p.source_ids.join(', ')||'None')}</dd></dl><h3>Location & route evidence</h3><p>${p.excluded?esc(p.exclude_reason)+'. No coordinates assigned.':p.kind==='kiosk'?'All listed native bay IDs are resolved through CAD/AVL SrcStopID to GTFS. The kiosk marker uses the GTFS parent station reference; its exact physical position is unknown. Choose a mapped bay below for stop navigation.':'Native GetStop SrcStopID explicitly identifies this GTFS stop. The marker and navigation links use GTFS coordinates only. OnSign location is unused. This is a bus-stop reference, not a surveyed device position.'}</p>${p.kind==='kiosk'?'<h3>Station reference</h3>'+p.candidate_ids.map(sid=>{const st=DATA.stops[sid];return `<div class="stopcard"><strong>GTFS ${sid} · ${esc(st.name)}</strong><p>Station reference only; not a kiosk GPS location.</p>${navigation(st)}</div>`;}).join('')+'<h3>Resolved bays</h3>':''}${p.crosswalk.map(c=>stopCard(c.gtfs_stop_id,c)).join('')}<h3>${p.kind==='kiosk'?'Routes serving mapped bays':'Routes serving this stop'}</h3>${badges(p.routes)}<p class="muted">Routes joined from verified stop IDs → stop_times → active trips → routes for October 28, 2026. Navigation destinations use GTFS coordinates. Scheduled associations do not establish displayed content or current stop availability.</p>`;$('detail').querySelector('.close').onclick=clearSelection;if(p.candidate_ids.length)map.fitBounds(p.candidate_ids.map(s=>[DATA.stops[s].lat,DATA.stops[s].lon]),{paddingTopLeft:[30,35],paddingBottomRight:[Math.min(400,map.getSize().x*.4),30],maxZoom:16});styleRoutes();render();}
|
||||||
|
function clearSelection(){selected=null;$('detail').classList.add('hidden');styleRoutes();render();}
|
||||||
|
function fit(){const coords=markerItems.filter(x=>matches(x.p)).map(x=>[DATA.stops[x.sid].lat,DATA.stops[x.sid].lon]);if(coords.length)map.fitBounds(coords,{padding:[36,36],maxZoom:15});}
|
||||||
|
$('search').oninput=render;$('filter').onchange=()=>{render();fit()};$('fit').onclick=fit;$('clear').onclick=clearSelection;
|
||||||
|
$('routegrid').innerHTML=DATA.routes.map(r=>`<label class="routechip" title="${esc(r.name)}"><input type="checkbox" checked value="${r.id}"><span class="swatch" style="background:${r.color}"></span>${esc(r.short)} · ${esc(r.name)}</label>`).join('');$('routegrid').querySelectorAll('input').forEach(c=>c.onchange=()=>{c.checked?enabled.add(c.value):enabled.delete(c.value);styleRoutes();});function routeToggle(all){enabled.clear();DATA.routes.forEach(r=>{if(all)enabled.add(r.id)});$('routegrid').querySelectorAll('input').forEach(c=>c.checked=all);styleRoutes();}$('routesall').onclick=()=>routeToggle(true);$('routesnone').onclick=()=>routeToggle(false);
|
||||||
|
$('report').onclick=()=>$('reportdialog').showModal();$('sources').onclick=()=>$('sourcesdialog').showModal();document.querySelectorAll('dialog .close').forEach(c=>c.onclick=()=>c.closest('dialog').close());
|
||||||
|
$('auditbody').innerHTML=DATA.audit.map(a=>`<tr><td>${esc(a.player_name.split(' - S')[0])}<br><b>S${a.native_id}</b></td><td><b>${esc(a.gtfs_stop_id)}</b> ${esc(a.gtfs_name)}<br>${esc(a.mapping_basis)}</td><td>GTFS ${a.gtfs_lat}, ${a.gtfs_lon}<br>Native API difference: ${a.coordinate_delta_m} m${a.coordinate_review?' · REVIEW':''}<br>${a.station_reference?'Kiosk station reference: '+a.station_reference:''}</td></tr>`).join('');
|
||||||
|
$('csvdownload').onclick=()=>{const keys=Object.keys(DATA.audit[0]),quote=x=>'"'+String(x??'').replace(/"/g,'""')+'"',csv=[keys,...DATA.audit.map(a=>keys.map(k=>a[k]))].map(r=>r.map(quote).join(',')).join('\r\n'),a=document.createElement('a'),url=URL.createObjectURL(new Blob([csv],{type:'text/csv'}));a.href=url;a.download='resolved-stop-crosswalk.csv';a.click();setTimeout(()=>URL.revokeObjectURL(url),5000);};
|
||||||
|
$('sourcebody').innerHTML=`<p><strong>OnSign:</strong> read-only MCP snapshot. As of ${esc(DATA.meta.as_of)} (cache age ${DATA.meta.cache_age_s}s). All supplied player records are retained, including excluded records. Health is a captured snapshot and does not refresh automatically. OnSign location values are never used.</p><p><strong>Official feed:</strong> linked by <a target="_blank" href="${DATA.meta.official_page}">RTC Washoe’s Resources and Reports page</a>. <a href="${DATA.meta.feed_url}">Download publisher’s current ZIP</a>.</p><p><strong>Verified validity:</strong> feed_info September 5, 2026–January 1, 2027, version 1. HTTP Last-Modified September 8, 2026, 19:07:45 UTC. Downloaded October 28, 2026. October 28 has active weekday service after calendar_dates exceptions. 20 routes, 63 active route/shape combinations; shapes are ordered by shape_pt_sequence and colored with route_color.</p><p><strong>Native crosswalk:</strong> CAD/AVL GetStop responses retrieved ${esc(DATA.meta.native_retrieved_at_utc)}. Native StopID resolves through SrcStopID to GTFS stop_id; names are recorded for corroboration. ${DATA.meta.coordinate_reviews} native stop coordinates differ by more than 25 m; differences are separately reported. All marker and navigation destinations use unmodified GTFS coordinates. Zero unmatched or ambiguous IDs remain.</p><p><strong>Kiosks:</strong> comma-separated IDs resolve individually to station bays. Kiosk markers remain GTFS parent station references, not device coordinates. Route associations use exactly the resolved bays rather than assuming every station bay is assigned.</p><p><strong>Route evidence:</strong> resolved stop IDs → stop_times → active trips → route_id and shape_id. Associations describe GTFS service, not verified OnSign screen content. Native route IDs are not equated to GTFS route IDs. No route is inferred from a nearby line.</p><p><strong>Navigation:</strong> Apple Maps and Google Maps links request driving directions to the selected GTFS stop coordinates. The phone supplies the origin when opened. Street tiles and navigation require internet; embedded map data and controls work offline.</p><p><strong>Basemaps:</strong> CARTO Positron uses vector tiles rendered by MapLibre inside Leaflet. The street-label option hides POIs and house numbers; the no-label option hides all labels. Esri Gray Canvas and original OpenStreetMap are raster alternatives. Plain background needs no basemap connection. Your basemap choice is remembered in this browser. Basemap choice does not alter GTFS coordinates, route colors, or navigation destinations.</p><p><strong>Operating limits:</strong> scheduled geometry; no bus positions, detours, closures or arrival predictions. Native CAD/AVL credentials are excluded. The configured CARTO browser basemap key is used only for basemap requests. No OnSign write operations are included.</p><p class="muted">Feed SHA-256: ${esc(DATA.meta.sha256)}</p>`;
|
||||||
|
new ResizeObserver(()=>map.invalidateSize({pan:false})).observe($('map'));
|
||||||
|
const field=DATA.players.filter(p=>!p.excluded);$('stat-field').textContent=field.length;$('stat-health').textContent=field.filter(p=>p.status==='online').length+' / '+field.filter(p=>p.status==='offline').length;$('stat-excluded').textContent=DATA.players.filter(p=>p.excluded).length;render();fit();styleRoutes();window.RTC_MAP={data:DATA,map,selectPlayer,markerItems,routeLayers};
|
||||||
|
</script></body></html>
|
||||||
Vendored
+26
@@ -0,0 +1,26 @@
|
|||||||
|
BSD 2-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2010-2023, Volodymyr Agafonkin
|
||||||
|
Copyright (c) 2010-2011, CloudMade
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
Copyright (c) 2021 MapLibre contributors
|
||||||
|
|
||||||
|
Copyright (c) 2014, Mapbox
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
Vendored
+116
@@ -0,0 +1,116 @@
|
|||||||
|
Copyright (c) 2023, MapLibre contributors
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of MapLibre GL JS nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software
|
||||||
|
without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Contains code from mapbox-gl-js v1.13 and earlier
|
||||||
|
|
||||||
|
Version v1.13 of mapbox-gl-js and earlier are licensed under a BSD-3-Clause license
|
||||||
|
|
||||||
|
Copyright (c) 2020, Mapbox
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of Mapbox GL JS nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software
|
||||||
|
without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Contains code from glfx.js
|
||||||
|
|
||||||
|
Copyright (C) 2011 by Evan Wallace
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Contains a portion of d3-color https://github.com/d3/d3-color
|
||||||
|
|
||||||
|
Copyright 2010-2016 Mike Bostock
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the author nor the names of contributors may be used to
|
||||||
|
endorse or promote products derived from this software without specific prior
|
||||||
|
written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
Vendored
+346
@@ -0,0 +1,346 @@
|
|||||||
|
(function (root, factory) {
|
||||||
|
if (typeof define === 'function' && define.amd) {
|
||||||
|
// AMD
|
||||||
|
define(['leaflet', 'maplibre-gl'], factory);
|
||||||
|
} else if (typeof exports === 'object') {
|
||||||
|
// Node, CommonJS-like
|
||||||
|
module.exports = factory(require('leaflet'), require('maplibre-gl'));
|
||||||
|
} else {
|
||||||
|
// Browser globals (root is window)
|
||||||
|
root.returnExports = factory(root.L, root.maplibregl);
|
||||||
|
}
|
||||||
|
}(typeof globalThis !== 'undefined' ? globalThis : this || self, function (L, maplibregl) {
|
||||||
|
L.MaplibreGL = L.Layer.extend({
|
||||||
|
options: {
|
||||||
|
updateInterval: 32,
|
||||||
|
// How much to extend the overlay view (relative to map size)
|
||||||
|
// e.g. 0.1 would be 10% of map view in each direction
|
||||||
|
padding: 0.1,
|
||||||
|
// whether or not to register the mouse and keyboard
|
||||||
|
// events on the maplibre overlay
|
||||||
|
interactive: false,
|
||||||
|
// set the tilepane as the default pane to draw gl tiles
|
||||||
|
pane: 'tilePane'
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize: function (options) {
|
||||||
|
L.setOptions(this, options);
|
||||||
|
|
||||||
|
// setup throttling the update event when panning
|
||||||
|
this._throttledUpdate = L.Util.throttle(this._update, this.options.updateInterval, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
onAdd: function (map) {
|
||||||
|
if (!this._container) {
|
||||||
|
this._initContainer();
|
||||||
|
}
|
||||||
|
|
||||||
|
var paneName = this.getPaneName();
|
||||||
|
map.getPane(paneName).appendChild(this._container);
|
||||||
|
|
||||||
|
this._initGL();
|
||||||
|
|
||||||
|
this._offset = this._map.containerPointToLayerPoint([0, 0]);
|
||||||
|
|
||||||
|
// work around https://github.com/mapbox/mapbox-gl-leaflet/issues/47
|
||||||
|
if (map.options.zoomAnimation) {
|
||||||
|
L.DomEvent.on(map._proxy, L.DomUtil.TRANSITION_END, this._transitionEnd, this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onRemove: function (map) {
|
||||||
|
if (this._map._proxy && this._map.options.zoomAnimation) {
|
||||||
|
L.DomEvent.off(this._map._proxy, L.DomUtil.TRANSITION_END, this._transitionEnd, this);
|
||||||
|
}
|
||||||
|
var paneName = this.getPaneName();
|
||||||
|
map.getPane(paneName).removeChild(this._container);
|
||||||
|
|
||||||
|
this._glMap.remove();
|
||||||
|
this._glMap = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
getEvents: function () {
|
||||||
|
return {
|
||||||
|
move: this._throttledUpdate, // sensibly throttle updating while panning
|
||||||
|
zoomanim: this._animateZoom, // applys the zoom animation to the <canvas>
|
||||||
|
zoom: this._pinchZoom, // animate every zoom event for smoother pinch-zooming
|
||||||
|
zoomstart: this._zoomStart, // flag starting a zoom to disable panning
|
||||||
|
zoomend: this._zoomEnd,
|
||||||
|
resize: this._resize
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// https://leafletjs.com/reference.html#layer-getattribution
|
||||||
|
getAttribution: function () {
|
||||||
|
// Return custom attribution if specified in options
|
||||||
|
if (this.options.attributionControl) {
|
||||||
|
return this.options.attributionControl.customAttribution;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather attributions from MapLibre styles
|
||||||
|
var map = this._glMap;
|
||||||
|
if (map && this.options.attributionControl !== false) {
|
||||||
|
var style = map.getStyle();
|
||||||
|
if (style && style.sources) {
|
||||||
|
return Object.keys(style.sources)
|
||||||
|
.map(function (sourceId) {
|
||||||
|
var source = map.getSource(sourceId);
|
||||||
|
return (source && typeof source.attribution === 'string') ? source.attribution.trim() : null;
|
||||||
|
})
|
||||||
|
.filter(Boolean) // Remove null/undefined values
|
||||||
|
.join(', ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
|
||||||
|
getMaplibreMap: function () {
|
||||||
|
return this._glMap;
|
||||||
|
},
|
||||||
|
|
||||||
|
getCanvas: function () {
|
||||||
|
return this._glMap.getCanvas();
|
||||||
|
},
|
||||||
|
|
||||||
|
getSize: function () {
|
||||||
|
return this._map.getSize().multiplyBy(1 + this.options.padding * 2);
|
||||||
|
},
|
||||||
|
|
||||||
|
getBounds: function () {
|
||||||
|
var halfSize = this.getSize().multiplyBy(0.5);
|
||||||
|
var center = this._map.latLngToContainerPoint(this._map.getCenter());
|
||||||
|
return L.latLngBounds(
|
||||||
|
this._map.containerPointToLatLng(center.subtract(halfSize)),
|
||||||
|
this._map.containerPointToLatLng(center.add(halfSize))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
getContainer: function () {
|
||||||
|
return this._container;
|
||||||
|
},
|
||||||
|
|
||||||
|
// returns the pane name set in options if it is a valid pane, defaults to tilePane
|
||||||
|
getPaneName: function () {
|
||||||
|
return this._map.getPane(this.options.pane) ? this.options.pane : 'tilePane';
|
||||||
|
},
|
||||||
|
|
||||||
|
_roundPoint: function (p) {
|
||||||
|
return { x: Math.round(p.x), y: Math.round(p.y) };
|
||||||
|
},
|
||||||
|
|
||||||
|
_initContainer: function () {
|
||||||
|
var container = this._container = L.DomUtil.create('div', 'leaflet-gl-layer');
|
||||||
|
this._resizeContainer();
|
||||||
|
|
||||||
|
var offset = this._map.getSize().multiplyBy(this.options.padding);
|
||||||
|
|
||||||
|
var topLeft = this._map.containerPointToLayerPoint([0, 0]).subtract(offset);
|
||||||
|
|
||||||
|
L.DomUtil.setPosition(container, this._roundPoint(topLeft));
|
||||||
|
},
|
||||||
|
|
||||||
|
_resizeContainer: function () {
|
||||||
|
var size = this.getSize();
|
||||||
|
this._container.style.width = size.x + 'px';
|
||||||
|
this._container.style.height = size.y + 'px';
|
||||||
|
},
|
||||||
|
|
||||||
|
_initGL: function () {
|
||||||
|
var center = this._map.getCenter();
|
||||||
|
|
||||||
|
var options = L.extend({}, this.options, {
|
||||||
|
container: this._container,
|
||||||
|
center: [center.lng, center.lat],
|
||||||
|
zoom: this._map.getZoom() - 1,
|
||||||
|
attributionControl: false
|
||||||
|
});
|
||||||
|
|
||||||
|
this._glMap = new maplibregl.Map(options);
|
||||||
|
|
||||||
|
var _map = this._map;
|
||||||
|
var _currentAttribution = this.getAttribution();
|
||||||
|
var _getAttribution = this.getAttribution.bind(this);
|
||||||
|
this._glMap.on('load', function () {
|
||||||
|
// Force attribution update
|
||||||
|
if (_map && _map.attributionControl) {
|
||||||
|
_map.attributionControl.removeAttribution(_currentAttribution);
|
||||||
|
_map.attributionControl.addAttribution(_getAttribution());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// allow GL base map to pan beyond min/max latitudes
|
||||||
|
// Defensively check if properties are writable before setting them,
|
||||||
|
// ensuring compatibility with both old and new versions of MapLibre GL JS.
|
||||||
|
var transformProto = Object.getPrototypeOf(this._glMap.transform);
|
||||||
|
|
||||||
|
var latRangeDescriptor = Object.getOwnPropertyDescriptor(transformProto, 'latRange');
|
||||||
|
if (!latRangeDescriptor || latRangeDescriptor.set || latRangeDescriptor.writable) {
|
||||||
|
this._glMap.transform.latRange = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Although this property is obsolete in modern versions, we apply the same
|
||||||
|
// defensive check for robust backward compatibility.
|
||||||
|
var maxValidLatitudeDescriptor = Object.getOwnPropertyDescriptor(transformProto, 'maxValidLatitude');
|
||||||
|
if (!maxValidLatitudeDescriptor || maxValidLatitudeDescriptor.set || maxValidLatitudeDescriptor.writable) {
|
||||||
|
this._glMap.transform.maxValidLatitude = Infinity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// check for the existence of _helper and _latRange in MapLibre
|
||||||
|
// this supports MapLibre v5
|
||||||
|
if (this._glMap.transform._helper && this._glMap.transform._helper._latRange) {
|
||||||
|
this._glMap.transform._helper._latRange = [-Infinity, Infinity];
|
||||||
|
}
|
||||||
|
|
||||||
|
this._transformGL(this._glMap);
|
||||||
|
|
||||||
|
if (this._glMap._canvas.canvas) {
|
||||||
|
// older versions of mapbox-gl surfaced the canvas differently
|
||||||
|
this._glMap._actualCanvas = this._glMap._canvas.canvas;
|
||||||
|
} else {
|
||||||
|
this._glMap._actualCanvas = this._glMap._canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// treat child <canvas> element like L.ImageOverlay
|
||||||
|
var canvas = this._glMap._actualCanvas;
|
||||||
|
L.DomUtil.addClass(canvas, 'leaflet-image-layer');
|
||||||
|
L.DomUtil.addClass(canvas, 'leaflet-zoom-animated');
|
||||||
|
|
||||||
|
if (this.options.interactive) {
|
||||||
|
L.DomUtil.addClass(canvas, 'leaflet-interactive');
|
||||||
|
}
|
||||||
|
if (this.options.className) {
|
||||||
|
L.DomUtil.addClass(canvas, this.options.className);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_update: function (e) {
|
||||||
|
if (!this._map) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// update the offset so we can correct for it later when we zoom
|
||||||
|
this._offset = this._map.containerPointToLayerPoint([0, 0]);
|
||||||
|
|
||||||
|
if (this._zooming) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var container = this._container,
|
||||||
|
gl = this._glMap,
|
||||||
|
offset = this._map.getSize().multiplyBy(this.options.padding),
|
||||||
|
topLeft = this._map.containerPointToLayerPoint([0, 0]).subtract(offset);
|
||||||
|
|
||||||
|
L.DomUtil.setPosition(container, this._roundPoint(topLeft));
|
||||||
|
|
||||||
|
this._transformGL(gl);
|
||||||
|
},
|
||||||
|
|
||||||
|
_transformGL: function (gl) {
|
||||||
|
var center = this._map.getCenter();
|
||||||
|
|
||||||
|
// gl.setView([center.lat, center.lng], this._map.getZoom() - 1, 0);
|
||||||
|
// calling setView directly causes sync issues because it uses requestAnimFrame
|
||||||
|
|
||||||
|
var tr = gl._getTransformForUpdate(); // .clone() ?
|
||||||
|
|
||||||
|
if (tr.setCenter) {
|
||||||
|
// maplibre 5.0.0 and higher:
|
||||||
|
tr.setCenter(maplibregl.LngLat.convert([center.lng, center.lat]));
|
||||||
|
tr.setZoom(this._map.getZoom() - 1);
|
||||||
|
gl.transform.apply(tr);
|
||||||
|
} else {
|
||||||
|
// maplibre < 5.0.0
|
||||||
|
tr = gl.transform;
|
||||||
|
tr.center = maplibregl.LngLat.convert([center.lng, center.lat]);
|
||||||
|
tr.zoom = this._map.getZoom() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl._fireMoveEvents();
|
||||||
|
},
|
||||||
|
|
||||||
|
// update the map constantly during a pinch zoom
|
||||||
|
_pinchZoom: function (e) {
|
||||||
|
this._glMap.jumpTo({
|
||||||
|
zoom: this._map.getZoom() - 1,
|
||||||
|
center: this._map.getCenter()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// borrowed from L.ImageOverlay
|
||||||
|
// https://github.com/Leaflet/Leaflet/blob/master/src/layer/ImageOverlay.js#L139-L144
|
||||||
|
_animateZoom: function (e) {
|
||||||
|
var scale = this._map.getZoomScale(e.zoom);
|
||||||
|
var padding = this._map.getSize().multiplyBy(this.options.padding * scale);
|
||||||
|
var viewHalf = this.getSize()._divideBy(2);
|
||||||
|
// corrections for padding (scaled), adapted from
|
||||||
|
// https://github.com/Leaflet/Leaflet/blob/master/src/map/Map.js#L1490-L1508
|
||||||
|
var topLeft = this._map.project(e.center, e.zoom)
|
||||||
|
._subtract(viewHalf)
|
||||||
|
._add(this._map._getMapPanePos()
|
||||||
|
.add(padding))._round();
|
||||||
|
var offset = this._map.project(this._map.getBounds().getNorthWest(), e.zoom)
|
||||||
|
._subtract(topLeft);
|
||||||
|
|
||||||
|
L.DomUtil.setTransform(
|
||||||
|
this._glMap._actualCanvas,
|
||||||
|
offset.subtract(this._offset),
|
||||||
|
scale
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
_zoomStart: function (e) {
|
||||||
|
this._zooming = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_zoomEnd: function () {
|
||||||
|
var scale = this._map.getZoomScale(this._map.getZoom());
|
||||||
|
|
||||||
|
L.DomUtil.setTransform(
|
||||||
|
this._glMap._actualCanvas,
|
||||||
|
// https://github.com/mapbox/mapbox-gl-leaflet/pull/130
|
||||||
|
null,
|
||||||
|
scale
|
||||||
|
);
|
||||||
|
|
||||||
|
this._zooming = false;
|
||||||
|
|
||||||
|
this._update();
|
||||||
|
},
|
||||||
|
|
||||||
|
_transitionEnd: function (e) {
|
||||||
|
L.Util.requestAnimFrame(function () {
|
||||||
|
var zoom = this._map.getZoom();
|
||||||
|
var center = this._map.getCenter();
|
||||||
|
var offset = this._map.latLngToContainerPoint(
|
||||||
|
this._map.getBounds().getNorthWest()
|
||||||
|
);
|
||||||
|
this._resizeContainer();
|
||||||
|
|
||||||
|
// reset the scale and offset
|
||||||
|
L.DomUtil.setTransform(this._glMap._actualCanvas, offset, 1);
|
||||||
|
|
||||||
|
// enable panning once the gl map is ready again
|
||||||
|
this._glMap.once('moveend', L.Util.bind(function () {
|
||||||
|
this._zoomEnd();
|
||||||
|
}, this));
|
||||||
|
|
||||||
|
// update the map position
|
||||||
|
this._glMap.jumpTo({
|
||||||
|
center: center,
|
||||||
|
zoom: zoom - 1
|
||||||
|
});
|
||||||
|
}, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
_resize: function (e) {
|
||||||
|
this._transitionEnd(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
L.maplibreGL = function (options) {
|
||||||
|
return new L.MaplibreGL(options);
|
||||||
|
};
|
||||||
|
|
||||||
|
}));
|
||||||
Vendored
+661
@@ -0,0 +1,661 @@
|
|||||||
|
/* required styles */
|
||||||
|
|
||||||
|
.leaflet-pane,
|
||||||
|
.leaflet-tile,
|
||||||
|
.leaflet-marker-icon,
|
||||||
|
.leaflet-marker-shadow,
|
||||||
|
.leaflet-tile-container,
|
||||||
|
.leaflet-pane > svg,
|
||||||
|
.leaflet-pane > canvas,
|
||||||
|
.leaflet-zoom-box,
|
||||||
|
.leaflet-image-layer,
|
||||||
|
.leaflet-layer {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.leaflet-container {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.leaflet-tile,
|
||||||
|
.leaflet-marker-icon,
|
||||||
|
.leaflet-marker-shadow {
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-drag: none;
|
||||||
|
}
|
||||||
|
/* Prevents IE11 from highlighting tiles in blue */
|
||||||
|
.leaflet-tile::selection {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
|
||||||
|
.leaflet-safari .leaflet-tile {
|
||||||
|
image-rendering: -webkit-optimize-contrast;
|
||||||
|
}
|
||||||
|
/* hack that prevents hw layers "stretching" when loading new tiles */
|
||||||
|
.leaflet-safari .leaflet-tile-container {
|
||||||
|
width: 1600px;
|
||||||
|
height: 1600px;
|
||||||
|
-webkit-transform-origin: 0 0;
|
||||||
|
}
|
||||||
|
.leaflet-marker-icon,
|
||||||
|
.leaflet-marker-shadow {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
|
||||||
|
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
|
||||||
|
.leaflet-container .leaflet-overlay-pane svg {
|
||||||
|
max-width: none !important;
|
||||||
|
max-height: none !important;
|
||||||
|
}
|
||||||
|
.leaflet-container .leaflet-marker-pane img,
|
||||||
|
.leaflet-container .leaflet-shadow-pane img,
|
||||||
|
.leaflet-container .leaflet-tile-pane img,
|
||||||
|
.leaflet-container img.leaflet-image-layer,
|
||||||
|
.leaflet-container .leaflet-tile {
|
||||||
|
max-width: none !important;
|
||||||
|
max-height: none !important;
|
||||||
|
width: auto;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-container img.leaflet-tile {
|
||||||
|
/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */
|
||||||
|
mix-blend-mode: plus-lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-container.leaflet-touch-zoom {
|
||||||
|
-ms-touch-action: pan-x pan-y;
|
||||||
|
touch-action: pan-x pan-y;
|
||||||
|
}
|
||||||
|
.leaflet-container.leaflet-touch-drag {
|
||||||
|
-ms-touch-action: pinch-zoom;
|
||||||
|
/* Fallback for FF which doesn't support pinch-zoom */
|
||||||
|
touch-action: none;
|
||||||
|
touch-action: pinch-zoom;
|
||||||
|
}
|
||||||
|
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
|
||||||
|
-ms-touch-action: none;
|
||||||
|
touch-action: none;
|
||||||
|
}
|
||||||
|
.leaflet-container {
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
.leaflet-container a {
|
||||||
|
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
|
||||||
|
}
|
||||||
|
.leaflet-tile {
|
||||||
|
filter: inherit;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
.leaflet-tile-loaded {
|
||||||
|
visibility: inherit;
|
||||||
|
}
|
||||||
|
.leaflet-zoom-box {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
z-index: 800;
|
||||||
|
}
|
||||||
|
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
|
||||||
|
.leaflet-overlay-pane svg {
|
||||||
|
-moz-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-pane { z-index: 400; }
|
||||||
|
|
||||||
|
.leaflet-tile-pane { z-index: 200; }
|
||||||
|
.leaflet-overlay-pane { z-index: 400; }
|
||||||
|
.leaflet-shadow-pane { z-index: 500; }
|
||||||
|
.leaflet-marker-pane { z-index: 600; }
|
||||||
|
.leaflet-tooltip-pane { z-index: 650; }
|
||||||
|
.leaflet-popup-pane { z-index: 700; }
|
||||||
|
|
||||||
|
.leaflet-map-pane canvas { z-index: 100; }
|
||||||
|
.leaflet-map-pane svg { z-index: 200; }
|
||||||
|
|
||||||
|
.leaflet-vml-shape {
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
.lvml {
|
||||||
|
behavior: url(#default#VML);
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* control positioning */
|
||||||
|
|
||||||
|
.leaflet-control {
|
||||||
|
position: relative;
|
||||||
|
z-index: 800;
|
||||||
|
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
.leaflet-top,
|
||||||
|
.leaflet-bottom {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1000;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.leaflet-top {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.leaflet-right {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
.leaflet-bottom {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
.leaflet-left {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.leaflet-control {
|
||||||
|
float: left;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
.leaflet-right .leaflet-control {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.leaflet-top .leaflet-control {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.leaflet-bottom .leaflet-control {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.leaflet-left .leaflet-control {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.leaflet-right .leaflet-control {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* zoom and fade animations */
|
||||||
|
|
||||||
|
.leaflet-fade-anim .leaflet-popup {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transition: opacity 0.2s linear;
|
||||||
|
-moz-transition: opacity 0.2s linear;
|
||||||
|
transition: opacity 0.2s linear;
|
||||||
|
}
|
||||||
|
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.leaflet-zoom-animated {
|
||||||
|
-webkit-transform-origin: 0 0;
|
||||||
|
-ms-transform-origin: 0 0;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
}
|
||||||
|
svg.leaflet-zoom-animated {
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-zoom-anim .leaflet-zoom-animated {
|
||||||
|
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||||
|
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||||
|
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||||
|
}
|
||||||
|
.leaflet-zoom-anim .leaflet-tile,
|
||||||
|
.leaflet-pan-anim .leaflet-tile {
|
||||||
|
-webkit-transition: none;
|
||||||
|
-moz-transition: none;
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-zoom-anim .leaflet-zoom-hide {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* cursors */
|
||||||
|
|
||||||
|
.leaflet-interactive {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.leaflet-grab {
|
||||||
|
cursor: -webkit-grab;
|
||||||
|
cursor: -moz-grab;
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
.leaflet-crosshair,
|
||||||
|
.leaflet-crosshair .leaflet-interactive {
|
||||||
|
cursor: crosshair;
|
||||||
|
}
|
||||||
|
.leaflet-popup-pane,
|
||||||
|
.leaflet-control {
|
||||||
|
cursor: auto;
|
||||||
|
}
|
||||||
|
.leaflet-dragging .leaflet-grab,
|
||||||
|
.leaflet-dragging .leaflet-grab .leaflet-interactive,
|
||||||
|
.leaflet-dragging .leaflet-marker-draggable {
|
||||||
|
cursor: move;
|
||||||
|
cursor: -webkit-grabbing;
|
||||||
|
cursor: -moz-grabbing;
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* marker & overlays interactivity */
|
||||||
|
.leaflet-marker-icon,
|
||||||
|
.leaflet-marker-shadow,
|
||||||
|
.leaflet-image-layer,
|
||||||
|
.leaflet-pane > svg path,
|
||||||
|
.leaflet-tile-container {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-marker-icon.leaflet-interactive,
|
||||||
|
.leaflet-image-layer.leaflet-interactive,
|
||||||
|
.leaflet-pane > svg path.leaflet-interactive,
|
||||||
|
svg.leaflet-image-layer.leaflet-interactive path {
|
||||||
|
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* visual tweaks */
|
||||||
|
|
||||||
|
.leaflet-container {
|
||||||
|
background: #ddd;
|
||||||
|
outline-offset: 1px;
|
||||||
|
}
|
||||||
|
.leaflet-container a {
|
||||||
|
color: #0078A8;
|
||||||
|
}
|
||||||
|
.leaflet-zoom-box {
|
||||||
|
border: 2px dotted #38f;
|
||||||
|
background: rgba(255,255,255,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* general typography */
|
||||||
|
.leaflet-container {
|
||||||
|
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* general toolbar styles */
|
||||||
|
|
||||||
|
.leaflet-bar {
|
||||||
|
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.leaflet-bar a {
|
||||||
|
background-color: #fff;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
line-height: 26px;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.leaflet-bar a,
|
||||||
|
.leaflet-control-layers-toggle {
|
||||||
|
background-position: 50% 50%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.leaflet-bar a:hover,
|
||||||
|
.leaflet-bar a:focus {
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
.leaflet-bar a:first-child {
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
}
|
||||||
|
.leaflet-bar a:last-child {
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.leaflet-bar a.leaflet-disabled {
|
||||||
|
cursor: default;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
color: #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-touch .leaflet-bar a {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
.leaflet-touch .leaflet-bar a:first-child {
|
||||||
|
border-top-left-radius: 2px;
|
||||||
|
border-top-right-radius: 2px;
|
||||||
|
}
|
||||||
|
.leaflet-touch .leaflet-bar a:last-child {
|
||||||
|
border-bottom-left-radius: 2px;
|
||||||
|
border-bottom-right-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* zoom control */
|
||||||
|
|
||||||
|
.leaflet-control-zoom-in,
|
||||||
|
.leaflet-control-zoom-out {
|
||||||
|
font: bold 18px 'Lucida Console', Monaco, monospace;
|
||||||
|
text-indent: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* layers control */
|
||||||
|
|
||||||
|
.leaflet-control-layers {
|
||||||
|
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-toggle {
|
||||||
|
background-image: url(images/layers.png);
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
.leaflet-retina .leaflet-control-layers-toggle {
|
||||||
|
background-image: url(images/layers-2x.png);
|
||||||
|
background-size: 26px 26px;
|
||||||
|
}
|
||||||
|
.leaflet-touch .leaflet-control-layers-toggle {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers .leaflet-control-layers-list,
|
||||||
|
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-expanded .leaflet-control-layers-list {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-expanded {
|
||||||
|
padding: 6px 10px 6px 6px;
|
||||||
|
color: #333;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-scrollbar {
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-selector {
|
||||||
|
margin-top: 2px;
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers label {
|
||||||
|
display: block;
|
||||||
|
font-size: 13px;
|
||||||
|
font-size: 1.08333em;
|
||||||
|
}
|
||||||
|
.leaflet-control-layers-separator {
|
||||||
|
height: 0;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
margin: 5px -10px 5px -6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default icon URLs */
|
||||||
|
.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */
|
||||||
|
background-image: url(images/marker-icon.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* attribution and scale controls */
|
||||||
|
|
||||||
|
.leaflet-container .leaflet-control-attribution {
|
||||||
|
background: #fff;
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.leaflet-control-attribution,
|
||||||
|
.leaflet-control-scale-line {
|
||||||
|
padding: 0 5px;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
.leaflet-control-attribution a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.leaflet-control-attribution a:hover,
|
||||||
|
.leaflet-control-attribution a:focus {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.leaflet-attribution-flag {
|
||||||
|
display: inline !important;
|
||||||
|
vertical-align: baseline !important;
|
||||||
|
width: 1em;
|
||||||
|
height: 0.6669em;
|
||||||
|
}
|
||||||
|
.leaflet-left .leaflet-control-scale {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
.leaflet-bottom .leaflet-control-scale {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.leaflet-control-scale-line {
|
||||||
|
border: 2px solid #777;
|
||||||
|
border-top: none;
|
||||||
|
line-height: 1.1;
|
||||||
|
padding: 2px 5px 1px;
|
||||||
|
white-space: nowrap;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
text-shadow: 1px 1px #fff;
|
||||||
|
}
|
||||||
|
.leaflet-control-scale-line:not(:first-child) {
|
||||||
|
border-top: 2px solid #777;
|
||||||
|
border-bottom: none;
|
||||||
|
margin-top: -2px;
|
||||||
|
}
|
||||||
|
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
|
||||||
|
border-bottom: 2px solid #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-touch .leaflet-control-attribution,
|
||||||
|
.leaflet-touch .leaflet-control-layers,
|
||||||
|
.leaflet-touch .leaflet-bar {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.leaflet-touch .leaflet-control-layers,
|
||||||
|
.leaflet-touch .leaflet-bar {
|
||||||
|
border: 2px solid rgba(0,0,0,0.2);
|
||||||
|
background-clip: padding-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* popup */
|
||||||
|
|
||||||
|
.leaflet-popup {
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.leaflet-popup-content-wrapper {
|
||||||
|
padding: 1px;
|
||||||
|
text-align: left;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
.leaflet-popup-content {
|
||||||
|
margin: 13px 24px 13px 20px;
|
||||||
|
line-height: 1.3;
|
||||||
|
font-size: 13px;
|
||||||
|
font-size: 1.08333em;
|
||||||
|
min-height: 1px;
|
||||||
|
}
|
||||||
|
.leaflet-popup-content p {
|
||||||
|
margin: 17px 0;
|
||||||
|
margin: 1.3em 0;
|
||||||
|
}
|
||||||
|
.leaflet-popup-tip-container {
|
||||||
|
width: 40px;
|
||||||
|
height: 20px;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
margin-top: -1px;
|
||||||
|
margin-left: -20px;
|
||||||
|
overflow: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.leaflet-popup-tip {
|
||||||
|
width: 17px;
|
||||||
|
height: 17px;
|
||||||
|
padding: 1px;
|
||||||
|
|
||||||
|
margin: -10px auto 0;
|
||||||
|
pointer-events: auto;
|
||||||
|
|
||||||
|
-webkit-transform: rotate(45deg);
|
||||||
|
-moz-transform: rotate(45deg);
|
||||||
|
-ms-transform: rotate(45deg);
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
.leaflet-popup-content-wrapper,
|
||||||
|
.leaflet-popup-tip {
|
||||||
|
background: white;
|
||||||
|
color: #333;
|
||||||
|
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
.leaflet-container a.leaflet-popup-close-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
border: none;
|
||||||
|
text-align: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
font: 16px/24px Tahoma, Verdana, sans-serif;
|
||||||
|
color: #757575;
|
||||||
|
text-decoration: none;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.leaflet-container a.leaflet-popup-close-button:hover,
|
||||||
|
.leaflet-container a.leaflet-popup-close-button:focus {
|
||||||
|
color: #585858;
|
||||||
|
}
|
||||||
|
.leaflet-popup-scrolled {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-oldie .leaflet-popup-content-wrapper {
|
||||||
|
-ms-zoom: 1;
|
||||||
|
}
|
||||||
|
.leaflet-oldie .leaflet-popup-tip {
|
||||||
|
width: 24px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
|
||||||
|
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaflet-oldie .leaflet-control-zoom,
|
||||||
|
.leaflet-oldie .leaflet-control-layers,
|
||||||
|
.leaflet-oldie .leaflet-popup-content-wrapper,
|
||||||
|
.leaflet-oldie .leaflet-popup-tip {
|
||||||
|
border: 1px solid #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* div icon */
|
||||||
|
|
||||||
|
.leaflet-div-icon {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Tooltip */
|
||||||
|
/* Base styles for the element that has a tooltip */
|
||||||
|
.leaflet-tooltip {
|
||||||
|
position: absolute;
|
||||||
|
padding: 6px;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #fff;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: #222;
|
||||||
|
white-space: nowrap;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
pointer-events: none;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
.leaflet-tooltip.leaflet-interactive {
|
||||||
|
cursor: pointer;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-top:before,
|
||||||
|
.leaflet-tooltip-bottom:before,
|
||||||
|
.leaflet-tooltip-left:before,
|
||||||
|
.leaflet-tooltip-right:before {
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
border: 6px solid transparent;
|
||||||
|
background: transparent;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Directions */
|
||||||
|
|
||||||
|
.leaflet-tooltip-bottom {
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-top {
|
||||||
|
margin-top: -6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-bottom:before,
|
||||||
|
.leaflet-tooltip-top:before {
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-top:before {
|
||||||
|
bottom: 0;
|
||||||
|
margin-bottom: -12px;
|
||||||
|
border-top-color: #fff;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-bottom:before {
|
||||||
|
top: 0;
|
||||||
|
margin-top: -12px;
|
||||||
|
margin-left: -6px;
|
||||||
|
border-bottom-color: #fff;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-left {
|
||||||
|
margin-left: -6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-right {
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-left:before,
|
||||||
|
.leaflet-tooltip-right:before {
|
||||||
|
top: 50%;
|
||||||
|
margin-top: -6px;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-left:before {
|
||||||
|
right: 0;
|
||||||
|
margin-right: -12px;
|
||||||
|
border-left-color: #fff;
|
||||||
|
}
|
||||||
|
.leaflet-tooltip-right:before {
|
||||||
|
left: 0;
|
||||||
|
margin-left: -12px;
|
||||||
|
border-right-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Printing */
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
/* Prevent printers from removing background-images of controls. */
|
||||||
|
.leaflet-control {
|
||||||
|
-webkit-print-color-adjust: exact;
|
||||||
|
print-color-adjust: exact;
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
/* @preserve
|
||||||
|
* Leaflet 1.9.4, a JS library for interactive maps. https://leafletjs.com
|
||||||
|
* (c) 2010-2023 Vladimir Agafonkin, (c) 2010-2011 CloudMade
|
||||||
|
*/
|
||||||
|
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).leaflet={})}(this,function(t){"use strict";function l(t){for(var e,i,n=1,o=arguments.length;n<o;n++)for(e in i=arguments[n])t[e]=i[e];return t}var R=Object.create||function(t){return N.prototype=t,new N};function N(){}function a(t,e){var i,n=Array.prototype.slice;return t.bind?t.bind.apply(t,n.call(arguments,1)):(i=n.call(arguments,2),function(){return t.apply(e,i.length?i.concat(n.call(arguments)):arguments)})}var D=0;function h(t){return"_leaflet_id"in t||(t._leaflet_id=++D),t._leaflet_id}function j(t,e,i){var n,o,s=function(){n=!1,o&&(r.apply(i,o),o=!1)},r=function(){n?o=arguments:(t.apply(i,arguments),setTimeout(s,e),n=!0)};return r}function H(t,e,i){var n=e[1],e=e[0],o=n-e;return t===n&&i?t:((t-e)%o+o)%o+e}function u(){return!1}function i(t,e){return!1===e?t:(e=Math.pow(10,void 0===e?6:e),Math.round(t*e)/e)}function W(t){return t.trim?t.trim():t.replace(/^\s+|\s+$/g,"")}function F(t){return W(t).split(/\s+/)}function c(t,e){for(var i in Object.prototype.hasOwnProperty.call(t,"options")||(t.options=t.options?R(t.options):{}),e)t.options[i]=e[i];return t.options}function U(t,e,i){var n,o=[];for(n in t)o.push(encodeURIComponent(i?n.toUpperCase():n)+"="+encodeURIComponent(t[n]));return(e&&-1!==e.indexOf("?")?"&":"?")+o.join("&")}var V=/\{ *([\w_ -]+) *\}/g;function q(t,i){return t.replace(V,function(t,e){e=i[e];if(void 0===e)throw new Error("No value provided for variable "+t);return e="function"==typeof e?e(i):e})}var d=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)};function G(t,e){for(var i=0;i<t.length;i++)if(t[i]===e)return i;return-1}var K="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";function Y(t){return window["webkit"+t]||window["moz"+t]||window["ms"+t]}var X=0;function J(t){var e=+new Date,i=Math.max(0,16-(e-X));return X=e+i,window.setTimeout(t,i)}var $=window.requestAnimationFrame||Y("RequestAnimationFrame")||J,Q=window.cancelAnimationFrame||Y("CancelAnimationFrame")||Y("CancelRequestAnimationFrame")||function(t){window.clearTimeout(t)};function x(t,e,i){if(!i||$!==J)return $.call(window,a(t,e));t.call(e)}function r(t){t&&Q.call(window,t)}var tt={__proto__:null,extend:l,create:R,bind:a,get lastId(){return D},stamp:h,throttle:j,wrapNum:H,falseFn:u,formatNum:i,trim:W,splitWords:F,setOptions:c,getParamString:U,template:q,isArray:d,indexOf:G,emptyImageUrl:K,requestFn:$,cancelFn:Q,requestAnimFrame:x,cancelAnimFrame:r};function et(){}et.extend=function(t){function e(){c(this),this.initialize&&this.initialize.apply(this,arguments),this.callInitHooks()}var i,n=e.__super__=this.prototype,o=R(n);for(i in(o.constructor=e).prototype=o,this)Object.prototype.hasOwnProperty.call(this,i)&&"prototype"!==i&&"__super__"!==i&&(e[i]=this[i]);if(t.statics&&l(e,t.statics),t.includes){var s=t.includes;if("undefined"!=typeof L&&L&&L.Mixin){s=d(s)?s:[s];for(var r=0;r<s.length;r++)s[r]===L.Mixin.Events&&console.warn("Deprecated include of L.Mixin.Events: this property will be removed in future releases, please inherit from L.Evented instead.",(new Error).stack)}l.apply(null,[o].concat(t.includes))}return l(o,t),delete o.statics,delete o.includes,o.options&&(o.options=n.options?R(n.options):{},l(o.options,t.options)),o._initHooks=[],o.callInitHooks=function(){if(!this._initHooksCalled){n.callInitHooks&&n.callInitHooks.call(this),this._initHooksCalled=!0;for(var t=0,e=o._initHooks.length;t<e;t++)o._initHooks[t].call(this)}},e},et.include=function(t){var e=this.prototype.options;return l(this.prototype,t),t.options&&(this.prototype.options=e,this.mergeOptions(t.options)),this},et.mergeOptions=function(t){return l(this.prototype.options,t),this},et.addInitHook=function(t){var e=Array.prototype.slice.call(arguments,1),i="function"==typeof t?t:function(){this[t].apply(this,e)};return this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(i),this};var e={on:function(t,e,i){if("object"==typeof t)for(var n in t)this._on(n,t[n],e);else for(var o=0,s=(t=F(t)).length;o<s;o++)this._on(t[o],e,i);return this},off:function(t,e,i){if(arguments.length)if("object"==typeof t)for(var n in t)this._off(n,t[n],e);else{t=F(t);for(var o=1===arguments.length,s=0,r=t.length;s<r;s++)o?this._off(t[s]):this._off(t[s],e,i)}else delete this._events;return this},_on:function(t,e,i,n){"function"!=typeof e?console.warn("wrong listener type: "+typeof e):!1===this._listens(t,e,i)&&(e={fn:e,ctx:i=i===this?void 0:i},n&&(e.once=!0),this._events=this._events||{},this._events[t]=this._events[t]||[],this._events[t].push(e))},_off:function(t,e,i){var n,o,s;if(this._events&&(n=this._events[t]))if(1===arguments.length){if(this._firingCount)for(o=0,s=n.length;o<s;o++)n[o].fn=u;delete this._events[t]}else"function"!=typeof e?console.warn("wrong listener type: "+typeof e):!1!==(e=this._listens(Line truncated
|
||||||
|
//# sourceMappingURL=leaflet.js.map
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
.maplibregl-map{font:12px/20px Helvetica Neue,Arial,Helvetica,sans-serif;overflow:hidden;position:relative;-webkit-tap-highlight-color:rgb(0,0,0,0)}.maplibregl-canvas{left:0;position:absolute;top:0}.maplibregl-map:fullscreen{height:100%;width:100%}.maplibregl-ctrl-group button.maplibregl-ctrl-compass{touch-action:none}.maplibregl-canvas-container.maplibregl-interactive,.maplibregl-ctrl-group button.maplibregl-ctrl-compass{cursor:grab;-webkit-user-select:none;-moz-user-select:none;user-select:none}.maplibregl-canvas-container.maplibregl-interactive.maplibregl-track-pointer{cursor:pointer}.maplibregl-canvas-container.maplibregl-interactive:active,.maplibregl-ctrl-group button.maplibregl-ctrl-compass:active{cursor:grabbing}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-canvas-container.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:pinch-zoom}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:none}.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures,.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-ctrl-bottom-left,.maplibregl-ctrl-bottom-right,.maplibregl-ctrl-top-left,.maplibregl-ctrl-top-right{pointer-events:none;position:absolute;z-index:2}.maplibregl-ctrl-top-left{left:0;top:0}.maplibregl-ctrl-top-right{right:0;top:0}.maplibregl-ctrl-bottom-left{bottom:0;left:0}.maplibregl-ctrl-bottom-right{bottom:0;right:0}.maplibregl-ctrl{clear:both;pointer-events:auto;transform:translate(0)}.maplibregl-ctrl-top-left .maplibregl-ctrl{float:left;margin:10px 0 0 10px}.maplibregl-ctrl-top-right .maplibregl-ctrl{float:right;margin:10px 10px 0 0}.maplibregl-ctrl-bottom-left .maplibregl-ctrl{float:left;margin:0 0 10px 10px}.maplibregl-ctrl-bottom-right .maplibregl-ctrl{float:right;margin:0 10px 10px 0}.maplibregl-ctrl-group{background:#fff;border-radius:4px}.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px rgba(0,0,0,.1)}@media (forced-colors:active){.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px ButtonText}}.maplibregl-ctrl-group button{background-color:transparent;border:0;box-sizing:border-box;cursor:pointer;display:block;height:29px;outline:none;padding:0;width:29px}.maplibregl-ctrl-group button+button{border-top:1px solid #ddd}.maplibregl-ctrl button .maplibregl-ctrl-icon{background-position:50%;background-repeat:no-repeat;display:block;height:100%;width:100%}@media (forced-colors:active){.maplibregl-ctrl-icon{background-color:transparent}.maplibregl-ctrl-group button+button{border-top:1px solid ButtonText}}.maplibregl-ctrl button::-moz-focus-inner{border:0;padding:0}.maplibregl-ctrl-attrib-button:focus,.maplibregl-ctrl-group button:focus{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl button:disabled{cursor:not-allowed}.maplibregl-ctrl button:disabled .maplibregl-ctrl-icon{opacity:.25}@media (hover:hover){.maplibregl-ctrl button:not(:disabled):hover{background-color:rgba(0,0,0,.05)}}.maplibregl-ctrl button:not(:disabled):active{background-color:rgba(0,0,0,.05)}.maplibregl-ctrl-group button:focus:focus-visible{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl-group button:focus:not(:focus-visible){box-shadow:none}.maplibregl-ctrl-group button:focus:first-child{border-radius:4px 4px 0 0}.maplibregl-ctrl-group button:focus:last-child{border-radius:0 0 4px 4px}.maplibregl-ctrl-group button:focus:only-child{border-radius:inherit}.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svLine truncated
|
||||||
Vendored
+59
@@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* MapLibre GL JS
|
||||||
|
* @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v5.6.2/LICENSE.txt
|
||||||
|
*/
|
||||||
|
(function (global, factory) {
|
||||||
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||||
|
typeof define === 'function' && define.amd ? define(factory) :
|
||||||
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.maplibregl = factory());
|
||||||
|
})(this, (function () { 'use strict';
|
||||||
|
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
var maplibregl = {};
|
||||||
|
var modules = {};
|
||||||
|
function define(moduleName, _dependencies, moduleFactory) {
|
||||||
|
modules[moduleName] = moduleFactory;
|
||||||
|
|
||||||
|
// to get the list of modules see generated dist/maplibre-gl-dev.js file (look for `define(` calls)
|
||||||
|
if (moduleName !== 'index') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we assume that when an index module is initializing then other modules are loaded already
|
||||||
|
var workerBundleString = 'var sharedModule = {}; (' + modules.shared + ')(sharedModule); (' + modules.worker + ')(sharedModule);'
|
||||||
|
|
||||||
|
var sharedModule = {};
|
||||||
|
// the order of arguments of a module factory depends on rollup (it decides who is whose dependency)
|
||||||
|
// to check the correct order, see dist/maplibre-gl-dev.js file (look for `define(` calls)
|
||||||
|
// we assume that for our 3 chunks it will generate 3 modules and their order is predefined like the following
|
||||||
|
modules.shared(sharedModule);
|
||||||
|
modules.index(maplibregl, sharedModule);
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
maplibregl.setWorkerUrl(window.URL.createObjectURL(new Blob([workerBundleString], { type: 'text/javascript' })));
|
||||||
|
}
|
||||||
|
|
||||||
|
return maplibregl;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
define("shared",["exports"],(function(t){"use strict";function e(t,e,r,n){return new(r||(r=Promise))((function(i,s){function a(t){try{l(n.next(t));}catch(t){s(t);}}function o(t){try{l(n.throw(t));}catch(t){s(t);}}function l(t){var e;t.done?i(t.value):(e=t.value,e instanceof r?e:new r((function(t){t(e);}))).then(a,o);}l((n=n.apply(t,e||[])).next());}))}function r(t,e){this.x=t,this.y=e;}function n(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var i,s;"function"==typeof SuppressedError&&SuppressedError,r.prototype={clone(){return new r(this.x,this.y)},add(t){return this.clone()._add(t)},sub(t){return this.clone()._sub(t)},multByPoint(t){return this.clone()._multByPoint(t)},divByPoint(t){return this.clone()._divByPoint(t)},mult(t){return this.clone()._mult(t)},div(t){return this.clone()._div(t)},rotate(t){return this.clone()._rotate(t)},rotateAround(t,e){return this.clone()._rotateAround(t,e)},matMult(t){return this.clone()._matMult(t)},unit(){return this.clone()._unit()},perp(){return this.clone()._perp()},round(){return this.clone()._round()},mag(){return Math.sqrt(this.x*this.x+this.y*this.y)},equals(t){return this.x===t.x&&this.y===t.y},dist(t){return Math.sqrt(this.distSqr(t))},distSqr(t){const e=t.x-this.x,r=t.y-this.y;return e*e+r*r},angle(){return Math.atan2(this.y,this.x)},angleTo(t){return Math.atan2(this.y-t.y,this.x-t.x)},angleWith(t){return this.angleWithSep(t.x,t.y)},angleWithSep(t,e){return Math.atan2(this.x*e-this.y*t,this.x*t+this.y*e)},_matMult(t){const e=t[2]*this.x+t[3]*this.y;return this.x=t[0]*this.x+t[1]*this.y,this.y=e,this},_add(t){return this.x+=t.x,this.y+=t.y,this},_sub(t){return this.x-=t.x,this.y-=t.y,this},_mult(t){return this.x*=t,this.y*=t,this},_div(t){return this.x/=t,this.y/=t,this},_multByPoint(t){return this.x*=t.x,this.y*=t.y,this},_divByPoint(t){return this.x/=t.x,this.y/=t.y,this},_unit(){return this._div(this.mag()),this},_perp(){const t=this.y;return this.y=this.x,this.x=-t,this},_rotate(t){const e=Math.cos(t),r=Math.sin(t),n=r*this.x+e*this.y;return this.x=e*this.x-r*this.y,this.y=n,this},_rotateAround(t,e){const r=Math.cos(t),n=Math.sin(t),i=e.y+n*(this.x-e.x)+r*(this.y-e.y);return this.x=e.x+r*(this.x-e.x)-n*(this.y-e.y),this.y=i,this},_round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},constructor:r},r.convert=function(t){if(t instanceof r)return t;if(Array.isArray(t))return new r(+t[0],+t[1]);if(void 0!==t.x&&void 0!==t.y)return new r(+t.x,+t.y);throw new Error("Expected [x, y] or {x, y} point format")};var a=function(){if(s)return i;function t(t,e,r,n){this.cx=3*t,this.bx=3*(r-t)-this.cx,this.ax=1-this.cx-this.bx,this.cy=3*e,this.by=3*(n-e)-this.cy,this.ay=1-this.cy-this.by,this.p1x=t,this.p1y=e,this.p2x=r,this.p2y=n;}return s=1,i=t,t.prototype={sampleCurveX:function(t){return ((this.ax*t+this.bx)*t+this.cx)*t},sampleCurveY:function(t){return ((this.ay*t+this.by)*t+this.cy)*t},sampleCurveDerivativeX:function(t){return (3*this.ax*t+2*this.bx)*t+this.cx},solveCurveX:function(t,e){if(void 0===e&&(e=1e-6),t<0)return 0;if(t>1)return 1;for(var r=t,n=0;n<8;n++){var i=this.sampleCurveX(r)-t;if(Math.abs(i)<e)return r;var s=this.sampleCurveDerivativeX(r);if(Math.abs(s)<1e-6)break;r-=i/s;}var a=0,o=1;for(r=t,n=0;n<20&&(i=this.sampleCurveX(r),!(Math.abs(i-t)<e));n++)t>i?a=r:o=r,r=.5*(o-a)+a;return r},solve:function(t,e){return this.sampleCurveY(this.solveCurveX(t,e))}},i}(),o=n(a);let l,u;function c(){return null==l&&(l="undefined"!=typeof OffscreenCanvas&&new OffscreenCanvas(1,1).getContext("2d")&&"function"==typeof createImageBitmap),l}function h(){if(null==u&&(u=!1,c())){const t=5,e=new OffscreenCanvas(t,t).getContext("2d",{willReadFrequently:!0});if(e){for(let r=0;r<t*t;r++){const n=4*r;e.fillStyle=`rgb(${n},${n+1},${n+2})`,e.fillRect(r%t,Math.floor(r/t),1,1);}const r=e.getImageData(0,0,t,t).data;for(let e=0;e<t*t*4;e++)if(e%4!=3&&r[e]!==e){u=!0;break}}}return u||!1}var p=1e-6,f="undefined"!=typeof Float32Array?Float32Array:Array;function d(){var t=new f(9);return f!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[5]=0,t[6]=0,t[7]=0),t[0]=1,t[4]=1,t[8]=1,t}function y(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t}function m(){var t=new f(3);return f!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t}function g(t){return Math.hypot(t[0],t[1],t[2])}function x(t,e,r){var n=new f(3);return n[0]=t,n[1]=e,n[2]=r,n}function v(t,e,r){return t[0]=e[0]+r[0],t[1]=e[1]+r[1],t[2]=e[2]+r[2],t}function b(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t}function w(t,e,r){var n=e[0],i=e[1],s=e[2],a=r[0],o=r[1],l=r[2];return t[0]=i*l-s*o,t[1]=s*a-n*l,t[2]=n*o-i*a,t}Math.hypot||(Math.hypot=function(){for(var t=0,e=arguments.length;e--;)t+=arguments[e]*arguments[e];return Math.sqrt(t)});var _,S=g;function A(t,e,r){var n=e[0],i=e[1],s=e[2],a=e[3];return t[0]=r[0]*n+r[4]*i+r[8]*s+r[12]*a,t[1]=r[1]*n+r[5]*i+r[9]*s+r[13]*a,t[2]=r[2]*n+r[6]*i+r[10]*s+r[14]*a,t[3]=r[Line truncated
|
||||||
|
|
||||||
|
define("worker",["./shared"],(function(e){"use strict";class t{constructor(e){this.keyCache={},e&&this.replace(e);}replace(e){this._layerConfigs={},this._layers={},this.update(e,[]);}update(t,i){for(const i of t){this._layerConfigs[i.id]=i;const t=this._layers[i.id]=e.bJ(i);t._featureFilter=e.aa(t.filter),this.keyCache[i.id]&&delete this.keyCache[i.id];}for(const e of i)delete this.keyCache[e],delete this._layerConfigs[e],delete this._layers[e];this.familiesBySource={};const o=e.cC(Object.values(this._layerConfigs),this.keyCache);for(const e of o){const t=e.map((e=>this._layers[e.id])),i=t[0];if("none"===i.visibility)continue;const o=i.source||"";let s=this.familiesBySource[o];s||(s=this.familiesBySource[o]={});const n=i.sourceLayer||"_geojsonTileLayer";let r=s[n];r||(r=s[n]=[]),r.push(t);}}}class i{constructor(t){const i={},o=[];for(const e in t){const s=t[e],n=i[e]={};for(const e in s){const t=s[+e];if(!t||0===t.bitmap.width||0===t.bitmap.height)continue;const i={x:0,y:0,w:t.bitmap.width+2,h:t.bitmap.height+2};o.push(i),n[e]={rect:i,metrics:t.metrics};}}const{w:s,h:n}=e.p(o),r=new e.q({width:s||1,height:n||1});for(const o in t){const s=t[o];for(const t in s){const n=s[+t];if(!n||0===n.bitmap.width||0===n.bitmap.height)continue;const a=i[o][t].rect;e.q.copy(n.bitmap,r,{x:0,y:0},{x:a.x+1,y:a.y+1},n.bitmap);}}this.image=r,this.positions=i;}}e.cD("GlyphAtlas",i);class o{constructor(t){this.tileID=new e.Z(t.tileID.overscaledZ,t.tileID.wrap,t.tileID.canonical.z,t.tileID.canonical.x,t.tileID.canonical.y),this.uid=t.uid,this.zoom=t.zoom,this.pixelRatio=t.pixelRatio,this.tileSize=t.tileSize,this.source=t.source,this.overscaling=this.tileID.overscaleFactor(),this.showCollisionBoxes=t.showCollisionBoxes,this.collectResourceTiming=!!t.collectResourceTiming,this.returnDependencies=!!t.returnDependencies,this.promoteId=t.promoteId,this.inFlightDependencies=[],this.globalState=t.globalState;}parse(t,o,n,r,a){return e._(this,void 0,void 0,(function*(){this.status="parsing",this.data=t,this.collisionBoxArray=new e.a8;const l=new e.cE(Object.keys(t.layers).sort()),c=new e.cF(this.tileID,this.promoteId);c.bucketLayerIDs=[];const u={},h={featureIndex:c,iconDependencies:{},patternDependencies:{},glyphDependencies:{},availableImages:n,subdivisionGranularity:a},d=o.familiesBySource[this.source];for(const i in d){const o=t.layers[i];if(!o)continue;1===o.version&&e.w(`Vector tile source "${this.source}" layer "${i}" does not use vector tile spec v2 and therefore may have some rendering errors.`);const r=l.encode(i),a=[];for(let e=0;e<o.length;e++){const t=o.feature(e),s=c.getId(t,i);a.push({feature:t,id:s,index:e,sourceLayerIndex:r});}for(const t of d[i]){const i=t[0];i.source!==this.source&&e.w(`layer.source = ${i.source} does not equal this.source = ${this.source}`),i.minzoom&&this.zoom<Math.floor(i.minzoom)||i.maxzoom&&this.zoom>=i.maxzoom||"none"!==i.visibility&&(s(t,this.zoom,n),(u[i.id]=i.createBucket({index:c.bucketLayerIDs.length,layers:t,zoom:this.zoom,pixelRatio:this.pixelRatio,overscaling:this.overscaling,collisionBoxArray:this.collisionBoxArray,sourceLayerIndex:r,sourceID:this.source,globalState:this.globalState})).populate(a,h,this.tileID.canonical),c.bucketLayerIDs.push(t.map((e=>e.id))));}}const f=e.bN(h.glyphDependencies,(e=>Object.keys(e).map(Number)));this.inFlightDependencies.forEach((e=>null==e?void 0:e.abort())),this.inFlightDependencies=[];let g=Promise.resolve({});if(Object.keys(f).length){const e=new AbortController;this.inFlightDependencies.push(e),g=r.sendAsync({type:"GG",data:{stacks:f,source:this.source,tileID:this.tileID,type:"glyphs"}},e);}const p=Object.keys(h.iconDependencies);let m=Promise.resolve({});if(p.length){const e=new AbortController;this.inFlightDependencies.push(e),m=r.sendAsync({type:"GI",data:{icons:p,source:this.source,tileID:this.tileID,type:"icons"}},e);}const y=Object.keys(h.patternDependencies);let v=Promise.resolve({});if(y.length){const e=new AbortController;this.inFlightDependencies.push(e),v=r.sendAsync({type:"GI",data:{icons:y,source:this.source,tileID:this.tileID,type:"patterns"}},e);}const[w,x,b]=yield Promise.all([g,m,v]),S=new i(w),M=new e.cG(x,b);for(const t in u){const i=u[t];i instanceof e.a9?(s(i.layers,this.zoom,n),e.cH({bucket:i,glyphMap:w,glyphPositions:S.positions,imageMap:x,imagePositions:M.iconPositions,showCollisionBoxes:this.showCollisionBoxes,canonical:this.tileID.canonical,subdivisionGranularity:h.subdivisionGranularity})):i.hasPattern&&(i instanceof e.cI||i instanceof e.cJ||i instanceof e.cK)&&(s(i.layers,this.zoom,n),i.addFeatures(h,this.tileID.canonical,M.patternPositions));}return this.status="done",{buckets:Object.values(u).filter((e=>!e.isEmpty())),featureIndex:c,collisionBoxArray:this.collisionBoxArray,glyphAtlasImage:S.image,imageAtlas:M,glyphMap:this.returnDependencies?w:null,iconMap:this.returnDependencies?x:null,glyphPositions:this.returnDependencies?S.positions:null}}))}}function s(t,i,o){const s=new e.F(i);for(const e of t)e.recalculate(s,o);}cLine truncated
|
||||||
|
|
||||||
|
define("index",["exports","./shared"],(function(e,t){"use strict";var i="5.6.2";function o(){var e=new t.A(4);return t.A!=Float32Array&&(e[1]=0,e[2]=0),e[0]=1,e[3]=1,e}let r,a;const s={now:"undefined"!=typeof performance&&performance&&performance.now?performance.now.bind(performance):Date.now.bind(Date),frame(e,i,o){const r=requestAnimationFrame((e=>{a(),i(e);})),{unsubscribe:a}=t.s(e.signal,"abort",(()=>{a(),cancelAnimationFrame(r),o(t.c());}),!1);},frameAsync(e){return new Promise(((t,i)=>{this.frame(e,t,i);}))},getImageData(e,t=0){return this.getImageCanvasContext(e).getImageData(-t,-t,e.width+2*t,e.height+2*t)},getImageCanvasContext(e){const t=window.document.createElement("canvas"),i=t.getContext("2d",{willReadFrequently:!0});if(!i)throw new Error("failed to create canvas 2d context");return t.width=e.width,t.height=e.height,i.drawImage(e,0,0,e.width,e.height),i},resolveURL:e=>(r||(r=document.createElement("a")),r.href=e,r.href),hardwareConcurrency:"undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,get prefersReducedMotion(){return !!matchMedia&&(null==a&&(a=matchMedia("(prefers-reduced-motion: reduce)")),a.matches)}};class n{static testProp(e){if(!n.docStyle)return e[0];for(let t=0;t<e.length;t++)if(e[t]in n.docStyle)return e[t];return e[0]}static create(e,t,i){const o=window.document.createElement(e);return void 0!==t&&(o.className=t),i&&i.appendChild(o),o}static createNS(e,t){return window.document.createElementNS(e,t)}static disableDrag(){n.docStyle&&n.selectProp&&(n.userSelect=n.docStyle[n.selectProp],n.docStyle[n.selectProp]="none");}static enableDrag(){n.docStyle&&n.selectProp&&(n.docStyle[n.selectProp]=n.userSelect);}static setTransform(e,t){e.style[n.transformProp]=t;}static addEventListener(e,t,i,o={}){e.addEventListener(t,i,"passive"in o?o:o.capture);}static removeEventListener(e,t,i,o={}){e.removeEventListener(t,i,"passive"in o?o:o.capture);}static suppressClickInternal(e){e.preventDefault(),e.stopPropagation(),window.removeEventListener("click",n.suppressClickInternal,!0);}static suppressClick(){window.addEventListener("click",n.suppressClickInternal,!0),window.setTimeout((()=>{window.removeEventListener("click",n.suppressClickInternal,!0);}),0);}static getScale(e){const t=e.getBoundingClientRect();return {x:t.width/e.offsetWidth||1,y:t.height/e.offsetHeight||1,boundingClientRect:t}}static getPoint(e,i,o){const r=i.boundingClientRect;return new t.P((o.clientX-r.left)/i.x-e.clientLeft,(o.clientY-r.top)/i.y-e.clientTop)}static mousePos(e,t){const i=n.getScale(e);return n.getPoint(e,i,t)}static touchPos(e,t){const i=[],o=n.getScale(e);for(let r=0;r<t.length;r++)i.push(n.getPoint(e,o,t[r]));return i}static mouseButton(e){return e.button}static remove(e){e.parentNode&&e.parentNode.removeChild(e);}static sanitize(e){const t=(new DOMParser).parseFromString(e,"text/html").body||document.createElement("body"),i=t.querySelectorAll("script");for(const e of i)e.remove();return n.clean(t),t.innerHTML}static isPossiblyDangerous(e,t){const i=t.replace(/\s+/g,"").toLowerCase();return !(!["src","href","xlink:href"].includes(e)||!i.includes("javascript:")&&!i.includes("data:"))||!!e.startsWith("on")||void 0}static clean(e){const t=e.children;for(const e of t)n.removeAttributes(e),n.clean(e);}static removeAttributes(e){for(const{name:t,value:i}of e.attributes)n.isPossiblyDangerous(t,i)&&e.removeAttribute(t);}}n.docStyle="undefined"!=typeof window&&window.document&&window.document.documentElement.style,n.selectProp=n.testProp(["userSelect","MozUserSelect","WebkitUserSelect","msUserSelect"]),n.transformProp=n.testProp(["transform","WebkitTransform"]);const l={supported:!1,testSupport:function(e){!u&&h&&(d?_(e):c=e);}};let c,h,u=!1,d=!1;function _(e){const t=e.createTexture();e.bindTexture(e.TEXTURE_2D,t);try{if(e.texImage2D(e.TEXTURE_2D,0,e.RGBA,e.RGBA,e.UNSIGNED_BYTE,h),e.isContextLost())return;l.supported=!0;}catch(e){}e.deleteTexture(t),u=!0;}var p;"undefined"!=typeof document&&(h=document.createElement("img"),h.onload=()=>{c&&_(c),c=null,d=!0;},h.onerror=()=>{u=!0,c=null;},h.src="data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAQAAAAfQ//73v/+BiOh/AAA="),function(e){let i,o,r,a;e.resetRequestQueue=()=>{i=[],o=0,r=0,a={};},e.addThrottleControl=e=>{const t=r++;return a[t]=e,t},e.removeThrottleControl=e=>{delete a[e],n();},e.getImage=(e,o,r=!0)=>new Promise(((a,s)=>{l.supported&&(e.headers||(e.headers={}),e.headers.accept="image/webp,*/*"),t.e(e,{type:"image"}),i.push({abortController:o,requestParameters:e,supportImageRefresh:r,state:"queued",onError:e=>{s(e);},onSuccess:e=>{a(e);}}),n();}));const s=e=>t._(this,void 0,void 0,(function*(){e.state="running";const{requestParameters:i,supportImageRefresh:r,onError:a,onSuccess:s,abortController:l}=e,h=!1===r&&!t.i(self)&&!t.g(i.url)&&(!i.headers||Object.keys(i.headers).reduce(((e,t)=>e&&"accept"===t),!0));o++;const u=h?c(i,l):t.m(i,l);try{const i=yield u;delete e.abortController,e.state="completed",i.data instanceof HTMLImageElement||t.b(i.data)?s(Line truncated
|
||||||
|
|
||||||
|
//
|
||||||
|
// Our custom intro provides a specialized "define()" function, called by the
|
||||||
|
// AMD modules below, that sets up the worker blob URL and then executes the
|
||||||
|
// main module, storing its exported value as 'maplibregl'
|
||||||
|
|
||||||
|
|
||||||
|
var maplibregl$1 = maplibregl;
|
||||||
|
|
||||||
|
return maplibregl$1;
|
||||||
|
|
||||||
|
}));
|
||||||
|
//# sourceMappingURL=maplibre-gl.js.map
|
||||||
Reference in new issue
Block a user