Exclude code about roads that cross borders: it needs 'crossing' and 'points' tables and there are no scripts/instructions how to fill them

This commit is contained in:
Alexey Zakharenkov 2020-10-13 13:35:02 +03:00
parent 580a1ab9ac
commit 4a3942e971
4 changed files with 37 additions and 315 deletions

View file

@ -173,99 +173,55 @@ def query_small_in_bbox():
else:
table = config.TABLE
cur = g.conn.cursor()
cur.execute('''SELECT name, round(ST_Area(geography(ring))) as area, ST_X(ST_Centroid(ring)), ST_Y(ST_Centroid(ring))
cur.execute(f"""
SELECT name, round(ST_Area(geography(ring))) as area,
ST_X(ST_Centroid(ring)), ST_Y(ST_Centroid(ring))
FROM (
SELECT name, (ST_Dump(geom)).geom as ring
FROM {table}
WHERE geom && ST_MakeBox2D(ST_Point(%s, %s), ST_Point(%s, %s))
) g
WHERE ST_Area(geography(ring)) < %s;'''.format(table=table), (xmin, ymin, xmax, ymax, config.SMALL_KM2 * 1000000))
WHERE ST_Area(geography(ring)) < %s
""", (xmin, ymin, xmax, ymax, config.SMALL_KM2 * 1000000)
)
result = []
for rec in cur:
result.append({ 'name': rec[0], 'area': rec[1], 'lon': float(rec[2]), 'lat': float(rec[3]) })
result.append({ 'name': rec[0], 'area': rec[1],
'lon': float(rec[2]), 'lat': float(rec[3]) })
return jsonify(features=result)
@app.route('/routing')
def query_routing_points():
xmin = request.args.get('xmin')
xmax = request.args.get('xmax')
ymin = request.args.get('ymin')
ymax = request.args.get('ymax')
cur = g.conn.cursor()
try:
cur.execute('''SELECT ST_X(geom), ST_Y(geom), type
FROM points
WHERE geom && ST_MakeBox2D(ST_Point(%s, %s), ST_Point(%s, %s)
);''', (xmin, ymin, xmax, ymax))
except psycopg2.Error as e:
return jsonify(features=[])
result = []
for rec in cur:
result.append({ 'lon': rec[0], 'lat': rec[1], 'type': rec[2] })
return jsonify(features=result)
@app.route('/crossing')
def query_crossing():
xmin = request.args.get('xmin')
xmax = request.args.get('xmax')
ymin = request.args.get('ymin')
ymax = request.args.get('ymax')
region = request.args.get('region', '').encode('utf-8')
points = request.args.get('points') == '1'
rank = request.args.get('rank') or '4'
cur = g.conn.cursor()
sql = """SELECT id, ST_AsGeoJSON({line}, 7) as geometry, region, processed FROM {table}
WHERE line && ST_MakeBox2D(ST_Point(%s, %s), ST_Point(%s, %s)) and processed = 0 {reg} and rank <= %s;
""".format(table=config.CROSSING_TABLE, reg='and region = %s' if region else '', line='line' if not points else 'ST_Centroid(line)')
params = [xmin, ymin, xmax, ymax]
if region:
params.append(region)
params.append(rank)
result = []
try:
cur.execute(sql, tuple(params))
for rec in cur:
props = { 'id': rec[0], 'region': rec[2], 'processed': rec[3] }
feature = { 'type': 'Feature', 'geometry': json.loads(rec[1]), 'properties': props }
result.append(feature)
except psycopg2.Error as e:
pass
return jsonify(type='FeatureCollection', features=result)
@app.route('/config')
def get_server_configuration():
osm = False
backup = False
old = []
crossing = False
try:
cur = g.conn.cursor()
cur.execute('select osm_id, ST_Area(way), admin_level, name from {} limit 2;'.format(config.OSM_TABLE))
cur.execute(f"""SELECT osm_id, ST_Area(way), admin_level, name
FROM {config.OSM_TABLE} LIMIT 2""")
if cur.rowcount == 2:
osm = True
except psycopg2.Error as e:
pass
try:
cur.execute('select backup, id, name, parent_id, ST_Area(geom), modified, disabled, count_k, cmnt from {} limit 2;'.format(config.BACKUP))
cur.execute(f"""SELECT backup, id, name, parent_id, ST_Area(geom),
modified, disabled, count_k, cmnt
FROM {config.BACKUP} LIMIT 2""")
backup = True
except psycopg2.Error as e:
pass
for t, tname in config.OTHER_TABLES.items():
try:
cur.execute('select name, ST_Area(geom), modified, disabled, count_k, cmnt from {} limit 2;'.format(tname))
cur.execute(f"""SELECT name, ST_Area(geom), modified, disabled,
count_k, cmnt
FROM {tname} LIMIT 2""")
if cur.rowcount == 2:
old.append(t)
except psycopg2.Error as e:
pass
try:
cur = g.conn.cursor()
cur.execute('select id, ST_Length(line), region, processed from {} limit 2;'.format(config.CROSSING_TABLE))
if cur.rowcount == 2:
crossing = True
except psycopg2.Error as e:
pass
return jsonify(osm=osm, tables=old, readonly=config.READONLY,
backup=backup, crossing=crossing,
return jsonify(osm=osm, tables=old,
readonly=config.READONLY,
backup=backup,
mwm_size_thr=config.MWM_SIZE_THRESHOLD)
@app.route('/search')
@ -431,7 +387,16 @@ def find_osm_borders():
lat = request.args.get('lat')
lon = request.args.get('lon')
cur = g.conn.cursor()
cur.execute("select osm_id, name, admin_level, (case when ST_Area(geography(way)) = 'NaN' then 0 else ST_Area(geography(way))/1000000 end) as area_km from {table} where ST_Contains(way, ST_SetSRID(ST_Point(%s, %s), 4326)) order by admin_level desc, name asc;".format(table=config.OSM_TABLE), (lon, lat))
cur.execute(f"""
SELECT osm_id, name, admin_level,
(CASE
WHEN ST_Area(geography(way)) = 'NaN' THEN 0
ELSE ST_Area(geography(way))/1000000
END) AS area_km
FROM {config.OSM_TABLE}
WHERE ST_Contains(way, ST_SetSRID(ST_Point(%s, %s), 4326))
ORDER BY admin_level DESC, name ASC
""", (lon, lat))
result = []
for rec in cur:
b = { 'id': rec[0], 'name': rec[1], 'admin_level': rec[2], 'area': rec[3] }
@ -483,7 +448,7 @@ def delete_border():
abort(405)
region_id = int(request.args.get('id'))
cur = g.conn.cursor()
cur.execute('delete from {} where id = %s;'.format(config.TABLE), (region_id,))
cur.execute(f"DELETE FROM {config.TABLE} WHERE id = %s", (region_id,))
g.conn.commit()
return jsonify(status='ok')
@ -883,56 +848,6 @@ def draw_hull():
g.conn.commit()
return jsonify(status='ok')
@app.route('/fixcrossing')
def fix_crossing():
if config.READONLY:
abort(405)
preview = request.args.get('preview') == '1'
region = request.args.get('region').encode('utf-8')
if region is None:
return jsonify(status='Please specify a region')
ids = request.args.get('ids')
if ids is None or len(ids) == 0:
return jsonify(status='Please specify a list of line ids')
ids = tuple(ids.split(','))
cur = g.conn.cursor()
if preview:
cur.execute("""
WITH lines as (SELECT ST_Buffer(ST_Collect(line), 0.002, 1) as g FROM {cross} WHERE id IN %s)
SELECT ST_AsGeoJSON(ST_Collect(ST_MakePolygon(er.ring))) FROM
(
SELECT ST_ExteriorRing((ST_Dump(ST_Union(ST_Buffer(geom, 0.0), lines.g))).geom) as ring FROM {table}, lines WHERE name = %s
) as er
""".format(table=config.TABLE, cross=config.CROSSING_TABLE), (ids, region))
res = cur.fetchone()
if not res:
return jsonify(status='Failed to extend geometry')
f = { "type": "Feature", "properties": {}, "geometry": json.loads(res[0]) }
#return jsonify(type="FeatureCollection", features=[f])
return jsonify(type="Feature", properties={}, geometry=json.loads(res[0]))
else:
cur.execute("""
WITH lines as (SELECT ST_Buffer(ST_Collect(line), 0.002, 1) as g FROM {cross} WHERE id IN %s)
UPDATE {table} SET geom = res.g FROM
(
SELECT ST_Collect(ST_MakePolygon(er.ring)) as g FROM
(
SELECT ST_ExteriorRing((ST_Dump(ST_Union(ST_Buffer(geom, 0.0), lines.g))).geom) as ring FROM {table}, lines WHERE name = %s
) as er
) as res
WHERE name = %s
""".format(table=config.TABLE, cross=config.CROSSING_TABLE), (ids, region, region))
cur.execute("""
UPDATE {table} b SET geom = ST_Difference(b.geom, o.geom)
FROM {table} o
WHERE ST_Overlaps(b.geom, o.geom)
AND o.name = %s
""".format(table=config.TABLE), (region,))
cur.execute("UPDATE {cross} SET processed = 1 WHERE id IN %s".format(cross=config.CROSSING_TABLE), (ids,))
g.conn.commit()
return jsonify(status='ok')
@app.route('/backup')
def backup_do():
if config.READONLY:

View file

@ -18,8 +18,6 @@ OTHER_TABLES = {
}
# backup table
BACKUP = 'borders_backup'
## table with crossing lines
CROSSING_TABLE = 'crossing'
# area of an island for it to be considered small
SMALL_KM2 = 10
# force multipolygons in JOSM output

View file

@ -14,25 +14,15 @@ var size_good, size_bad;
var maxRank = 1;
var tooSmallLayer = null;
var oldBordersLayer = null;
var routingGroup = null;
var crossingLayer = null;
function init() {
map = L.map('map', { editable: true }).setView([30, 0], 3);
var hash = new L.Hash(map);
L.tileLayer('https://tile.openstreetmap.de/{z}/{x}/{y}.png', { attribution: '&copy; OpenStreetMap' }).addTo(map);
//L.tileLayer('https://b.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png', { attribution: '&copy; OpenStreetMap' }).addTo(map);
//L.tileLayer('http://tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '&copy; OpenStreetMap' }).addTo(map);
// L.tileLayer('http://korona.geog.uni-heidelberg.de/tiles/adminb/x={x}&y={y}&z={z}',
// { attribution: '&copy; GIScience Heidelberg' }).addTo(map);
// L.tileLayer('https://tile.cyclestreets.net/boundaries/{z}/{x}/{y}.png',
// { attribution: '&copy; CycleStreets.net' }).addTo(map);
//L.tileLayer('http://tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '&copy; OpenStreetMap' }).addTo(map);
bordersLayer = L.layerGroup();
map.addLayer(bordersLayer);
routingGroup = L.layerGroup();
map.addLayer(routingGroup);
crossingLayer = L.layerGroup();
map.addLayer(crossingLayer);
map.on('moveend', function() {
if( map.getZoom() >= 5 )
@ -85,8 +75,6 @@ function getServerConfiguration() {
$('#old_action').css('display', 'block');
$('#josm_old').css('display', 'inline');
}
if( res.crossing )
$('#cross_actions').css('display', 'block');
if( res.backup ) {
$('#backups').show();
}
@ -128,34 +116,6 @@ function updateBorders() {
simplified: simplified
});
/*$.ajax(getServer('routing'), {
data: {
'xmin': b.getWest(),
'xmax': b.getEast(),
'ymin': b.getSouth(),
'ymax': b.getNorth()
},
success: processRouting,
dataType: 'json'
});
if (map.getZoom() >= 4) {
$.ajax(getServer('crossing'), {
data: {
'xmin': b.getWest(),
'xmax': b.getEast(),
'ymin': b.getSouth(),
'ymax': b.getNorth(),
'points': (map.getZoom() < 10 ? 1 : 0),
'rank': maxRank
},
success: processCrossing,
dataType: 'json'
});
} else {
crossingLayer.clearLayers();
}
if( oldBordersLayer != null && config.OLD_BORDERS_NAME ) {
oldBordersLayer.clearLayers();
$.ajax(getServer('bbox'), {
@ -167,10 +127,10 @@ function updateBorders() {
'ymin': b.getSouth(),
'ymax': b.getNorth()
},
success: processOldBorders,
success: makeAnswerHandler(processOldBorders),
dataType: 'json'
});
} */
}
}
function makeAnswerHandler(on_ok_func) {
@ -182,18 +142,6 @@ function makeAnswerHandler(on_ok_func) {
};
}
routingTypes = {1: "Border and feature are intersecting several times.",
2: "Unknown outgoing feature."};
function processRouting(data) {
routingGroup.clearLayers();
for( var f = 0; f < data.features.length; f++ ) {
marker = L.marker([data.features[f]["lat"], data.features[f]["lon"]]);
marker.bindPopup(routingTypes[data.features[f]["type"]], {showOnMouseOver: true});
routingGroup.addLayer(marker);
}
}
function processBorders(data) {
data = data.geojson;
for( var id in borders ) {
@ -240,8 +188,8 @@ function processBorders(data) {
}
function processOldBorders(data) {
var layer = L.geoJson(data, {
style: { fill: false, color: 'purple', weight: 3, clickable: false }
var layer = L.geoJson(data.geojosn, {
style: { fill: false, color: 'purple', weight: 5, clickable: false }
});
oldBordersLayer.addLayer(layer);
}
@ -1064,9 +1012,6 @@ function bDivideCancel() {
$('#actions').show();
}
function bLargest() {
if( !selectedId || !(selectedId in borders) )
return;
@ -1172,131 +1117,6 @@ function getPolyDownloadLink(use_bbox) {
return downloadLink;
}
var crossSelected = null, fcPreview = null;
var selectedCrossings = {};
function crossingUpdateColor(layer) {
if( 'setStyle' in layer )
layer.setStyle({ color: selectedCrossings[layer.crossId] ? 'red' : 'blue' });
}
function crossingClicked(e) {
if( !crossSelected )
return;
var layer = e.target;
if( 'crossId' in layer ) {
var id = layer.crossId;
if( selectedCrossings[id] )
delete selectedCrossings[id];
else
selectedCrossings[id] = true;
crossingUpdateColor(layer);
}
}
function setBordersSelectable(selectable) {
crossingLayer.eachLayer(function(l) {
l.bringToFront();
});
}
function processCrossing(data) {
crossingLayer.clearLayers();
for( var f = 0; f < data.features.length; f++ ) {
var layer = L.GeoJSON.geometryToLayer(data.features[f].geometry),
props = data.features[f].properties;
layer.crossId = '' + props.id;
layer.crossRegion = props.region;
crossingUpdateColor(layer);
layer.on('click', crossingClicked);
crossingLayer.addLayer(layer);
}
}
function selectCrossingByRegion(region) {
if( region ) {
crossingLayer.eachLayer(function(l) {
if( l.crossId && l.crossRegion == region ) {
selectedCrossings[l.crossId] = true;
crossingUpdateColor(l);
}
});
} else {
crossingLayer.eachLayer(function(l) {
if( l.crossId ) {
delete selectedCrossings[l.crossId];
crossingUpdateColor(l);
}
});
}
}
function bFixCross() {
if( !selectedId || !(selectedId in borders) )
return;
setBordersSelectable(false);
crossSelected = selectedId;
fcPreview = null;
$('#actions').css('display', 'none');
$('#fc_sel').text(crossSelected);
$('#fc_do').css('display', 'none');
$('#fixcross').css('display', 'block');
selectCrossingByRegion(crossSelected);
}
function bFixCrossPreview() {
if( fcPreview != null ) {
map.removeLayer(fcPreview);
fcPreview = null;
}
$('#fc_do').css('display', 'none');
$.ajax(getServer('fixcrossing'), {
data: {
'preview': 1,
'region': crossSelected,
'ids': Object.keys(selectedCrossings).join(',')
},
success: bFixCrossDrawPreview
});
}
function bFixCrossDrawPreview(geojson) {
if( !('geometry' in geojson) ) {
return;
}
fcPreview = L.geoJson(geojson, {
style: function(f) {
return { color: 'red', weight: 1, fill: false };
}
});
map.addLayer(fcPreview);
$('#fc_do').css('display', 'block');
}
function bFixCrossDo() {
$.ajax(getServer('fixcrossing'), {
data: {
'region': crossSelected,
'ids': Object.keys(selectedCrossings).join(',')
},
success: updateBorders
});
bFixCrossCancel();
}
function bFixCrossCancel() {
if( fcPreview != null ) {
map.removeLayer(fcPreview);
fcPreview = null;
}
crossSelected = null;
selectCrossingByRegion(false);
selectedCrossings = {};
updateBorders();
$('#actions').css('display', 'block');
$('#fixcross').css('display', 'none');
}
function startOver() {
if (confirm('Вы уверены, что хотите начать разбиение сначала?')) {
finishChooseParent();
@ -1306,7 +1126,6 @@ function startOver() {
bPointCancel();
bDivideCancel();
bBackupCancel();
bFixCrossCancel();
selectLayer(null);
$('#wait_start_over').show();
$.ajax(getServer('start_over'), {

View file

@ -23,12 +23,12 @@
#info { margin-top: 1em; }
#b_delete, #b_clear, .back_del { font-size: 8pt; }
#split, #join, #join_to_parent, #point,
#divide, #backup, #fixcross { display: none; }
#divide, #backup { display: none; }
.actions input[type='text'], #search input[type='text'] { width: 150px; }
#header { border-bottom: 1px solid gray; margin-bottom: 1em; padding-bottom: 1em; }
#f_topo, #f_chars, #f_comments, #links { font-size: 10pt; }
#backup_saving, #backup_restoring { margin-bottom: 1em; }
#filefm, #old_action, #josm_old, #cross_actions { display: none; }
#filefm, #old_action, #josm_old { display: none; }
#h_iframe { display: block; width: 100%; height: 80px; }
a, a:hover, a:visited { color: blue; }
#start_over, #start_over:hover, #start_over:visited { color: red; }
@ -226,16 +226,6 @@
<div id="backup_list"></div>
<button onclick="bBackupCancel()">Вернуться</button>
</div>
<div id="fixcross" class="actions">
Границы региона <span id="fc_sel"></span> будут поправлены, чтобы включать в себя подсвеченные красным линии.
Кликайте на линии, чтобы изменять их статус.<br>
<br>
<button onclick="bFixCrossPreview()">Посмотреть, что получится</button><br>
<div id="fc_do">
<button onclick="bFixCrossDo()">Включить линии в контур</button>
</div>
<button onclick="bFixCrossCancel()">Вернуться</button>
</div>
</div>
<div id="map"></div>
</body>