348 lines
12 KiB
Python
348 lines
12 KiB
Python
import time
|
|
import requests
|
|
import json
|
|
import psycopg2
|
|
import psycopg2.extensions
|
|
from psycopg2.extras import Json, execute_values
|
|
import re
|
|
|
|
conn = psycopg2.connect(host='127.0.0.1', database='nws', user='nws', password='nws')
|
|
cursor = conn.cursor()
|
|
|
|
ohcams = 'https://api.ohgo.com/roadmarkers/cameras?pointData={%22lowLongitude%22:-83.85946954181879,%22highLongitude%22:-77.00743340900629,%22lowLatitude%22:37.434082799235426,%22highLatitude%22:40.20012906764399,%22routeDirection%22:%22%22,%22routeName%22:%22%22}'
|
|
|
|
response = json.loads(requests.get(ohcams).text)
|
|
tings = []
|
|
for p in response:
|
|
tings = [p['Cameras'][0]['Direction'], p['Cameras'][0]['LargeURL'], p['Latitude'], p['Longitude'],p['Description'],'true','normalproxy','10','https://ohgo.com']
|
|
sql = 'INSERT INTO cams (aspect, url, lat, lon, description, active, method, interval,source) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s) ON CONFLICT DO NOTHING;'
|
|
cursor.execute(sql,tings)
|
|
conn.commit()
|
|
|
|
|
|
|
|
|
|
wvids = 'https://wv511.org/wsvc/gmap.asmx/buildCamerasJSONjs'
|
|
|
|
wvheaders = {'Connection': 'keep-alive',
|
|
'Accept': 'application/json',
|
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
|
|
'X-HTTP-Method-Override': 'GET',
|
|
'Referer': 'http://wv511.org/',
|
|
'Accept-Language': 'en-US,en;q=0.9',
|
|
'Cookie': 'mapExtent=-86.271172363286%3A35.832853816094%3A-74.92229541016%3A41.956945104642'}
|
|
|
|
r = requests.get(wvids,headers=wvheaders)
|
|
x = r.text
|
|
potato = re.findall(r'var camera_data = { "count": 118,(.*?}])?', x, re.DOTALL | re.MULTILINE)
|
|
potato = re.findall(r' "cams":.(.*?}])?', x, re.DOTALL | re.MULTILINE)
|
|
potatoer = json.loads(potato[0])
|
|
|
|
|
|
wvcams = []
|
|
|
|
|
|
#print(potatoer)
|
|
for i in potatoer:
|
|
try:
|
|
lat = i['start_lat']
|
|
lon = i['start_lng']
|
|
url = str('https://sfstest.roadsummary.com/rtplive/' + str(i['md5'])+'/playlist.m3u8')
|
|
description = re.findall(r'<div id="camDescription">(.*?)<span?',i['description'])[0]
|
|
tings = [description,lat,lon,url,'rtsp','true','10','http://wv511.org']
|
|
sql = 'INSERT INTO cams (description, lat, lon, url, method, active, interval,source) VALUES (%s,%s,%s,%s,%s,%s,%s,%s) ON CONFLICT DO NOTHING;'
|
|
cursor.execute(sql,tings)
|
|
conn.commit()
|
|
except Exception as e:
|
|
print(e,url)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#Bounding box of CWA
|
|
|
|
x101 = 137
|
|
x102 = 143
|
|
y101 = 194
|
|
y102 = 200
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def bloomsky():
|
|
|
|
(xlat,xlon,nlat,nlon) = (40.000897, -77.513573, 36.749991, -83.098124)
|
|
cursor.execute("UPDATE cams set bloomsky = url where method = 'bloomsky' and bloomsky is null")
|
|
conn.commit()
|
|
cursor.execute("SELECT camid, bloomsky from cams where method = 'bloomsky' and active = True")
|
|
bloomcams = cursor.fetchall()
|
|
bloomurl = 'https://map.bloomsky.com/data/device/'
|
|
bloomheaders = {'Connection': 'keep-alive',
|
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
|
|
'Content-Type': 'application/json; charset=UTF-8',
|
|
'Accept': '*/*',
|
|
'Origin': 'https://map.bloomsky.com',
|
|
'Sec-Fetch-Site': 'same-origin',
|
|
'Sec-Fetch-Mode': 'cors',
|
|
'Sec-Fetch-Dest': 'empty',
|
|
'X-HTTP-Method-Override': 'POST',
|
|
'Host': 'map.bloomsky.com',
|
|
'Referer': 'https://map.bloomsky.com',
|
|
'Accept-Language': 'en-US,en;q=0.9'}
|
|
#'Cookie': 'AWSELB=A38D8DDD18933E21FE3ADA5953BE34121339A3A57D7398DD0E6A3F21299C92AD55655B00C912D9CF7F381931286A93623AAED145BC6DAEC803C16F67CC4FA4015E6CF710F2; AWSELBCORS=A38D8DDD18933E21FE3ADA5953BE34121339A3A57D7398DD0E6A3F21299C92AD55655B00C912D9CF7F381931286A93623AAED145BC6DAEC803C16F67CC4FA4015E6CF710F2'
|
|
|
|
|
|
|
|
bloompost = json.dumps({"type":"ALL"})
|
|
r = requests.post(bloomurl,headers=bloomheaders,data=bloompost)
|
|
# print(r)
|
|
bloomcameras = r.json()
|
|
if r.status_code == 200:
|
|
for p in bloomcameras:
|
|
lat = bloomcameras[p]['lat']
|
|
lon = bloomcameras[p]['lng']
|
|
if (lat < xlat and lat > nlat and lon < xlon and lon > nlon):
|
|
tings = [bloomcameras[p]['img'], bloomcameras[p]['city'],bloomcameras[p]['name'],lat,lon,p,'10','True','240','bloomsky','https://map.bloomsky.com/weather-stations/' + str(p)]
|
|
sql = "INSERT INTO cams (url,town,description,lat,lon,bloomsky,interval,active,keephours, method,source) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) ON CONFLICT DO NOTHING"
|
|
cursor.execute(sql,tings)
|
|
|
|
|
|
conn.commit()
|
|
|
|
|
|
|
|
def ambient():
|
|
url = "https://lightning.ambientweather.net/devices?%24publicBox[0][0]=-83.144531&%24publicBox[0][1]=36.72&%24publicBox[1][0]=-75.5647&%24publicBox[1][1]=40.33802332110745&%24limit=1000&rank=2"
|
|
r = requests.get(url)
|
|
x = r.json()
|
|
for i in x['data']:
|
|
if (i.get('info').get('webcam') != None and "jpg" in i.get('info').get('webcam')):
|
|
description = i.get('info').get('coords').get('location')
|
|
lat = i.get('info').get('coords').get('coords').get('lat')
|
|
lon = i.get('info').get('coords').get('coords').get('lon')
|
|
url = i.get('info').get('webcam')
|
|
|
|
|
|
try:
|
|
tings = [description,lat,lon,url,'normal','true','10','https://ambientweather.net/']
|
|
sql = 'INSERT INTO cams (description, lat, lon, url, method, active, interval,source) VALUES (%s,%s,%s,%s,%s,%s,%s,%s) ON CONFLICT DO NOTHING;'
|
|
cursor.execute(sql,tings)
|
|
except Exception as e:
|
|
print(e)
|
|
conn.commit()
|
|
|
|
ambient()
|
|
|
|
|
|
|
|
def kymeso():
|
|
ky = 'https://www.kymesonet.org/json/StationsInfo.json'
|
|
response = json.loads(requests.get(ky).text)
|
|
tings = []
|
|
base = 'https://www.kymesonet.org/json/appSiteCam/'
|
|
suff = '.jpg'
|
|
for key,value in response.items():
|
|
if value.get('isDiscontinued') == False:
|
|
tings = [base + value.get('abbr')+ suff, value.get('lat'),value.get('lon'),value.get('name'),'true','normal','10','https://www.kymesonet.org/']
|
|
sql = 'INSERT INTO cams (url, lat, lon, description, active, method, interval,source) VALUES (%s,%s,%s,%s,%s,%s,%s,%s) ON CONFLICT DO NOTHING;'
|
|
# print(tings)
|
|
cursor.execute(sql,tings)
|
|
conn.commit()
|
|
|
|
kymeso()
|
|
|
|
|
|
def ky511cams():
|
|
"""
|
|
Fetches traffic camera data, filters it, and performs an efficient
|
|
bulk insert into the database.
|
|
"""
|
|
# Define constants for clarity and easy maintenance
|
|
API_URL = 'https://services2.arcgis.com/CcI36Pduqd0OR4W9/ArcGIS/rest/services/trafficCamerasCur_Prd/FeatureServer/0/query?where=1%3D1&outFields=name%2C+description%2C+snapshot%2C+status%2C+latitude%2C+longitude%2C+county&returnGeometry=false&f=pjson'
|
|
|
|
# Define the SQL template with placeholders for the values
|
|
SQL_INSERT = """
|
|
INSERT INTO cams (description, lat, lon, url, active, method, interval, source)
|
|
VALUES %s
|
|
ON CONFLICT DO NOTHING;
|
|
"""
|
|
|
|
try:
|
|
# 1. Use .json() for efficiency and add a timeout for safety
|
|
response = requests.get(API_URL, timeout=15)
|
|
response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx)
|
|
data = response.json()
|
|
except requests.exceptions.RequestException as e:
|
|
print(f"Error fetching data from API: {e}")
|
|
return
|
|
except json.JSONDecodeError:
|
|
print("Error: Failed to decode JSON from response.")
|
|
return
|
|
|
|
# 2. Use a list comprehension to process and filter data concisely
|
|
cams_to_insert = [
|
|
(
|
|
cam.get('description'),
|
|
cam.get('latitude'),
|
|
cam.get('longitude'),
|
|
cam.get('snapshot'),
|
|
True, # active
|
|
'normalproxy', # method
|
|
10, # interval
|
|
'https://goky.ky.gov/' # source
|
|
)
|
|
for feature in data.get('features', [])
|
|
if (cam := feature.get('attributes')) and cam.get('longitude') > -83.8
|
|
]
|
|
|
|
if not cams_to_insert:
|
|
print("No new camera data meeting the criteria to insert.")
|
|
return
|
|
|
|
try:
|
|
# 3. Use a single, highly efficient bulk insert operation
|
|
execute_values(cursor, SQL_INSERT, cams_to_insert)
|
|
conn.commit()
|
|
print(f"✅ Successfully processed and inserted {len(cams_to_insert)} records.")
|
|
except Exception as e:
|
|
print(f"❌ Database error occurred: {e}")
|
|
conn.rollback() # Roll back the transaction on error
|
|
|
|
|
|
ky511cams()
|
|
|
|
|
|
|
|
#bloomsky()
|
|
|
|
|
|
|
|
headers = {
|
|
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def get_elev(lon, lat):
|
|
"""Gets elevation from the local database first, then falls back to a web API."""
|
|
try:
|
|
# Primary method: Local PostGIS database
|
|
sql = """
|
|
SELECT round(ST_Value(rast, ST_SetSRID(ST_MakePoint(%s,%s),4326)) * 3.28084) AS val
|
|
FROM dem
|
|
WHERE ST_Intersects(rast, ST_SetSRID(ST_MakePoint(%s,%s),4326));
|
|
"""
|
|
vars_tuple = (lon, lat, lon, lat)
|
|
cursor.execute(sql, vars_tuple)
|
|
result = cursor.fetchone()
|
|
|
|
# Check if the database returned a valid, non-zero elevation
|
|
if result and result[0] is not None and result[0] != 0:
|
|
return int(result[0])
|
|
|
|
except Exception as e:
|
|
# Log the database error if needed
|
|
print(f"Database error: {e}")
|
|
|
|
# Fallback method: Web API
|
|
# This part runs if the try block fails or returns no valid data
|
|
elev = get_elev_backup(lon, lat)
|
|
return int(elev) if elev is not None else None
|
|
|
|
|
|
def get_elev_backup(lon, lat):
|
|
"""Backup function to get elevation from the National Map API."""
|
|
baseurl = 'https://epqs.nationalmap.gov/v1/json'
|
|
params = {
|
|
'x': lon,
|
|
'y': lat,
|
|
'units': 'Feet',
|
|
'wkid': 4326,
|
|
'includeDate': False
|
|
}
|
|
print(params)
|
|
try:
|
|
# The timeout prevents the request from hanging indefinitely
|
|
r = requests.get(baseurl, params=params, timeout=5)
|
|
# Raise an exception for bad status codes (4xx or 5xx)
|
|
r.raise_for_status()
|
|
|
|
# Attempt to parse JSON and get the value
|
|
data = r.json()
|
|
elev = data.get('value')
|
|
|
|
# The API may return a string 'no-data' for some locations
|
|
if isinstance(elev, str) and not elev.replace('.', '', 1).isdigit():
|
|
return None
|
|
|
|
# Add a small delay to avoid hammering the API
|
|
time.sleep(1)
|
|
return float(elev)
|
|
|
|
except requests.exceptions.RequestException as e:
|
|
# Handles connection errors, timeouts, bad status codes, etc.
|
|
print(r.content)
|
|
print(f"API Request failed: {e}")
|
|
return None
|
|
except JSONDecodeError:
|
|
# Handles cases where the response is not valid JSON
|
|
print(f"Failed to decode JSON from response. Response text: {r.text}")
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cursor.execute("SELECT camid, lon, lat from cams WHERE elevation ISNULL or elevation = 0 and active = true")
|
|
allcams = cursor.fetchall()
|
|
|
|
|
|
|
|
for cam in allcams:
|
|
camid, lon, lat = cam
|
|
elev = get_elev(lon,lat)
|
|
val = (elev,camid)
|
|
sql = "UPDATE cams SET elevation= %s WHERE camid = %s"
|
|
cursor.execute(sql,val)
|
|
conn.commit()
|
|
|
|
updates = ['UPDATE public.cams SET geom = ST_SetSRID(ST_MakePoint(lon, lat), 4326)',
|
|
'UPDATE public.cams SET county = county.countyname from public.county WHERE ST_Contains(county.geom,cams.geom)',
|
|
'UPDATE public.cams SET pzone = pzone.state_zone from public.pzone WHERE ST_Contains(pzone.geom,cams.geom)',
|
|
'UPDATE public.cams SET fzone = fzone.state_zone from public.fzone WHERE ST_Contains(fzone.geom,cams.geom)',
|
|
'UPDATE public.cams SET cwa = fzone.cwa from public.fzone WHERE ST_Contains(fzone.geom,cams.geom)',
|
|
'UPDATE public.cams SET zonename = pzone.shortname from public.pzone WHERE ST_Contains(pzone.geom,cams.geom)',
|
|
'UPDATE public.cams SET keephours = 240 WHERE keephours is null',
|
|
"""UPDATE public.cams SET method = 'normal' WHERE method is null""",
|
|
'UPDATE public.cams SET state = county.state from public.county WHERE ST_Contains(county.geom,cams.geom)',
|
|
'UPDATE public.cams SET airport = EXISTS (SELECT 1 FROM public.airports WHERE ST_Intersects(cams.geom, airports.geom5)) WHERE airport IS NULL',
|
|
"UPDATE cams SET active = false FROM cwa WHERE cwa.cwa = 'RLX' AND NOT ST_DWithin(cams.geom::geography, cwa.geom::geography, 10 * 1609.344) AND cams.url ~ 'oh.us' AND cams.active = true",]
|
|
|
|
for i in updates:
|
|
cursor.execute(i)
|
|
|
|
|
|
|
|
conn.commit()
|
|
|
|
|
|
|
|
|
|
|
|
ambient()
|
|
|
|
|
|
cursor.close()
|
|
conn.close()
|