def create_display_boundaries(pg_cur, settings): # Step 3 of 3 : create web optimised versions of the census boundaries start_time = datetime.now() # create schema if settings['web_schema'] != "public": pg_cur.execute( "CREATE SCHEMA IF NOT EXISTS {0} AUTHORIZATION {1}".format( settings['web_schema'], settings['pg_user'])) # prepare boundaries for all tiled map zoom levels create_sql_list = list() insert_sql_list = list() vacuum_sql_list = list() for boundary_dict in settings['bdy_table_dicts']: boundary_name = boundary_dict["boundary"] if boundary_name != "mb": id_field = boundary_dict["id_field"] name_field = boundary_dict["name_field"] area_field = boundary_dict["area_field"] input_pg_table = "{0}_{1}_aust".format(boundary_name, settings["census_year"]) pg_table = "{0}".format(boundary_name) # build create table statement create_table_list = list() create_table_list.append("DROP TABLE IF EXISTS {0}.{1} CASCADE;") create_table_list.append("CREATE TABLE {0}.{1} (") # build column list column_list = list() column_list.append("id text NOT NULL PRIMARY KEY") column_list.append("name text NOT NULL") column_list.append("area double precision NOT NULL") column_list.append("population double precision NOT NULL") column_list.append("geom geometry(MultiPolygon, 4283) NULL") for zoom_level in range(4, 18): display_zoom = str(zoom_level).zfill(2) column_list.append( "geojson_{0} jsonb NOT NULL".format(display_zoom)) # add columns to create table statement and finish it create_table_list.append(",".join(column_list)) create_table_list.append(") WITH (OIDS=FALSE);") create_table_list.append("ALTER TABLE {0}.{1} OWNER TO {2};") create_table_list.append( "CREATE INDEX {1}_geom_idx ON {0}.{1} USING gist (geom);") create_table_list.append( "ALTER TABLE {0}.{1} CLUSTER ON {1}_geom_idx") sql = "".join(create_table_list).format(settings['web_schema'], pg_table, settings['pg_user']) create_sql_list.append(sql) # get population field and table if boundary_name[:1] == "i": pop_stat = settings['indigenous_population_stat'] pop_table = settings['indigenous_population_table'] else: pop_stat = settings['population_stat'] pop_table = settings['population_table'] # print(boundary_name) # print(pop_stat + " - " + pop_table) # build insert statement insert_into_list = list() insert_into_list.append("INSERT INTO {0}.{1}".format( settings['web_schema'], pg_table)) insert_into_list.append( "SELECT bdy.{0} AS id, {1} AS name, SUM(bdy.{2}) AS area, tab.{3} AS population," .format(id_field, name_field, area_field, pop_stat)) # thin geometry to make querying faster tolerance = utils.get_tolerance(10) insert_into_list.append( "ST_Transform(ST_Multi(ST_Union(ST_SimplifyVW(" "ST_Transform(geom, 3577), {0}))), 4283),".format(tolerance, )) # create statements for geojson optimised for each zoom level geojson_list = list() for zoom_level in range(4, 18): # thin geometries to a default tolerance per zoom level tolerance = utils.get_tolerance(zoom_level) # trim coords to only the significant ones decimal_places = utils.get_decimal_places(zoom_level) geojson_list.append( "ST_AsGeoJSON(ST_Transform(ST_Multi(ST_Union(ST_SimplifyVW(ST_Transform(" "bdy.geom, 3577), {0}))), 4283), {1})::jsonb".format( tolerance, decimal_places)) insert_into_list.append(",".join(geojson_list)) insert_into_list.append("FROM {0}.{1} AS bdy".format( settings['boundary_schema'], input_pg_table)) insert_into_list.append("INNER JOIN {0}.{1}_{2} AS tab".format( settings['data_schema'], boundary_name, pop_table)) insert_into_list.append("ON bdy.{0} = tab.{1}".format( id_field, settings["region_id_field"])) insert_into_list.append("WHERE bdy.geom IS NOT NULL") insert_into_list.append("GROUP BY {0}, {1}, {2}".format( id_field, name_field, pop_stat)) sql = " ".join(insert_into_list) insert_sql_list.append(sql) vacuum_sql_list.append("VACUUM ANALYZE {0}.{1}".format( settings['web_schema'], pg_table)) # print("\n".join(insert_sql_list)) utils.multiprocess_list("sql", create_sql_list, settings, logger) utils.multiprocess_list("sql", insert_sql_list, settings, logger) utils.multiprocess_list("sql", vacuum_sql_list, settings, logger) logger.info( "\t- Step 3 of 3 : web optimised boundaries created : {0}".format( datetime.now() - start_time))
def denominations_combinations( currency: Currency, amount: float, max_solutions=1_000) -> Tuple[int, List[Dict[float, int]]]: """ This function returns the number of different ways that value given as parameter can be achieved by using all the possible combinations of the denominations of the given currency. :param currency: currency to use :param amount: the total amount to be achieved :param max_solutions: the maximum number of solutions that must be returned. After 1_000, memory usage increases :return: the number of all the possible combinations of denominations that match the amount """ amount = round(amount, 2) amount_decimal_places = get_decimal_places(amount) den_decimal_places = 0 denominations = [] unused_denominations = [] for den in currency.iter_denominations(): if den <= amount: den_decimal_places = max(den_decimal_places, get_decimal_places(round( den, 2))) # max is needed denominations.append(round(den, 2)) else: unused_denominations.append(den) if amount_decimal_places > den_decimal_places: # if the smallest denomination has less decimal places than the amount, then a solution does not exist return 0, [] # the amount and the denominations are multiplied by the minimum possible value # e.g. if amount = 2.20 and den = [0.10, 0.20, 0.50] then everything is multiplied by 10^1 amount = int(round(amount, 2) * (10**den_decimal_places)) int_denominations = [ int(denomination * (10**den_decimal_places)) for denomination in denominations ] int2real = {k: v for k, v in zip(int_denominations, denominations)} # dynamic programming solution base_sol = {denomination: 0 for denomination in denominations} sol = [[(0, [])] * (amount + 1) for _ in range(len(int_denominations))] for i in range(len(int_denominations)): for j in range(amount + 1): if j == 0: sol[i][j] = (1, [base_sol] ) # base solution, take zero denominations continue if i == 0: if j % int_denominations[i] == 0: d_sol = dict(base_sol) d_sol[int2real[ int_denominations[i]]] = j // int_denominations[i] sol[i][j] = (1, [d_sol]) else: sol[i][j] = (0, []) continue if j >= int_denominations[i]: n_sol = sol[i - 1][j][0] + sol[i][j - int_denominations[i]][0] d_sol = [] d_sol += sol[i - 1][j][1][:] d_sol += add_den_usage(int2real[int_denominations[i]], sol[i][j - int_denominations[i]][1], max_solutions=max_solutions) sol[i][j] = (n_sol, d_sol[-max_solutions:]) else: sol[i][j] = (sol[i - 1][j][0], sol[i - 1][j][1][:]) sol[i - 1][j] = (sol[i - 1][j][0], []) # Memory usage optimization unused_sol = {denomination: 0 for denomination in unused_denominations} for s in sol[-1][-1][1]: s.update(unused_sol) return sol[-1][-1]