def calc_road_bearing(estamap_version):

    logging.info('environment')
    em = gis.ESTAMAP(estamap_version)
    cursor = em.conn.cursor()

    logging.info('get coords')
    pfis = []
    x_entry = []
    y_entry = []
    x_exit = []
    y_exit = []
    with arcpy.da.SearchCursor(in_table=os.path.join(em.sde, 'ROAD'),
                               field_names=['PFI', 'SHAPE@WKB'],
                               spatial_reference=gis.ingr_spatial_reference(),
                               sql_clause=(None, 'ORDER BY PFI')) as sc:
        for enum, (pfi, geom_wkb) in enumerate(sc, 1):

            geom = shapely.wkb.loads(str(geom_wkb))

            first_point = geom.coords[0]
            entry_point = geom.interpolate(2.5)
            dx_entry = entry_point.x - first_point[0]
            dy_entry = entry_point.y - first_point[1]

            last_point = geom.coords[-1]
            exit_point = geom.interpolate(geom.length - 2.5)
            dx_exit = last_point[0] - exit_point.x
            dy_exit = last_point[1] - exit_point.y

            pfis.append(pfi)
            x_entry.append(dx_entry)
            y_entry.append(dy_entry)
            x_exit.append(dx_exit)
            y_exit.append(dy_exit)

            if enum % 10000 == 0:
                logging.info('{}'.format(enum))
        logging.info('{}'.format(enum))

    logging.info('calc bearings and insert')
    with dbpy.SQL_BULK_COPY(em.server, em.database_name, 'dbo.ROAD_BEARING') as sbc:
        
        for enum, (pfi, entry_angle, exit_angle) in enumerate(zip(pfis,
                                                                  np.arctan2(np.array(y_entry), np.array(x_entry)) * 180 / np.pi,
                                                                  np.arctan2(np.array(y_exit), np.array(x_exit)) * 180 / np.pi)):
##            if entry_angle < 0:
##                entry_bearing = (90 - entry_angle) % 360
##            else:
##                entry_bearing = (450 - entry_angle) % 360
##            if exit_angle < 0:
##                exit_bearing = (90 - exit_angle) % 360
##            else:
##                exit_bearing = (450 - exit_angle) % 360

##            if entry_angle < 0:
##                entry_bearing = int((90 - entry_angle) % 360)
##            else:
##                entry_bearing = int((450 - entry_angle) % 360)
##            if exit_angle < 0:
##                exit_bearing = int((90 - exit_angle) % 360)
##            else:
##                exit_bearing = int((450 - exit_angle) % 360)

            if entry_angle < 0:
                entry_bearing = float((90 - entry_angle) % 360)
            else:
                entry_bearing = float((450 - entry_angle) % 360)
            if exit_angle < 0:
                exit_bearing = float((90 - exit_angle) % 360)
            else:
                exit_bearing = float((450 - exit_angle) % 360)

            sbc.add_row((pfi, entry_bearing, exit_bearing, (exit_bearing + 180) % 360, (entry_bearing + 180) % 360))
            if enum % 10000 == 0:
                logging.info(enum)
                sbc.flush()
        logging.info(enum)
    logging.info('count start: {}'.format(sbc.count_start))
    logging.info('count finish: {}'.format(sbc.count_finish))
def create_locality_centroid(estamap_version):

    logging.info('environment')
    em = gis.ESTAMAP(estamap_version)
    arcpy.env.outputCoordinateSystem = arcpy.SpatialReference(3111)
    ingr_sr = gis.ingr_spatial_reference()
    ingr_uor_sr = gis.ingr_uor_spatial_reference()

    if arcpy.Exists(os.path.join(em.sde, 'LOCALITY_CENTROID')):
        logging.info('deleting existing locality centroid fc')
        arcpy.Delete_management(os.path.join(em.sde, 'LOCALITY_CENTROID'))

    logging.info('creating locality centroid fc')
    arcpy.CreateFeatureclass_management(out_path='in_memory',
                                        out_name='locality_centroid_temp',
                                        geometry_type='POINT')
    arcpy.AddField_management(in_table='in_memory\\locality_centroid_temp',
                              field_name='PFI',
                              field_type='LONG')
    # vicgrid coords
    arcpy.AddField_management(in_table='in_memory\\locality_centroid_temp',
                              field_name='X_VICGRID',
                              field_type='DOUBLE',
                              field_precision=12,
                              field_scale=3)
    arcpy.AddField_management(in_table='in_memory\\locality_centroid_temp',
                              field_name='Y_VICGRID',
                              field_type='DOUBLE',
                              field_precision=12,
                              field_scale=3)

    # ingr coords
    arcpy.AddField_management(in_table='in_memory\\locality_centroid_temp',
                              field_name='X_INGR',
                              field_type='DOUBLE',
                              field_precision=12,
                              field_scale=3)
    arcpy.AddField_management(in_table='in_memory\\locality_centroid_temp',
                              field_name='Y_INGR',
                              field_type='DOUBLE',
                              field_precision=12,
                              field_scale=3)

    # ingr uor coords
    arcpy.AddField_management(in_table='in_memory\\locality_centroid_temp',
                              field_name='X_INGR_UOR',
                              field_type='DOUBLE',
                              field_precision=12,
                              field_scale=3)
    arcpy.AddField_management(in_table='in_memory\\locality_centroid_temp',
                              field_name='Y_INGR_UOR',
                              field_type='DOUBLE',
                              field_precision=12,
                              field_scale=3)

    arcpy.FeatureToPoint_management(
        in_features=os.path.join(em.sde, 'LOCALITY'),
        out_feature_class='in_memory\\locality_centroid')

    logging.info('calc coordinates...')
    with arcpy.da.SearchCursor(in_table='in_memory\\locality_centroid',
                               field_names=['PFI', 'SHAPE@']) as sc, \
         arcpy.da.InsertCursor(in_table='in_memory\\locality_centroid_temp',
                               field_names=['PFI', 'SHAPE@',
                                            'X_VICGRID', 'Y_VICGRID',
                                            'X_INGR', 'Y_INGR',
                                            'X_INGR_UOR', 'Y_INGR_UOR',
                                            ]) as ic:

        for enum, (pfi, geom) in enumerate(sc):
            if enum % 100 == 0:
                logging.info(enum)
            geom_ingr = geom.projectAs(ingr_sr)
            geom_ingr_uor = geom.projectAs(ingr_uor_sr)

            ic.insertRow((pfi, geom, geom.centroid.X, geom.centroid.Y,
                          geom_ingr.centroid.X, geom_ingr.centroid.Y,
                          geom_ingr_uor.centroid.X, geom_ingr_uor.centroid.Y))
        logging.info(enum)

    logging.info('exporting...')
    arcpy.FeatureClassToFeatureClass_conversion(
        in_features='in_memory\\locality_centroid_temp',
        out_path=em.sde,
        out_name='LOCALITY_CENTROID')
def calc_address_detail(estamap_version):

    logging.info('environment')
    em = gis.ESTAMAP(estamap_version)
    ingr_sr = gis.ingr_spatial_reference()
    ingr_uor_sr = gis.ingr_uor_spatial_reference()


    logging.info('reading locality geoms')
    locality_geoms = {}
    with arcpy.da.SearchCursor(in_table=os.path.join(em.sde, 'LOCALITY'),
                               field_names=['PFI', 'NAME', 'SHAPE@WKB']) as sc:
        for pfi, locality_name, wkb in sc:
            locality_geoms[pfi] = (locality_name, shapely.prepared.prep(shapely.wkb.loads(str(wkb))))
    logging.info(len(locality_geoms))

    logging.info('building locality rtree')
    


    def stream_load_locality():
        for pfi, (locality_name, geom) in locality_geoms.iteritems():
            yield (pfi, geom.context.bounds, None)
    locality_rtree = rtree.index.Index(stream_load_locality())
    
            
    logging.info('reading lga geoms')
    lga_geoms = {}
    with arcpy.da.SearchCursor(in_table=os.path.join(em.sde, 'LGA'),
                               field_names=['PFI', 'NAME', 'SHAPE@WKB']) as sc:
        for pfi, lga_name, wkb in sc:
            lga_geoms[pfi] = (lga_name, shapely.prepared.prep(shapely.wkb.loads(str(wkb))))
    logging.info(len(lga_geoms))

    logging.info('building lga rtree')
    def stream_load_lga():
        for pfi, (lga_name, geom) in lga_geoms.iteritems():
            yield (pfi, geom.context.bounds, None)
    lga_rtree = rtree.index.Index(stream_load_lga())


    logging.info('looping ADDRESS...')
    with arcpy.da.SearchCursor(in_table=os.path.join(em.sde, 'ADDRESS'),
                               field_names=['PFI', 'SHAPE@X', 'SHAPE@Y'],
                               sql_clause=(None, 'ORDER BY PFI')) as sc, \
         arcpy.da.SearchCursor(in_table=os.path.join(em.sde, 'ADDRESS'),
                               field_names=['PFI', 'SHAPE@X', 'SHAPE@Y'],
                               spatial_reference=ingr_sr,
                               sql_clause=(None, 'ORDER BY PFI')) as sc_ingr, \
         dbpy.SQL_BULK_COPY(em.server, em.database_name, 'dbo.ADDRESS_DETAIL') as sbc:

        total_area = shapely.geometry.Point(0,0).buffer(2.5).area

        for enum, (row_vg, row_ingr) in enumerate(itertools.izip(sc, sc_ingr)):

            addr_pfi, x_vicgrid, y_vicgrid = row_vg
            _, x_ingr, y_ingr = row_ingr
            x_ingr_uor = x_ingr * 100.0
            y_ingr_uor = y_ingr * 100.0
            
            geom = shapely.geometry.Point(x_vicgrid, y_vicgrid)

            # locality
            localities = []
            locality_in_scope = locality_rtree.intersection(geom.bounds)
            for pfi in locality_in_scope:
                if locality_geoms[pfi][1].contains(geom):
                    localities.append(pfi)

            locality_name = 'UNKNOWN'
            locality_percent = 0.0
            if len(localities) == 1:
                locality_name = locality_geoms[localities[0]][0]
                locality_percent = 100.0
                
            elif len(localities) > 1:
                logging.info('within 2 localities: {}'.format(addr_pfi))
                localities_ranked = []
                
                # determine largest locality by area if contained within multiple
                for pfi in localities:
                    locality_name, locality_geom = locality_geoms[pfi]
                    geom_buffer = geom.buffer(2.5)
                    area_geom = locality_geom.context.intersection(geom_buffer)
                    locality_percent = area_geom.area / total_area
                    localities_ranked.append((pfi, locality_name, locality_geom, locality_percent))
                
                locality_name, locality_percent = max(localities_ranked, key=lambda x: x[-1])


            # lga
            lgas = []
            lga_in_scope = lga_rtree.intersection(geom.bounds)
            for pfi in lga_in_scope:
                lga_name, lga_geom = lga_geoms[pfi]
                if lga_geom.contains(geom):
                    lgas.append(lga_name)
                    
            lga_name = 'UNKNOWN'
            if len(lgas) == 1:
                lga_name = lgas[0]
            elif len(lgas) > 1:
                lga_name = sorted(lgas)[0]

            
            # self intersections
            # todo, check dependency if required
            intersect_count = 0

            sbc.add_row((addr_pfi,
                         locality_name, locality_percent,
                         x_vicgrid, y_vicgrid,
                         x_ingr, y_ingr,
                         x_ingr_uor, y_ingr_uor,
                         lga_name,
                         intersect_count))
            
            if enum % 1000 == 0:
                logging.info(enum)
            if enum % 100000 == 0:
                sbc.flush()
        logging.info(enum)
    logging.info('count start: {}'.format(sbc.count_start))
    logging.info('count finish: {}'.format(sbc.count_finish))