Repair 'Extract islands' operation; add automatic recalculation of mwm size estimation after some operations

This commit is contained in:
Alexey Zakharenkov 2020-10-16 11:03:54 +03:00
parent ce461536a4
commit d46dca1df1
5 changed files with 87 additions and 21 deletions

View file

@ -25,7 +25,11 @@ from countries_structure import (
create_countries_initial_structure,
get_osm_border_name_by_osm_id,
)
from subregions import get_subregions_info
from subregions import (
get_subregions_info,
update_border_mwm_size_estimation,
)
try:
from lxml import etree
@ -279,6 +283,7 @@ def split():
base_name = name
# insert new geometries
counter = 1
new_ids = []
free_id = get_free_id()
for geom in geometries:
cur.execute(f"""
@ -286,8 +291,11 @@ def split():
VALUES (%s, %s, ST_GeomFromText(%s, 4326), %s, -1, now(), %s)
""", (free_id, f'{base_name}_{counter}', geom, disabled, parent_id)
)
new_ids.append(free_id)
counter += 1
free_id -= 1
for border_id in new_ids:
update_border_mwm_size_estimation(g.conn, border_id)
g.conn.commit()
return jsonify(status='ok')
@ -430,6 +438,7 @@ def copy_from_osm():
""", (osm_id,)
)
assign_region_to_lowerst_parent(osm_id)
update_border_mwm_size_estimation(g.conn, osm_id)
g.conn.commit()
return jsonify(status='ok')
@ -818,23 +827,35 @@ def divide_into_clusters(region_ids, next_level, mwm_size_thr):
def chop_largest_or_farthest():
if config.READONLY:
abort(405)
name = request.args.get('name').encode('utf-8')
region_id = int(request.args.get('id'))
table = config.TABLE
cur = g.conn.cursor()
cur.execute('select ST_NumGeometries(geom) from {} where name = %s;'.format(config.TABLE), (name,))
cur.execute(f"""SELECT ST_NumGeometries(geom)
FROM {table}
WHERE id = {region_id}""")
res = cur.fetchone()
if not res or res[0] < 2:
return jsonify(status='border should have more than one outer ring')
cur.execute("""INSERT INTO {table} (name, disabled, modified, geom)
SELECT name, disabled, modified, geom from
free_id1 = get_free_id()
free_id2 = free_id1 - 1
cur.execute(f"""
INSERT INTO {table} (id, parent_id, name, disabled, modified, geom)
SELECT id, region_id, name, disabled, modified, geom FROM
(
(WITH w AS (SELECT name, disabled, (ST_Dump(geom)).geom AS g FROM {table} WHERE name = %s)
(SELECT name||'_main' as name, disabled, now() as modified, g as geom, ST_Area(g) as a FROM w ORDER BY a DESC LIMIT 1)
UNION ALL
SELECT name||'_small' as name, disabled, now() as modified, ST_Collect(g) AS geom, ST_Area(ST_Collect(g)) as a
FROM (SELECT name, disabled, g, ST_Area(g) AS a FROM w ORDER BY a DESC OFFSET 1) ww
GROUP BY name, disabled)
) x;""".format(table=config.TABLE), (name,))
cur.execute('delete from {} where name = %s;'.format(config.TABLE), (name,))
(WITH w AS (SELECT name, disabled, (ST_Dump(geom)).geom AS g
FROM {table} WHERE id = {region_id})
(SELECT {free_id1} id, {region_id} region_id, name||'_main' as name, disabled,
now() as modified, g as geom, ST_Area(g) as a
FROM w ORDER BY a DESC LIMIT 1)
UNION ALL
SELECT {free_id2} id, {region_id} region_id, name||'_small' as name, disabled,
now() as modified, ST_Collect(g) AS geom,
ST_Area(ST_Collect(g)) as a
FROM (SELECT name, disabled, g, ST_Area(g) AS a FROM w ORDER BY a DESC OFFSET 1) ww
GROUP BY name, disabled)
) x""")
for border_id in (free_id1, free_id2):
update_border_mwm_size_estimation(g.conn, border_id)
g.conn.commit()
return jsonify(status='ok')

View file

@ -12,7 +12,14 @@ class MwmSizePredictor:
with open(config.MWM_SIZE_PREDICTION_MODEL_SCALER_PATH, 'rb') as f:
self.scaler = pickle.load(f)
def predict(self, features_array):
@classmethod
def _get_instance(cls):
if not hasattr(cls, '_instance'):
cls._instance = cls()
return cls._instance
@classmethod
def predict(cls, features_array):
"""1D or 2D array of feature values for predictions. Features are
'urban_pop', 'area', 'city_cnt', 'hamlet_cnt' as defined for the
prediction model.
@ -21,8 +28,10 @@ class MwmSizePredictor:
one_prediction = (X.ndim == 1)
if one_prediction:
X = X.reshape(1, -1)
X_scaled = self.scaler.transform(X)
predictions = self.model.predict(X_scaled)
predictor = cls._get_instance()
X_scaled = predictor.scaler.transform(X)
predictions = predictor.model.predict(X_scaled)
if one_prediction:
return predictions[0]
else:

View file

@ -1040,8 +1040,8 @@ function bLargest() {
if( !selectedId || !(selectedId in borders) )
return;
$.ajax(getServer('chop1'), {
data: { 'name': selectedId },
success: updateBorders
data: { 'id': selectedId },
success: makeAnswerHandler(updateBorders)
});
}

View file

@ -4,7 +4,6 @@ from mwm_size_predictor import MwmSizePredictor
osm_table = config.OSM_TABLE
osm_places_table = config.OSM_PLACES_TABLE
size_predictor = MwmSizePredictor()
def get_subregions_info(conn, region_id, region_table,
@ -93,10 +92,47 @@ def _add_mwm_size_estimation(subregions):
]
feature_array = [x[1] for x in subregions_sorted]
predictions = size_predictor.predict(feature_array)
predictions = MwmSizePredictor.predict(feature_array)
for subregion_id, mwm_size_prediction in zip(
(x[0] for x in subregions_sorted),
predictions
):
subregions[subregion_id]['mwm_size_est'] = mwm_size_prediction
def update_border_mwm_size_estimation(conn, border_id):
table = config.TABLE
cursor = conn.cursor()
cursor.execute(f"""
SELECT ST_Area(geography(geom))/1.0E+6 area
FROM {table}
WHERE id = %s""", (border_id, ))
rec = cursor.fetchone()
border_data = {
'area': rec[0],
'urban_pop': 0,
'city_cnt': 0,
'hamlet_cnt': 0
}
cursor.execute(f"""
SELECT COALESCE(p.population, 0), p.place
FROM {table} b, {config.OSM_PLACES_TABLE} p
WHERE id = %s
AND ST_CONTAINS(b.geom, p.center)
""", (border_id, ))
for place_population, place_type in cursor:
if place_type in ('city', 'town'):
border_data['city_cnt'] += 1
border_data['urban_pop'] += place_population
else:
border_data['hamlet_cnt'] += 1
feature_array = [
border_data[f] for f in
('urban_pop', 'area', 'city_cnt', 'hamlet_cnt')
]
mwm_size_est = MwmSizePredictor.predict(feature_array)
cursor.execute(f"UPDATE {table} SET mwm_size_est = %s WHERE id = %s",
(mwm_size_est, border_id))
conn.commit()

View file

@ -121,7 +121,7 @@
<button onclick="bSplit()">Разрезать</button>
<button onclick="bJoin()">Склеить</button><br>
<button onclick="bJoinToParent()">Склеить всё до родителя</button><br>
<button disabled onclick="bLargest()">Выделить острова</button>
<button onclick="bLargest()">Выделить острова</button>
<button disabled onclick="bHull()">Единый контур</button><br>
<button id="b_divide" onclick="bDivide()">Заменить регионами</button><br>
</div>