Unverified Commit c59c81fc authored by jlaura's avatar jlaura Committed by GitHub
Browse files

Initial work to config after instantiation (#438)

* Initial work to iconfig after instantiation

* refactors session, engine, config from NetworkNode

* refactors session, engine, config from NetworkNode

* mend

* Refactors session and config from models

* refactors find_side to need geom as arg

* Migrates sessions, configs out of API

* Migrates sessions, configs out of API

* Generic submission, tested to overlap, fixed refactor induced bugs

* Fixes acn_submit to push one job not all jobs n times

* Full removal of config on import

* Generalized overlap and running subpixel

* Fixes from DB to then populate the graph

* Adds support for Measures

* Removes bin scripts that are obsoleted by acn_submit

* Expunges objs for use outside a session

* Updates for node/edge dispatching via apply

* Updates tests for refactoring

* Removes nontest test

* Reverts port to default in test

* Updates for creation via an existing data store

* Updates for PR comments cleaning

* Fixes find_side

* Trying to get network docstring to be happy

* Adds asserts to throw is pt is out of image

* Removes hacked in asserts and adds debug to subpixel

* Updates for session comments

* Moves apply_map into NCG

* Updates docstring for comments

* class to instance attr
parent 4aefbb4a
Loading
Loading
Loading
Loading
+0 −32
Original line number Diff line number Diff line
@@ -23,38 +23,6 @@ except DistributionNotFound:
else:
    __version__ = _dist.version

# Defaults
dem = None

from autocnet.config_parser import parse_config

config = parse_config()

if config:
    dem = config['spatial']['dem']
    try:
        dem = GeoDataset(dem)
    except:
        warnings.warn(f'Unable to load the desired DEM: {dem}.')
        dem = None

    db_uri = '{}://{}:{}@{}:{}/{}'.format(config['database']['type'],
                                            config['database']['username'],
                                            config['database']['password'],
                                            config['database']['host'],
                                            config['database']['pgbouncer_port'],
                                            config['database']['name'])
    hostname = socket.gethostname()
    engine = create_engine(db_uri, poolclass=pool.NullPool,
                    connect_args={"application_name":"AutoCNet_{}".format(hostname)},
                    isolation_level="AUTOCOMMIT")
    Session = orm.session.sessionmaker(bind=engine)
else:
    def sessionwarn():
        raise RuntimeError('Attempting to use a database session without a config file specified.')
    Session = sessionwarn
    engine = sessionwarn

# Patch the candidate graph into the root namespace
from autocnet.graph.network import CandidateGraph, NetworkCandidateGraph

+13 −9
Original line number Diff line number Diff line
@@ -15,7 +15,6 @@ from shapely.affinity import scale
from shapely import wkt

from autocnet.utils import utils
from autocnet import Session



@@ -267,13 +266,16 @@ def nearest(pt, search):
    """
    return np.argmin(np.sum((search - pt)**2, axis=1))

def find_side(side):
def find_side(side, Session):
    """
    Parameters
    ----------
    side: str
            describes which extrema you cube you want; can equal 'east' or 'west'

    geom : obj
           A shapely geom object

    Returns
    -------
    lon : float
@@ -290,6 +292,7 @@ def find_side(side):
    func = func[side]
    order = {'east': 'desc', 'west': 'asc'}
    order = order[side]

    query = f"""
    select ST_AsText(geom) from images
    order by {func}(geom) {order}
@@ -364,7 +367,7 @@ def xy_in_polygon(x,y, geom):
    return geom.contains(Point(x, y))


def distribute_points_classic(geom, nspts, ewpts):
def distribute_points_classic(geom, nspts, ewpts, **kwargs):
    """
    This is a decision tree that attempts to perform a
    very simplistic approximation of the shape
@@ -424,7 +427,7 @@ def distribute_points_classic(geom, nspts, ewpts):
    valid = [p for p in points if xy_in_polygon(p[0], p[1], geom)]
    return valid

def distribute_points_new(geom, nspts, ewpts):
def distribute_points_new(geom, nspts, ewpts, Session):
    """
    This is a decision tree that attempts to perform a
    very simplistic approximation of the shape
@@ -459,11 +462,11 @@ def distribute_points_new(geom, nspts, ewpts):
    ul = coords[3]

    # Find the points nearest the ur and ll aligned // with eastern side of ground_poly
    elon, elat = find_side('east')
    elon, elat = find_side('east', Session)
    ur_actual = np.array(two_point_extrapolate(ur[1], elat, elon))[::-1]
    lr_actual = np.array(two_point_extrapolate(lr[1],elat, elon))[::-1]

    wlon, wlat = find_side('west')
    wlon, wlat = find_side('west', Session)
    ul_actual = np.array(two_point_extrapolate(ul[1], wlat, wlon))[::-1]
    ll_actual = np.array(two_point_extrapolate(ll[1], wlat, wlon))[::-1]

@@ -490,7 +493,8 @@ def distribute_points_new(geom, nspts, ewpts):

def distribute_points_in_geom(geom, method="classic",
                              nspts_func=lambda x: ceil(round(x,1)*10),
                              ewpts_func=lambda x: ceil(round(x,1)*5)):
                              ewpts_func=lambda x: ceil(round(x,1)*5),
                              Session=None):
    """
    Given a geometry, attempt a basic classification of the shape.
    RIght now, this simply attempts to determine if the bounding box
@@ -571,7 +575,7 @@ def distribute_points_in_geom(geom, method="classic",
        if nspts == 1 and ewpts == 1:
            valid = single_centroid(geom)
        else:
            valid = point_distribution_func(geom, nspts, ewpts)
            valid = point_distribution_func(geom, nspts, ewpts, Session=Session)
    elif ew == True:
        # Since this is an LS, we should place these diagonally from the 'lower left' to the 'upper right'
        nspts = ewpts_func(short)
@@ -579,7 +583,7 @@ def distribute_points_in_geom(geom, method="classic",
        if nspts == 1 and ewpts == 1:
            valid = single_centroid(geom)
        else:
            valid = point_distribution_func(geom, nspts, ewpts)
            valid = point_distribution_func(geom, nspts, ewpts, Session=Session)
    else:
        print('WTF Willy')
    return valid
+6 −6
Original line number Diff line number Diff line
import os
import yaml

def parse_config(name='autocnet_config'):
    if name not in os.environ.keys():
        return {}
    
    filepath = os.environ[name]
def parse_config(filepath):
    if not os.path.exists(filepath):
        raise OSError(f'Specified config file does not exist. Currently set to {filepath}.')
        raise FileNotFoundError(f'Config file {filepath} does not exist.')

    # Not wrapping in a try/except so that we get the 
    # yaml library to raise any issues on parsing
@@ -25,6 +21,10 @@ def parse_config(name='autocnet_config'):
    if database == None:
        raise KeyError('Config is missing the root "database" key.')
    
    redis = config.get('redis', None)
    if redis == None:
        raise KeyError('Config is missing the root "redis" key.')

    for k in ['type', 'username', 'password', 'host', 'pgbouncer_port', 'name']:
        if k not in database.keys():
            raise KeyError(f'Missing key: "{k}" in the database section of the config.')
+160 −173
Original line number Diff line number Diff line
@@ -9,7 +9,6 @@ from scipy.spatial.distance import cdist
from shapely.geometry import Point
import sqlalchemy

from autocnet import Session, engine
from autocnet.graph.node import Node
from autocnet.utils import utils
from autocnet.matcher import cpu_outlier_detector as od
@@ -740,21 +739,28 @@ class NetworkEdge(Edge):
        table_obj : object
                    The declared table class (from db.model)
        """
        session = Session()
        with self.parent.session_scope() as session:
            res = session.query(table_obj).\
                   filter(table_obj.source == self.source['node_id']).\
                   filter(table_obj.destination == self.destination['node_id'])
        session.close()
            session.expunge_all()
        return res

    @property
    def parent(self):
        return getattr(self, '_parent', None)

    @parent.setter
    def parent(self, parent):
        self._parent = parent

    @property
    def masks(self):
        session = Session()
        with self.parent.session_scope() as session:
            res = session.query(Edges.masks).\
                                            filter(Edges.source == self.source['node_id']).\
                                            filter(Edges.destination == self.destination['node_id']).\
                                            first()
        session.close()
        try:
            df = pd.DataFrame.from_records(res[0])
            df.index = df.index.map(int)
@@ -778,7 +784,7 @@ class NetworkEdge(Edge):


        df = pd.DataFrame(v)
        session = Session()
        with self.parent.session_scope() as session:
            res = session.query(Edges).\
                                    filter(Edges.source == self.source['node_id']).\
                                    filter(Edges.destination == self.destination['node_id']).first()
@@ -788,17 +794,14 @@ class NetworkEdge(Edge):
                # Update the masks
                res.masks = as_dict
                session.add(res)
            session.commit()
        session.close()

    @property
    def costs(self):
        # these are np.float coming out, sqlalchemy needs ints
        ids = list(map(int, self.matches.index.values))
        session = Session()
        with self.parent.session_scope() as session:
            res = session.query(Costs).filter(Costs.match_id.in_(ids)).all()
            #qf = q.filter(Costs.match_id.in_(ids))
        session.close()
        if res:
        # Parse the JSON dicts in the cost field into a full dimension dataframe
            costs = {r.match_id:r._cost for r in res}
@@ -814,7 +817,7 @@ class NetworkEdge(Edge):
    def costs(self, v):
        to_db_add = []
        # Get the query obj
        session = Session()
        with self.parent.session_scope() as session:
            q = session.query(Costs)
            # Need the new instance here to avoid __setattr__ issues
            df = pd.DataFrame(v)
@@ -832,7 +835,6 @@ class NetworkEdge(Edge):
                        res._cost[k] = v
                    sqlalchemy.orm.attributes.flag_modified(res, '_cost')
                    session.add(res)
                session.commit()
                else:
                    row = row.to_dict()
                    costs = row.pop('_costs', {})
@@ -844,20 +846,16 @@ class NetworkEdge(Edge):
                    to_db_add.append(cost)
            if to_db_add:
                session.bulk_save_objects(to_db_add)
        session.commit()
        session.close()

    @property
    def matches(self):
        session = Session()
        with self.parent.session_scope() as session:
            q = session.query(Matches)
            qf = q.filter(Matches.source == self.source['node_id'],
                          Matches.destination == self.destination['node_id'])
            odf = pd.read_sql(qf.statement, q.session.bind).set_index('id')
            df = pd.DataFrame(odf.values, index=odf.index.values, columns=odf.columns.values)
            df.index.name = 'id'
        # Explicit close to get the session cleaned up
        session.close()
        return DbDataFrame(df,  parent=self, name='matches')

    @matches.setter
@@ -867,7 +865,7 @@ class NetworkEdge(Edge):
        df = pd.DataFrame(v)
        df.index.name = v.index.name
        # Get the query obj
        session = Session()
        with self.parent.session_scope() as session:
            q = session.query(Matches)
            for idx, row in df.iterrows():
                # Determine if this is an update or the addition of a new row
@@ -907,15 +905,11 @@ class NetworkEdge(Edge):
                session.bulk_save_objects(to_db_add)
            if to_db_update:
                session.bulk_update_mappings(Matches, to_db_update)
        session.commit()
        session.close()

    @matches.deleter
    def matches(self):
        session = Session()
        with self.parent.session_scope() as session:
            session.query(Matches).filter(Matches.source == self.source['node_id'], Matches.destination == self.destination['node_id']).delete()
        session.commit()
        session.close()

    @property
    def ring(self):
@@ -928,7 +922,7 @@ class NetworkEdge(Edge):
    def ring(self, ring):
        # Setters need a single session and so should not make use of the
        # syntax sugar _from_db
        session = Session()
        with self.parent.session_scope() as session:
            res = session.query(Edges).\
                   filter(Edges.source == self.source['node_id']).\
                   filter(Edges.destination == self.destination['node_id']).first()
@@ -939,9 +933,6 @@ class NetworkEdge(Edge):
                             destination=self.destination['node_id'],
                             ring=ring)
                session.add(edge)
            session.commit()
        session.close()
        return

    @property
    def intersection(self):
@@ -959,7 +950,7 @@ class NetworkEdge(Edge):

    @fundamental_matrix.setter
    def fundamental_matrix(self, v):
        session = Session()
        with self.parent.session_scope() as session:
            res = session.query(Edges).\
                   filter(Edges.source == self.source['node_id']).\
                   filter(Edges.destination == self.destination['node_id']).first()
@@ -970,8 +961,6 @@ class NetworkEdge(Edge):
                             destination=self.destination['node_id'],
                             fundamental = v)
                session.add(edge)
        session.commit()
        session.close()

    def get_overlapping_indices(self, kps):
        lons, lats, alts = reproject([kps.xm.values, kps.ym.values, kps.zm.values], semi_major, semi_minor, 'geocent', 'latlon')
@@ -981,9 +970,8 @@ class NetworkEdge(Edge):

    @property
    def measures(self):
        session = Session()
        with self.parent.session_scope() as session:
            res = session.query(Measures).filter(sqlalchemy.or_(Measures.imageid == self.source['node_id'], Measures.imageid == self.destination['node_id'])).all()
        session.close()
        return res

    def network_to_matches(self, ignore_point=False, ignore_measure=False, rejected_jigsaw=False):
@@ -1012,7 +1000,7 @@ class NetworkEdge(Edge):
        if source > destin:
            source, destin = destin, source

        session = Session()
        with self.parent.session_scope() as session:
            q = session.query(Points.id,
                      Points.pointtype,
                      Measures.id.label('mid'),
@@ -1026,13 +1014,12 @@ class NetworkEdge(Edge):
                       sqlalchemy.or_(Measures.imageid==source,
                                      Measures.imageid==destin)).join(Measures)

        df = pd.read_sql(q.statement, engine)
            df = pd.read_sql(q.statement, self.parent.engine)
            matches = []
            columns = ['point_id', 'source_measure_id', 'destin_measure_id', 'source', 'source_idx', 'destination', 'destination_idx',
                   'lat', 'lon', 'geom', 'source_x', 'source_y', 'destination_x',
                   'destination_y', 'shift_x', 'shift_y', 'original_destination_x',
                   'original_destination_y']
        session.close()

        def net2matches(grp, matches, source, destin):
            # Grab the image ids and then get the cartesian product of the ids to know which
@@ -1075,13 +1062,13 @@ class NetworkEdge(Edge):
        """
        mask = self.masks[mask]
        matches_to_disable = mask[mask == False].index
        session = Session()

        
        bad = {}
        with self.parent.session_scope() as session:
            for o in session.query(Matches).filter(Matches.id.in_(matches_to_disable)).all():
                # This can't just set both to False, we loose a ton of good points - a bad point in 1 image is not
                # necessarily bad in all of the other images. Doing it this way assumes that it is...
                bad[o.source_measure_id] = 1
                bad[o.destin_measure_id] = 1
        session.close()
        return Counter(bad)
+320 −131

File changed.

Preview size limit exceeded, changes collapsed.

Loading