Files
test/ohcam.py
2025-11-27 22:25:36 +00:00

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()