Advertisement
Guest User

3D pointcloud from earthquake data

a guest
Jul 21st, 2019
1,229
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.94 KB | None | 0 0
  1. """
  2. Required python packages:
  3.    - numpy
  4.    - matplotlib
  5.    - requests
  6.    - netCDF4
  7.    - dateutil
  8.  
  9. Download the landmask (lsmask.nc) from
  10.    https://www.esrl.noaa.gov/psd/data/gridded/data.noaa.oisst.v2.html
  11.  
  12. More info on the earthquake catalog:
  13.    https://earthquake.usgs.gov/fdsnws/event/1/
  14. """
  15.  
  16. import os
  17. import numpy as np
  18. import shutil
  19. import netCDF4
  20. import requests
  21. import pickle
  22.  
  23. from math import cos, sin, pi
  24. from matplotlib import cm
  25. from datetime import datetime
  26. from dateutil.relativedelta import relativedelta
  27.  
  28. # Parameters
  29. ENDPOINT  = "https://earthquake.usgs.gov/fdsnws/event/1/"
  30. LANDMASK  = "netcdf_data/lsmask.nc"
  31. DB_FILE   = "earthquakes.pickle"
  32. PLY_FILE  = "earthquakes.ply"
  33. MAG_RANGE = [2.5, 7]
  34. START     = datetime.strptime("2019-01-01","%Y-%m-%d")
  35. END       = datetime.strptime("2019-07-01","%Y-%m-%d")
  36.  
  37.  
  38. class PointCloud:
  39.     """Helper class for point cloud writing"""
  40.    
  41.     def __init__(self):
  42.         self.coords = []
  43.         self.colors = []
  44.         pass
  45.    
  46.     def write(self, path):
  47.         with open(path, "w") as f:
  48.            
  49.             # Do we write the colours?
  50.             write_colors = len(self.colors) == len(self.coords)
  51.            
  52.             # Write the header
  53.             f.write("ply\nformat ascii 1.0\n")
  54.             f.write("element vertex %d\n" % len(self.coords))
  55.             f.write("property float x\n")
  56.             f.write("property float y\n")
  57.             f.write("property float z\n")
  58.             if write_colors:
  59.                 f.write("property uchar red\n")
  60.                 f.write("property uchar green\n")
  61.                 f.write("property uchar blue\n")
  62.             f.write("end_header\n")
  63.            
  64.             # Write the elements
  65.             if write_colors:
  66.                 for coord, color in zip(self.coords, self.colors):
  67.                     f.write("%f %f %f %d %d %d\n" % (
  68.                         coord[0],
  69.                         coord[1],
  70.                         coord[2],
  71.                         color[0],
  72.                         color[1],
  73.                         color[2]
  74.                     ))
  75.             else:
  76.                 for coord in self.coords:
  77.                     f.write("%f %f %f\n" % (
  78.                         coord[0],
  79.                         coord[1],
  80.                         coord[2]
  81.                     ))
  82.  
  83. def polar_to_xyz(lon, lat, depth):
  84.     """Convert lat lon and depth to polar coordinates"""
  85.    
  86.     C = (0, 0, 0) # Center
  87.     S = 0.01      # Scale factor
  88.     D = 3         # Depth exageration
  89.     R = 6400      # Earth radius
  90.  
  91.     # Conversion to radians then cartesian coordinates
  92.     rad_lat, rad_lon = lat * pi / 180, lon * pi / 180
  93.     x = C[0] + S * (R-D*depth) * cos(rad_lat) * cos(rad_lon)
  94.     y = C[1] + S * (R-D*depth) * cos(rad_lat) * sin(rad_lon)
  95.     z = C[2] + S * (R-D*depth) * sin(rad_lat)
  96.    
  97.     return (x, y, z)
  98.  
  99. def colormap(x, mini, maxi, cmap=cm.inferno):
  100.     """Apply a colormap to a value according to a min/max range"""
  101.    
  102.     fac = (x-mini)/(maxi-mini) # From 0 to 1
  103.     c = cmap( int(255*fac) )
  104.    
  105.     return (int(255*c[0]), int(255*c[1]), int(255*c[2]))
  106.  
  107. def get_landmask():
  108.     """Return a landmask from a netcdf file"""
  109.     data = netCDF4.Dataset(LANDMASK)
  110.    
  111.     LATS, LONS       = np.meshgrid(data.variables["lon"], data.variables["lat"])
  112.     LATS, LONS       = LATS.ravel(), LONS.ravel()
  113.     MASKS            = data.variables["mask"][0].ravel()
  114.     MASKS[LONS<=-84] = 1 # Remove the bottom of Antarctica (mask=1)
  115.     lats, lons       = LATS[MASKS==0], LONS[MASKS==0]
  116.        
  117.     data.close()
  118.  
  119.     return lons, lats
  120.      
  121. def make_request(method, t_range, m_range):
  122.    
  123.     # Create the get request parameters
  124.     request_params = {
  125.         "minmagnitude" : m_range[0],
  126.         "maxmagnitude" : m_range[1],
  127.         "starttime"    : t_range[0],
  128.         "endtime"      : t_range[1],
  129.         "format"       : "csv",
  130.         "orderby"      : "time",
  131.     }
  132.  
  133.     # Do the request
  134.     r = requests.get(
  135.         ENDPOINT + method,
  136.         params = request_params
  137.     )
  138.  
  139.     # Check for failure
  140.     if r.status_code != requests.codes.ok:
  141.         print("Bad request")
  142.         return None
  143.    
  144.     # Count the number of earthquakes on the period
  145.     if method == "count":
  146.         return int(r.text)
  147.    
  148.     # Get the earthquakes on the period
  149.     elif method == "query":
  150.         earthquakes = []
  151.         lines = r.text.split("\n")[1:-1]
  152.         for l in lines:
  153.             splitted = l.split(",")
  154.             obj = {
  155.                 "time" : datetime.strptime(splitted[0], '%Y-%m-%dT%H:%M:%S.%fZ'),
  156.                 "lat"  : float(splitted[1]),
  157.                 "lon"  : float(splitted[2]),
  158.                 "depth": float(splitted[3]),
  159.                 "mag"  : float(splitted[4]),
  160.             }
  161.             earthquakes.append(obj)
  162.         return earthquakes
  163.  
  164. def get_earthquakes(_start, _end, _mag_range):
  165.    
  166.     earthquakes = []
  167.    
  168.     while _start < datetime.now():
  169.            
  170.         # Format the start and end dates
  171.         _stop  = (_start + relativedelta(weeks=2)).strftime("%Y-%m-%d")
  172.        
  173.         # Get the number of quakes on the period
  174.         nQuakes = make_request(
  175.             method  = "count",
  176.             t_range = [_start, _stop],
  177.             m_range = _mag_range
  178.         )
  179.  
  180.         # Give some info
  181.         print("Collecting %d earthquakes from %s to %s" % (nQuakes, _start, _stop))
  182.        
  183.         # Get the earthquakes
  184.         try:
  185.             earthquakes.extend(
  186.                 make_request(
  187.                     method = "query",
  188.                     t_range = [_start, _stop],
  189.                     m_range = _mag_range
  190.                 )
  191.             )
  192.         except:
  193.             pass
  194.        
  195.         # Increment the starting date
  196.         _start = _start + relativedelta(weeks=2)
  197.        
  198.     return earthquakes
  199.  
  200.  
  201. if __name__ == "__main__":
  202.    
  203.     # Make the API requests to get the data (comment if already done)
  204.     earthquakes = get_earthquakes(START, END, MAG_RANGE)
  205.     with open(DB_FILE, "wb") as f:
  206.         pickle.dump(earthquakes, f, protocol=pickle.HIGHEST_PROTOCOL)
  207.    
  208.     # Read the data from a file (uncomment to avoid repeting the requests)
  209.     # earthquakes = []
  210.     # with open(DB_FILE, 'rb') as f:
  211.     #     earthquakes = pickle.load(f)
  212.    
  213.     # Create the point cloud
  214.     PC = PointCloud()
  215.     # Add the land mask as white points
  216.     lons, lats = get_landmask()
  217.     PC.coords.extend([ polar_to_xyz(lat, lon, 0) for lon, lat in zip(lons, lats) ])
  218.     PC.colors.extend([ (255,255,255) for x in lats ])
  219.     # Add the earthquake as colored data
  220.     PC.coords.extend([ polar_to_xyz(x["lon"], x["lat"], x["depth"]) for x in earthquakes ])
  221.     PC.colors.extend([ colormap(x["mag"], MAG_RANGE[0], MAG_RANGE[1]) for x in earthquakes ])
  222.     # Write the .ply file
  223.     PC.write(PLY_FILE)
  224.    
  225.     # Zip the point cloud
  226.     # shutil.make_archive("earthquakes", "zip", "./", "earthquakes.ply")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement