Commit a8acf81e authored by jlaura's avatar jlaura
Browse files

Merge pull request #61 from jlaura/master

Updates graph generation from file
parents e436c757 c97476b3
Loading
Loading
Loading
Loading
+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()

@@ -34,6 +34,7 @@ for k, v in iter(NP2GDAL_CONVERSION.items()):

GDAL2NP_CONVERSION[1] = 'int8'


class GeoDataset(object):
    """
    Geospatial dataset object that represents.
@@ -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):
        """
@@ -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):
@@ -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):
        """
+22 −19
Original line number Diff line number Diff line
import itertools
import os
import dill as pickle

@@ -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
@@ -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.
@@ -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
@@ -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
@@ -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)


@@ -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):
+4 −1
Original line number Diff line number Diff line
@@ -65,7 +65,7 @@ class TestCandidateGraph(unittest.TestCase):
    def tearDown(self):
        pass


'''
class TestFromList(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
@@ -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

@@ -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):