Skip to content

Instantly share code, notes, and snippets.

@quephird
Created February 19, 2013 20:30
Show Gist options
  • Save quephird/4989645 to your computer and use it in GitHub Desktop.
Save quephird/4989645 to your computer and use it in GitHub Desktop.
This isn't exactly the most useful code in the world but I was annoyed that I couldn't save satellite imagery on the NOAA site as a local video file. I know... first world problems. Anyway... after spelunking in the browser source, I saw that the images are fetched individually by either a Java or Flash applet. I figured that this was a good opp…
(ns animated-gif-from-noaa-images
(:require [clojure.string :as string]
[clj-http.client :as client])
(:import [java.io File]
[java.net URL]
[java.awt.image BufferedImage]
[javax.imageio IIOImage ImageIO ImageTypeSpecifier ImageWriter ImageWriteParam]
[javax.imageio.metadata IIOMetadata IIOMetadataNode]))
; Satellite image names are listed in the vis_names.txt file
(defn get-satellite-image-urls [partial-url]
(->> (str partial-url "txtfiles/vis_names.txt")
client/get
:body
string/split-lines
(map #(string/split % #"\s+"))
(map first)
(map #(str partial-url %))))
(defn create-image-output-stream [file-name]
(ImageIO/createImageOutputStream (File. file-name)))
(defn read-image [url]
(ImageIO/read (URL. url)))
(defn get-gif-writer [specifier]
(.next (ImageIO/getImageWriters specifier "GIF")))
(defn make-metadata-node [name]
(IIOMetadataNode. name))
(defn make-delay-node [time]
"Delay time is expressed in hundredths of a second."
(doto (make-metadata-node "GraphicControlExtension")
(.setAttribute "disposalMethod" "none")
(.setAttribute "userInputFlag" "FALSE")
(.setAttribute "transparentColorFlag" "FALSE")
(.setAttribute "delayTime" (str time))
(.setAttribute "transparentColorIndex" "255")))
(defn make-loop-node [loop-count]
"Loop count of 0 => loop indefinitely; 1-65535 => loop that many times."
(doto (make-metadata-node "ApplicationExtension")
(.setAttribute "applicationID" "NETSCAPE")
(.setAttribute "authenticationCode" "2.0")
(.setUserObject
(byte-array
(map unchecked-byte
[0x01
(bit-and loop-count 0xff)
(bit-and (bit-shift-right loop-count 8) 0xff)])))))
(defn make-app-ext-node [loop-node]
(doto (make-metadata-node "ApplicationExtensions")
(.appendChild loop-node)))
(defn add-node! [parent-node child-node]
(.appendChild parent-node child-node))
(defn set-from-tree! [metadata format-name root-node]
(.setFromTree metadata format-name root-node))
(defn set-output! [writer output-stream]
(.setOutput writer output-stream))
(defn prepare-write-sequence! [writer metadata]
(.prepareWriteSequence writer metadata))
(defn write-to-sequence! [writer image-url metadata param]
(.writeToSequence writer (IIOImage. (read-image image-url) nil metadata) param))
(defn make-animated-gif! [image-urls delay-time loop-count output-file-name]
(let [first-image (read-image (first image-urls))
image-output-stream (create-image-output-stream output-file-name)
specifier (ImageTypeSpecifier. first-image)
writer (get-gif-writer specifier)
param (.getDefaultWriteParam writer)
metadata (.getDefaultImageMetadata writer specifier param)
format-name (.getNativeMetadataFormatName metadata)
root-node (.getAsTree metadata format-name)
delay-node (make-delay-node delay-time)
loop-node (make-loop-node loop-count)
app-ext-node (make-app-ext-node loop-node)
]
(add-node! root-node delay-node)
(add-node! root-node app-ext-node)
(set-from-tree! metadata format-name root-node)
(set-output! writer image-output-stream)
(prepare-write-sequence! writer metadata)
(doseq [image-url image-urls]
(write-to-sequence! writer image-url metadata param))
)
)
(make-animated-gif! (get-satellite-image-urls "http://www.ssd.noaa.gov/goes/east/eaus/")
50
0
"latest-eastern-us-satellite.gif")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment