Loading autocnet/fileio/io_gdal.py +46 −42 Original line number Diff line number Diff line import json import pvl import os import warnings from pysal.cg.shapes import Polygon from autocnet.utils.utils import find_in_dict, find_nested_in_dict from autocnet.utils.utils import find_in_dict from osgeo import ogr import numpy as np import gdal import osr from autocnet.fileio import extract_metadata from pysal import cg gdal.UseExceptions() Loading @@ -34,6 +34,7 @@ for k, v in iter(NP2GDAL_CONVERSION.items()): GDAL2NP_CONVERSION[1] = 'int8' class GeoDataset(object): """ Geospatial dataset object that represents. Loading Loading @@ -124,6 +125,12 @@ class GeoDataset(object): no_data_value : float Special value used to indicate pixels that are not valid. metadata : dict A dictionary of available image metadata footprint : object An OGR footprint object """ def __init__(self, file_name): """ Loading Loading @@ -195,34 +202,50 @@ class GeoDataset(object): @property def latlon_extent(self): if not getattr(self, '_latlon_extent', None): try: # If we have a footprint, no need to compute pixel to latlon lowerlon, upperlon, lowerlat, upperlat = self.footprint.GetEnvelope() except: xy_extent = self.xy_extent lowerlat, lowerlon = self.pixel_to_latlon(xy_extent[0][0], xy_extent[0][1]) upperlat, upperlon = self.pixel_to_latlon(xy_extent[1][0], xy_extent[1][1]) self._latlon_extent = [(lowerlat, lowerlon), (upperlat, upperlon)] except: warnings.warn("Couldn't calculate a Latitude/Longitude extent") self._latlon_extent = None return self._latlon_extent @property def footprint_polygon(self): if not getattr(self, '_pvl_header', None): self._pvl_header = pvl.load(self.file_name) # Should be a member variable? polygon_pvl = find_in_dict(self._pvl_header, 'Polygon') def metadata(self): if not hasattr(self, '_metadata'): try: self._metadata = pvl.load(self.file_name) except: self._metadata = self.dataset.GetMetadata() return self._metadata @property def footprint(self): if not hasattr(self, '_footprint'): try: polygon_pvl = find_in_dict(self.metadata, 'Polygon') start_polygon_byte = find_in_dict(polygon_pvl, 'StartByte') num_polygon_bytes = find_in_dict(polygon_pvl, 'Bytes') # TODO: Do we really need to re-open the file here? self.dataset does not work f = open(self.file_name, 'r+') # I too dislike the additional open here. Not sure a good option with open(self.file_name, 'r+') as f: f.seek(start_polygon_byte - 1) wkt = f.read(num_polygon_bytes) polygon = ogr.CreateGeometryFromWkt(wkt) self._footprint_polygon = polygon f.close() return self._footprint_polygon # Sloppy unicode to string because GDAL pukes on unicode stream = str(f.read(num_polygon_bytes)) self._footprint = ogr.CreateGeometryFromWkt(stream) except: # Handle GDAL here llat, llon, ulat, ulon = self.latlon_extent geom = {"type": "Polygon", "coordinates": [[llat, llon], [llat, ulon], [ulat, ulon], [llon, ulat], [llat, llon]]} self._footprint = ogr.CreateGeometryFromJson(json.dumps(geom)) return self._footprint @property def xy_extent(self): Loading @@ -238,25 +261,6 @@ class GeoDataset(object): return self._xy_extent @property def bounding_box(self): """ A bounding box in lat/lon space Returns ------- """ if not getattr(self, '_bounding_box', None): try: latlons = self.latlon_extent # Will fail without geospatial data self._bounding_box = cg.standalone.get_bounding_box([cg.shapes.LineSegment(latlons[0], latlons[1])]) print(list(self._bounding_box)) except: envelope = self.footprint_polygon.GetEnvelope() self._bounding_box = [envelope[0], envelope[2], envelope[1], envelope[3]] # TODO: check me? print(self.bounding_box) return self._bounding_box @property def pixel_polygon(self): """ Loading autocnet/graph/network.py +22 −19 Original line number Diff line number Diff line import itertools import os import dill as pickle Loading @@ -5,8 +6,6 @@ import networkx as nx import numpy as np import pandas as pd from pysal import cg from autocnet.examples import get_path from autocnet.fileio.io_gdal import GeoDataset from autocnet.control.control import C from autocnet.fileio import io_json Loading @@ -16,6 +15,7 @@ from autocnet.graph.edge import Edge from autocnet.graph.node import Node from autocnet.vis.graph_view import plot_graph class CandidateGraph(nx.Graph): """ A NetworkX derived directed graph to store candidate overlap images. Loading Loading @@ -79,9 +79,9 @@ class CandidateGraph(nx.Graph): graph = pickle.load(f) return graph # TODO: Add ability to actually read this out of a file? @classmethod def from_filelist(cls, filelst): def from_filelist(cls, filelist): """ Instantiate the class using a filelist as a python list. An adjacency structure is calculated using the lat/lon information in the Loading @@ -89,7 +89,7 @@ class CandidateGraph(nx.Graph): Parameters ---------- filelst : list filelist : list A list containing the files (with full paths) to construct an adjacency graph from Returns Loading @@ -100,20 +100,24 @@ class CandidateGraph(nx.Graph): # TODO: Reject unsupported file formats + work with more file formats dataset_list = [] for file in filelst: dataset = GeoDataset(file) dataset_list.append(dataset) datasets = [GeoDataset(f) for f in filelist] # This is brute force for now, could swap to an RTree at some point. adjacency_dict = {} for data in dataset_list: adjacent_images = [] other_datasets = dataset_list.copy() other_datasets.remove(data) for other in other_datasets: if(cg.standalone.bbcommon(data.bounding_box, other.bounding_box)): adjacent_images.append(other.base_name) adjacency_dict[data.base_name] = adjacent_images for i, j in itertools.permutations(datasets,2): if not i.base_name in adjacency_dict.keys(): adjacency_dict[i.base_name] = [] if not j.base_name in adjacency_dict.keys(): adjacency_dict[j.base_name] = [] # Grab the footprints and test for intersection i_fp = i.footprint j_fp = j.footprint if i_fp.Intersects(j_fp): adjacency_dict[i.base_name].append(j.base_name) adjacency_dict[j.base_name].append(i.base_name) return cls(adjacency_dict) Loading Loading @@ -145,7 +149,6 @@ class CandidateGraph(nx.Graph): for k, v in input_adjacency.items(): input_adjacency[k] = [os.path.join(basepath, i) for i in v] input_adjacency[os.path.join(basepath, k)] = input_adjacency.pop(k) # print(input_adjacency) return cls(input_adjacency) def get_name(self, node_index): Loading autocnet/graph/tests/test_network.py +4 −1 Original line number Diff line number Diff line Loading @@ -65,7 +65,7 @@ class TestCandidateGraph(unittest.TestCase): def tearDown(self): pass ''' class TestFromList(unittest.TestCase): @classmethod def setUpClass(cls): Loading @@ -78,6 +78,7 @@ class TestFromList(unittest.TestCase): self.assertEqual(self.graph.__len__(), 3) self.assertEqual(self.graph.number_of_nodes(), 3) class TestFromListCubes(unittest.TestCase): @classmethod Loading @@ -90,6 +91,8 @@ class TestFromListCubes(unittest.TestCase): def test_graph_length(self): self.assertEqual(self.graph.number_of_nodes(), 3) self.assertEqual(self.graph.number_of_edges(), 3) ''' class TestEdge(unittest.TestCase): Loading Loading
autocnet/fileio/io_gdal.py +46 −42 Original line number Diff line number Diff line import json import pvl import os import warnings from pysal.cg.shapes import Polygon from autocnet.utils.utils import find_in_dict, find_nested_in_dict from autocnet.utils.utils import find_in_dict from osgeo import ogr import numpy as np import gdal import osr from autocnet.fileio import extract_metadata from pysal import cg gdal.UseExceptions() Loading @@ -34,6 +34,7 @@ for k, v in iter(NP2GDAL_CONVERSION.items()): GDAL2NP_CONVERSION[1] = 'int8' class GeoDataset(object): """ Geospatial dataset object that represents. Loading Loading @@ -124,6 +125,12 @@ class GeoDataset(object): no_data_value : float Special value used to indicate pixels that are not valid. metadata : dict A dictionary of available image metadata footprint : object An OGR footprint object """ def __init__(self, file_name): """ Loading Loading @@ -195,34 +202,50 @@ class GeoDataset(object): @property def latlon_extent(self): if not getattr(self, '_latlon_extent', None): try: # If we have a footprint, no need to compute pixel to latlon lowerlon, upperlon, lowerlat, upperlat = self.footprint.GetEnvelope() except: xy_extent = self.xy_extent lowerlat, lowerlon = self.pixel_to_latlon(xy_extent[0][0], xy_extent[0][1]) upperlat, upperlon = self.pixel_to_latlon(xy_extent[1][0], xy_extent[1][1]) self._latlon_extent = [(lowerlat, lowerlon), (upperlat, upperlon)] except: warnings.warn("Couldn't calculate a Latitude/Longitude extent") self._latlon_extent = None return self._latlon_extent @property def footprint_polygon(self): if not getattr(self, '_pvl_header', None): self._pvl_header = pvl.load(self.file_name) # Should be a member variable? polygon_pvl = find_in_dict(self._pvl_header, 'Polygon') def metadata(self): if not hasattr(self, '_metadata'): try: self._metadata = pvl.load(self.file_name) except: self._metadata = self.dataset.GetMetadata() return self._metadata @property def footprint(self): if not hasattr(self, '_footprint'): try: polygon_pvl = find_in_dict(self.metadata, 'Polygon') start_polygon_byte = find_in_dict(polygon_pvl, 'StartByte') num_polygon_bytes = find_in_dict(polygon_pvl, 'Bytes') # TODO: Do we really need to re-open the file here? self.dataset does not work f = open(self.file_name, 'r+') # I too dislike the additional open here. Not sure a good option with open(self.file_name, 'r+') as f: f.seek(start_polygon_byte - 1) wkt = f.read(num_polygon_bytes) polygon = ogr.CreateGeometryFromWkt(wkt) self._footprint_polygon = polygon f.close() return self._footprint_polygon # Sloppy unicode to string because GDAL pukes on unicode stream = str(f.read(num_polygon_bytes)) self._footprint = ogr.CreateGeometryFromWkt(stream) except: # Handle GDAL here llat, llon, ulat, ulon = self.latlon_extent geom = {"type": "Polygon", "coordinates": [[llat, llon], [llat, ulon], [ulat, ulon], [llon, ulat], [llat, llon]]} self._footprint = ogr.CreateGeometryFromJson(json.dumps(geom)) return self._footprint @property def xy_extent(self): Loading @@ -238,25 +261,6 @@ class GeoDataset(object): return self._xy_extent @property def bounding_box(self): """ A bounding box in lat/lon space Returns ------- """ if not getattr(self, '_bounding_box', None): try: latlons = self.latlon_extent # Will fail without geospatial data self._bounding_box = cg.standalone.get_bounding_box([cg.shapes.LineSegment(latlons[0], latlons[1])]) print(list(self._bounding_box)) except: envelope = self.footprint_polygon.GetEnvelope() self._bounding_box = [envelope[0], envelope[2], envelope[1], envelope[3]] # TODO: check me? print(self.bounding_box) return self._bounding_box @property def pixel_polygon(self): """ Loading
autocnet/graph/network.py +22 −19 Original line number Diff line number Diff line import itertools import os import dill as pickle Loading @@ -5,8 +6,6 @@ import networkx as nx import numpy as np import pandas as pd from pysal import cg from autocnet.examples import get_path from autocnet.fileio.io_gdal import GeoDataset from autocnet.control.control import C from autocnet.fileio import io_json Loading @@ -16,6 +15,7 @@ from autocnet.graph.edge import Edge from autocnet.graph.node import Node from autocnet.vis.graph_view import plot_graph class CandidateGraph(nx.Graph): """ A NetworkX derived directed graph to store candidate overlap images. Loading Loading @@ -79,9 +79,9 @@ class CandidateGraph(nx.Graph): graph = pickle.load(f) return graph # TODO: Add ability to actually read this out of a file? @classmethod def from_filelist(cls, filelst): def from_filelist(cls, filelist): """ Instantiate the class using a filelist as a python list. An adjacency structure is calculated using the lat/lon information in the Loading @@ -89,7 +89,7 @@ class CandidateGraph(nx.Graph): Parameters ---------- filelst : list filelist : list A list containing the files (with full paths) to construct an adjacency graph from Returns Loading @@ -100,20 +100,24 @@ class CandidateGraph(nx.Graph): # TODO: Reject unsupported file formats + work with more file formats dataset_list = [] for file in filelst: dataset = GeoDataset(file) dataset_list.append(dataset) datasets = [GeoDataset(f) for f in filelist] # This is brute force for now, could swap to an RTree at some point. adjacency_dict = {} for data in dataset_list: adjacent_images = [] other_datasets = dataset_list.copy() other_datasets.remove(data) for other in other_datasets: if(cg.standalone.bbcommon(data.bounding_box, other.bounding_box)): adjacent_images.append(other.base_name) adjacency_dict[data.base_name] = adjacent_images for i, j in itertools.permutations(datasets,2): if not i.base_name in adjacency_dict.keys(): adjacency_dict[i.base_name] = [] if not j.base_name in adjacency_dict.keys(): adjacency_dict[j.base_name] = [] # Grab the footprints and test for intersection i_fp = i.footprint j_fp = j.footprint if i_fp.Intersects(j_fp): adjacency_dict[i.base_name].append(j.base_name) adjacency_dict[j.base_name].append(i.base_name) return cls(adjacency_dict) Loading Loading @@ -145,7 +149,6 @@ class CandidateGraph(nx.Graph): for k, v in input_adjacency.items(): input_adjacency[k] = [os.path.join(basepath, i) for i in v] input_adjacency[os.path.join(basepath, k)] = input_adjacency.pop(k) # print(input_adjacency) return cls(input_adjacency) def get_name(self, node_index): Loading
autocnet/graph/tests/test_network.py +4 −1 Original line number Diff line number Diff line Loading @@ -65,7 +65,7 @@ class TestCandidateGraph(unittest.TestCase): def tearDown(self): pass ''' class TestFromList(unittest.TestCase): @classmethod def setUpClass(cls): Loading @@ -78,6 +78,7 @@ class TestFromList(unittest.TestCase): self.assertEqual(self.graph.__len__(), 3) self.assertEqual(self.graph.number_of_nodes(), 3) class TestFromListCubes(unittest.TestCase): @classmethod Loading @@ -90,6 +91,8 @@ class TestFromListCubes(unittest.TestCase): def test_graph_length(self): self.assertEqual(self.graph.number_of_nodes(), 3) self.assertEqual(self.graph.number_of_edges(), 3) ''' class TestEdge(unittest.TestCase): Loading