|
| 1 | +#!/usr/bin/env python |
| 2 | +# -*- coding: utf-8 -*- |
| 3 | +import json |
| 4 | +import requests |
| 5 | +import os |
| 6 | +import sys |
| 7 | +from app import app |
| 8 | +from shapely.geometry import LineString |
| 9 | +from status.profile_plots import iter_deployments, is_recent_data, is_recent_update |
| 10 | + |
| 11 | + |
| 12 | +def get_trajectory(erddap_url): |
| 13 | + ''' |
| 14 | + Reads the trajectory information from ERDDAP and returns a GEOJSON like |
| 15 | + structure. |
| 16 | + ''' |
| 17 | + # https://gliders.ioos.us/erddap/tabledap/ru01-20140104T1621.json?latitude,longitude&time&orderBy(%22time%22) |
| 18 | + url = erddap_url.replace('html', 'json') |
| 19 | + # ERDDAP requires the variable being sorted to be present in the variable |
| 20 | + # list. The time variable will be removed before converting to GeoJSON |
| 21 | + url += '?longitude,latitude,time&orderBy(%22time%22)' |
| 22 | + response = requests.get(url, timeout=180) |
| 23 | + if response.status_code != 200: |
| 24 | + raise IOError("Failed to fetch trajectories: {}".format(erddap_url)) |
| 25 | + data = response.json() |
| 26 | + geo_data = { |
| 27 | + 'type': 'LineString', |
| 28 | + 'coordinates': [c[0:2] for c in data['table']['rows']] |
| 29 | + } |
| 30 | + |
| 31 | + geometry = parse_geometry(geo_data) |
| 32 | + coords = LineString(geometry['coordinates']) |
| 33 | + trajectory = coords.simplify(0.02, preserve_topology=False) |
| 34 | + geometry = { |
| 35 | + 'type': 'LineString', |
| 36 | + 'coordinates': list(trajectory.coords), |
| 37 | + 'properties': { |
| 38 | + 'oceansmap_type': 'glider' |
| 39 | + } |
| 40 | + } |
| 41 | + return geometry |
| 42 | + |
| 43 | + |
| 44 | +def get_path(deployment): |
| 45 | + ''' |
| 46 | + Returns the path to the trajectory file |
| 47 | +
|
| 48 | + :param dict deployment: Dictionary containing the deployment metadata |
| 49 | + ''' |
| 50 | + trajectory_dir = app.config.get('TRAJECTORY_DIR') |
| 51 | + username = deployment['username'] |
| 52 | + name = deployment['name'] |
| 53 | + dir_path = os.path.join(trajectory_dir, username) |
| 54 | + if not os.path.exists(dir_path): |
| 55 | + os.makedirs(dir_path) |
| 56 | + file_path = os.path.join(dir_path, name + '.json') |
| 57 | + return file_path |
| 58 | + |
| 59 | + |
| 60 | +def write_trajectory(deployment, geo_data): |
| 61 | + ''' |
| 62 | + Writes a geojson like python structure to the appropriate data file |
| 63 | +
|
| 64 | + :param dict deployment: Dictionary containing the deployment metadata |
| 65 | + :param dict geometry: A GeoJSON Geometry object |
| 66 | + ''' |
| 67 | + file_path = get_path(deployment) |
| 68 | + with open(file_path, 'w') as f: |
| 69 | + f.write(json.dumps(geo_data)) |
| 70 | + |
| 71 | + |
| 72 | +def parse_geometry(geometry): |
| 73 | + ''' |
| 74 | + Filters out potentially bad coordinate pairs as returned from |
| 75 | + GliderDAC. Returns a safe geometry object. |
| 76 | +
|
| 77 | + :param dict geometry: A GeoJSON Geometry object |
| 78 | + ''' |
| 79 | + coords = [] |
| 80 | + for lon, lat in geometry['coordinates']: |
| 81 | + if lon is None or lat is None: |
| 82 | + continue |
| 83 | + coords.append([lon, lat]) |
| 84 | + return {'coordinates': coords} |
| 85 | + |
| 86 | + |
| 87 | +def trajectory_exists(deployment): |
| 88 | + ''' |
| 89 | + Returns True if the data is within the last week |
| 90 | +
|
| 91 | + :param dict deployment: Dictionary containing the deployment metadata |
| 92 | + ''' |
| 93 | + |
| 94 | + file_path = get_path(deployment) |
| 95 | + return os.path.exists(file_path) |
| 96 | + |
| 97 | + |
| 98 | +def generate_trajectories(deployments=None): |
| 99 | + ''' |
| 100 | + Determine which trajectories need to be built, and write geojson to file |
| 101 | + ''' |
| 102 | + for deployment in iter_deployments(): |
| 103 | + try: |
| 104 | + # Only add if the deployment has been recently updated or the data is recent |
| 105 | + recent_update = is_recent_update(deployment['updated']) |
| 106 | + recent_data = is_recent_data(deployment) |
| 107 | + existing_trajectory = trajectory_exists(deployment) |
| 108 | + if (recent_update or recent_data or not existing_trajectory): |
| 109 | + geo_data = get_trajectory(deployment['erddap']) |
| 110 | + write_trajectory(deployment, geo_data) |
| 111 | + except Exception: |
| 112 | + from traceback import print_exc |
| 113 | + print_exc() |
| 114 | + return 0 |
| 115 | + |
| 116 | + |
| 117 | +if __name__ == '__main__': |
| 118 | + from argparse import ArgumentParser |
| 119 | + parser = ArgumentParser(description=generate_trajectories.__doc__) |
| 120 | + parser.add_argument( |
| 121 | + '-d', '--deployment', |
| 122 | + action='append', |
| 123 | + help='Which deployment to build' |
| 124 | + ) |
| 125 | + args = parser.parse_args() |
| 126 | + sys.exit(generate_trajectories(args.deployment)) |
0 commit comments