예제 #1
0
def way_points(bbox=None):
    """
    Generate evenly-spaced points along all ways in the ROI
    
    NOTE: output is in the projected coord sys defined by cfg.PRJ_SRID 
    
    Arguments
        bbox: 5-element list/tuple, containing bounding box [x_min, x_max,
            y_min, y_max, srid], the srid is an integer spatial reference ID
            (EPSG code) for the float limits. Set None to return all ways in
            the database

    Returns: 
        ways: dict, keys are way IDs, values are N x 2 numpy arrays containing
            the x, y position of sequential points along the way
        way_pts: dict, keys are way IDs, values are N x 2 numpy arrays
            containing the x, y position of evenly-spaced sequential points
            interpolated along the way.  The first row is always the start
            point, and the last is always the endpoint. Spacing for the last
            point for each way is generally less than the desired spacing,
            beware! this will cause an overshoot of up to spacing for the last
            point
    """
    logger.info(
        f'Computing way points, bbox={bbox}, spacing={cfg.OSM_WAYPT_SPACING}')
    with common.connect_db(cfg.OSM_DB) as conn, conn.cursor() as cur:

        # query returning geometry of all ways
        where = ''
        if bbox:
            where = f'WHERE ST_Intersects(ways.the_geom, ST_MakeEnvelope(' \
                    f'{bbox[0]}, {bbox[1]}, {bbox[2]}, {bbox[3]}, {bbox[4]}))'
        geom = f'ST_AsBinary(ST_Transform(the_geom, {cfg.PRJ_SRID}))'
        cur.execute(f'SELECT gid, {geom} FROM ways {where};')

        # read results and resample each way geometry
        # note: column osm_id is non-unique, do not use this as a key below
        recs = cur.fetchall()
        way_pts = {}
        ways = {}
        for rec in recs:
            # unpack record
            way_id = rec[0]
            line = shapely.wkb.loads(rec[1].tobytes())
            ways[way_id] = np.vstack(line.xy).T
            # resample line
            dists = range(0, round(line.length) + 1, cfg.OSM_WAYPT_SPACING)
            line_pts = [line.interpolate(d).xy for d in dists]
            way_pts[way_id] = np.hstack(line_pts).T
    logger.info(f'Completed way points for {len(ways)} ways')

    return way_pts
예제 #2
0
def nearest_id(lon, lat):
    """
    Return record ID for nearest vertex in the OSM database
    
    Arguments:
        lon, lat: floats, longitude and latitude (WGS84) of the query point

    Returns: int, record ID for nearest point
    """
    with common.connect_db(cfg.OSM_DB) as conn, conn.cursor() as cur:
        cur.execute(
            "SELECT id FROM ways_vertices_pgr ORDER BY the_geom <-> ST_SetSRID(ST_Point(%s, %s), 4326) LIMIT 1;",
            (lon, lat))
        return cur.fetchone()[0]
예제 #3
0
def create_db(clobber=False):
    """
    Create a new database and initialize for osm dataset

    Arguments:
        clobber: set True to delete and re-initialize an existing database

    Return: Nothing
    """
    # TODO: add index, if necessary
    common.new_db(cfg.OSM_DB, clobber)
    with common.connect_db(cfg.OSM_DB) as conn, conn.cursor() as cur:
        # add extensions
        cur.execute('CREATE EXTENSION postgis;')
        cur.execute('CREATE EXTENSION pgrouting;')
예제 #4
0
def update_cost_db(wpts):
    """
    Update insolation cost columns for all way elements in OSM database

    Arguments:
        wpts: dict, output from way_points(), contains way gid as keys, and
            evenly spaced points along way as values
    
    Returns: Nothing, sets values in cost_insolation_HHMM columns of OSM DB
    """
    with common.connect_db(cfg.OSM_DB) as conn:

        # loop over all calculated times
        for meta in common.shade_meta():
            with conn.cursor() as cur:

                # compute the cost at this time
                logger.info(
                    f'Updating insolation cost for {meta["hour"]:02d}:{meta["minute"]:02d}'
                )
                sun_cost, shade_cost = way_insolation(meta["hour"],
                                                      meta["minute"], wpts)

                # prepare columns
                cur.execute(
                    f'ALTER TABLE ways ADD COLUMN IF NOT EXISTS {meta["sun_cost"]} float8;'
                )
                cur.execute(
                    f'ALTER TABLE ways ADD COLUMN IF NOT EXISTS {meta["shade_cost"]} float8;'
                )

                # run batch of sql updates
                sql = f'UPDATE ways SET {meta["sun_cost"]} = %(cost)s WHERE gid = %(gid)s'
                params = [{
                    'gid': x[0],
                    'cost': x[1]
                } for x in sun_cost.items()]
                psycopg2.extras.execute_batch(cur, sql, params, page_size=1000)

                sql = f'UPDATE ways SET {meta["shade_cost"]} = %(cost)s WHERE gid = %(gid)s'
                params = [{
                    'gid': x[0],
                    'cost': x[1]
                } for x in shade_cost.items()]
                psycopg2.extras.execute_batch(cur, sql, params, page_size=1000)
예제 #5
0
def create_db(clobber=False):
    """
    Create a new database and initialize for lidar point data

    Arguments:
        clobber: set True to delete and re-initialize an existing database

    Return: Nothing
    """
    common.new_db(cfg.LIDAR_DB, clobber)
    with common.connect_db(cfg.LIDAR_DB) as conn, conn.cursor() as cur:
        cur.execute('CREATE EXTENSION postgis;')
        cur.execute('CREATE EXTENSION pointcloud;')
        cur.execute('CREATE EXTENSION pointcloud_postgis;')
        cur.execute(
            f'CREATE TABLE {cfg.LIDAR_TABLE} (id SERIAL PRIMARY KEY, pa PCPATCH(1));'
        )
        cur.execute(
            f'CREATE INDEX ON {cfg.LIDAR_TABLE} USING GIST(PC_EnvelopeGeometry(pa));'
        )
    logger.info(
        f'Created new database: {cfg.LIDAR_DB} @ {cfg.PSQL_HOST}:{cfg.PSQL_PORT}'
    )
예제 #6
0
파일: test.py 프로젝트: keithfma/parasol
"""
Figure out a way to fix the surface gridding routines - they are terribly slow and broken now
"""

from parasol import common, cfg, lidar
import shapely
import uuid
import json
import numpy as np

xmin = 327480.17
xmax = 328500.17
ymin = 4689458.81
ymax = 4690478.81

with common.connect_db(cfg.LIDAR_DB) as conn, conn.cursor() as cur:
    cur.execute(
        f'SELECT PC_AsText(PC_Union(pa)) FROM lidar WHERE PC_Intersects(pa, ST_MakeEnvelope({xmin}, {ymin}, {xmax}, {ymax}, {cfg.PRJ_SRID}))'
    )
    data = np.array(
        json.loads(cur.fetchone()[0])['pts']
    )  # ordered as ReturnNumber,NumberOfReturns,Classification,X,Y,Z
    data = np.roll(
        data,
        3)  # ordered as X,Y,Z,ReturnNumber,NumberOfReturns,Classification

data2 = lidar.retrieve(xmin, xmax, ymin, ymax)
예제 #7
0
def _route(lon0, lat0, lon1, lat1, time=None, beta=None):
    """
    Shared utility to retrieve route from pgrouting server
    
    Arguments: 
        lat0, lon0 = floats, start point latitude, longitude
        lat1, lon1 = floats, end point latitude, longitude
        time: 
        beta: 
    
    Returns: meters, sun, geojson
        meters: total length of route in meters
        sun: total solar cost (normalized units)
        geojson: optimal route as geoJSON
    """
    # parse time
    if time is None:
        time = datetime.now()
    if not isinstance(time, datetime):
        raise TypeError('Argument "time" must be a datetime object')

    # get cost columns corresponding to current date/time
    delta = timedelta(hours=9999)  # arbitrarily large
    # TODO: use common.shade_meta()
    for fhours in np.arange(cfg.SHADE_START_HOUR, cfg.SHADE_STOP_HOUR,
                            cfg.SHADE_INTERVAL_HOUR):
        this_hour = math.floor(fhours)
        this_minute = math.floor((fhours - this_hour) * 60)
        this_time = datetime.now().replace(hour=this_hour,
                                           minute=this_minute,
                                           second=0,
                                           microsecond=0)
        this_delta = abs(time - this_time)
        if this_delta <= delta:
            sun_cost = f'{cfg.OSM_SUN_COST_PREFIX}{this_time.hour:02d}{this_time.minute:02d}'
            shade_cost = f'{cfg.OSM_SHADE_COST_PREFIX}{this_time.hour:02d}{this_time.minute:02d}'
            delta = this_delta

    # sql expression for cost (shortest or optimal)
    if beta is None:
        # shortest
        cost_expr = 'length_m'
    elif beta >= 0 and beta <= 1:
        # optimal
        cost_expr = f'{1 - beta} * {sun_cost} + {beta} * {shade_cost}'
    else:
        # invalid
        raise ValueError('"beta" must be either in the range [0, 1] or None')

    # find start/end vertices
    start_id = nearest_id(lon0, lat0)
    end_id = nearest_id(lon1, lat1)

    # compute djikstra path, return total length, total sun cost, and route
    with common.connect_db(cfg.OSM_DB) as conn, conn.cursor() as cur:
        inner_sql = f'SELECT gid AS id, source, target, {cost_expr} AS cost, the_geom FROM ways'
        cur.execute(
            f"SELECT SUM(length_m), SUM({sun_cost}), ST_AsGeoJSON(ST_Union(the_geom)) "
            f"FROM pgr_dijkstra('{inner_sql}', {start_id}, {end_id}, directed := false) "
            f"LEFT JOIN ways ON (edge = gid);")
        return cur.fetchone()