parent
							
								
									e0e4f6fb38
								
							
						
					
					
						commit
						3fb4afca42
					
				@ -0,0 +1,143 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env julia
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					using DataFrames
 | 
				
			||||||
 | 
					using Dates
 | 
				
			||||||
 | 
					using CSV
 | 
				
			||||||
 | 
					using EzXML
 | 
				
			||||||
 | 
					using Images
 | 
				
			||||||
 | 
					using Colors
 | 
				
			||||||
 | 
					using ImageDraw
 | 
				
			||||||
 | 
					using CairoMakie
 | 
				
			||||||
 | 
					using Printf
 | 
				
			||||||
 | 
					using TimeZones
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function get_station_lat_lon(station_id)
 | 
				
			||||||
 | 
						stations = readxml("activestations.xml")
 | 
				
			||||||
 | 
						station_node = findfirst("/stations/station[@id=\"$station_id\"]", stations)
 | 
				
			||||||
 | 
						return parse.(Float32, [station_node["lat"], station_node["lon"]])
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tryparsem(T, str) = something(tryparse(T, str), missing)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function get_tile_xy_fraction(lat, lon, zoom)
 | 
				
			||||||
 | 
						n = 2^zoom
 | 
				
			||||||
 | 
						lat_rad = lat * pi / 180
 | 
				
			||||||
 | 
						xtile = (lon + 180) / 360 * n
 | 
				
			||||||
 | 
					    ytile = (1 - log(tan(lat_rad) + 1 / cos(lat_rad)) / pi) / 2 * n
 | 
				
			||||||
 | 
						return xtile, ytile
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function get_tile_xy(lat, lon, zoom)
 | 
				
			||||||
 | 
						xf, yf = get_tile_xy_fraction(lat, lon, zoom)
 | 
				
			||||||
 | 
						return (Int(floor(xf)), Int(floor(yf)))
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function get_tile(x, y, zoom)
 | 
				
			||||||
 | 
						url = "https://tile.openstreetmap.org/$zoom/$x/$y.png"
 | 
				
			||||||
 | 
						tmp_path = download(url)
 | 
				
			||||||
 | 
						tile = load(tmp_path)
 | 
				
			||||||
 | 
						return tile
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function get_tile_by_lat_lon(lat, lon, zoom)
 | 
				
			||||||
 | 
						xtile, ytile = get_tile_xy(lat, lon, zoom)
 | 
				
			||||||
 | 
						return get_tile(xtile, ytile, zoom)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function get_meters_per_pixel(lat, zoom)
 | 
				
			||||||
 | 
						px_per_tile = 256
 | 
				
			||||||
 | 
						return 156543.03 * cos(lat) / (2^zoom)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function get_map(lat, lon, zoom)
 | 
				
			||||||
 | 
						px_per_tile = 256
 | 
				
			||||||
 | 
						meters_per_pixel = get_meters_per_pixel(lat, zoom)
 | 
				
			||||||
 | 
						xf, yf = get_tile_xy_fraction(lat, lon, zoom)
 | 
				
			||||||
 | 
						x0, y0 = (Int(floor(xf)), Int(floor(yf)))
 | 
				
			||||||
 | 
						xpoint = 1 + px_per_tile + Int(floor((xf - x0) * px_per_tile))
 | 
				
			||||||
 | 
						ypoint = 1 + px_per_tile + Int(floor((yf - y0) * px_per_tile))
 | 
				
			||||||
 | 
						map = Array{RGB{N0f8}, 2}(undef, (px_per_tile * 3, px_per_tile * 3))
 | 
				
			||||||
 | 
						for i in -1:1
 | 
				
			||||||
 | 
							x = x0 + i
 | 
				
			||||||
 | 
							ioffset = 1 + (i + 1) * px_per_tile
 | 
				
			||||||
 | 
							for j in -1:1
 | 
				
			||||||
 | 
								y = y0 + j
 | 
				
			||||||
 | 
							    joffset = 1 + (j + 1) * px_per_tile
 | 
				
			||||||
 | 
								img = get_tile(x, y, zoom)
 | 
				
			||||||
 | 
								map[joffset:joffset+px_per_tile-1, ioffset:ioffset+px_per_tile-1] = img
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						draw!(map, Ellipse(CirclePointRadius(xpoint, ypoint, 6)),
 | 
				
			||||||
 | 
							  RGB{N0f8}(0.5, 0.0, 0.5))
 | 
				
			||||||
 | 
						return map, xpoint, ypoint
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function get_realtime_data_file(station)
 | 
				
			||||||
 | 
					    station = uppercase(station)
 | 
				
			||||||
 | 
						url = "http://www.ndbc.noaa.gov/data/realtime2/$station.txt"
 | 
				
			||||||
 | 
						return download(url)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function get_realtime_dataframe(station_name; timezone=tz"America/New_York")
 | 
				
			||||||
 | 
						station_fname = get_realtime_data_file(station_name)
 | 
				
			||||||
 | 
					    lines = open(station_fname) do file
 | 
				
			||||||
 | 
					        readlines(file)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    dt_array = (y, mon, d, hr, min) -> (astimezone.(ZonedDateTime.(
 | 
				
			||||||
 | 
					        DateTime.(y, mon, d, hr, min), tz"UTC"), timezone))
 | 
				
			||||||
 | 
						deg_c_to_f = passmissing(x -> x * 9.0 / 5.0 + 32.0)
 | 
				
			||||||
 | 
						speed_ms_to_mph = passmissing(x -> 0.44704 * x)
 | 
				
			||||||
 | 
					    lines[1] = lstrip(lines[1], '#')
 | 
				
			||||||
 | 
					    data = join([lines[1:1]; lines[3:end]], "\n")
 | 
				
			||||||
 | 
					    df = CSV.read(IOBuffer(data), DataFrame; header=1, delim=" ",
 | 
				
			||||||
 | 
					                  ignorerepeated=true)
 | 
				
			||||||
 | 
						select!(df,
 | 
				
			||||||
 | 
							[:YY, :MM, :DD, :hh, :mm] => dt_array => :TIME,
 | 
				
			||||||
 | 
							:WDIR => x -> tryparsem.(Int, x),
 | 
				
			||||||
 | 
							:WSPD => x -> speed_ms_to_mph.(tryparsem.(Float32, x)),
 | 
				
			||||||
 | 
							:GST => x -> speed_ms_to_mph.(tryparsem.(Float32, x)),
 | 
				
			||||||
 | 
							:PRES => x -> tryparsem.(Float32, x),
 | 
				
			||||||
 | 
							:ATMP => x -> deg_c_to_f.(tryparsem.(Float32, x)),
 | 
				
			||||||
 | 
							:WTMP => x -> deg_c_to_f.(tryparsem.(Float32, x));
 | 
				
			||||||
 | 
							renamecols=false)
 | 
				
			||||||
 | 
						return df
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function plot_station_data(station_name, station_map, station_x, station_y, df, i)
 | 
				
			||||||
 | 
						obs_time = df.TIME[i]
 | 
				
			||||||
 | 
						fig, ax, p = image(rotr90(station_map),
 | 
				
			||||||
 | 
							               axis=(aspect=DataAspect(),
 | 
				
			||||||
 | 
					                             title="$station_name $obs_time"))
 | 
				
			||||||
 | 
					    hidespines!(ax)
 | 
				
			||||||
 | 
					    hidedecorations!(ax)
 | 
				
			||||||
 | 
						wind_rad = deg2rad(90 - df.WDIR[1])
 | 
				
			||||||
 | 
						wind_mph = df.WSPD[i]
 | 
				
			||||||
 | 
						txt = @sprintf "Air: %.1f F, Water: %.1f F, Wind: %.1f mph" df.ATMP[i] df.WTMP[i] wind_mph
 | 
				
			||||||
 | 
						wind_vec = 50 * Vec2f(cos(wind_rad), sin(wind_rad)) * wind_mph / 10
 | 
				
			||||||
 | 
						tooltip!(ax, station_x, station_y, txt; offset=10)
 | 
				
			||||||
 | 
						arrows!(ax, [Point2f(station_x, station_y)], [wind_vec], fxaa=true,
 | 
				
			||||||
 | 
						        linewidth=2, align=:center, arrowhead='⟰')
 | 
				
			||||||
 | 
						return fig
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function main()
 | 
				
			||||||
 | 
					    if (length(ARGS) < 1)
 | 
				
			||||||
 | 
					        println("Usage: create_latest_map.jl station_name [out_path]")
 | 
				
			||||||
 | 
					        exit(1)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    station_name = ARGS[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    out_fname = "$station_name.png"
 | 
				
			||||||
 | 
					    if (length(ARGS) > 1)
 | 
				
			||||||
 | 
					        out_fname = ARGS[2]
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    lat, lon = get_station_lat_lon(station_name)
 | 
				
			||||||
 | 
						station_map, station_x, station_y = get_map(lat, lon, 14)
 | 
				
			||||||
 | 
					    df = get_realtime_dataframe(station_name; timezone=tz"America/New_York")
 | 
				
			||||||
 | 
					    fig = plot_station_data(station_name, station_map, station_x, station_y,
 | 
				
			||||||
 | 
					                            df, 1)
 | 
				
			||||||
 | 
					    save("$station_name.png", fig)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					main()
 | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue