175 lines
6.5 KiB
TypeScript
175 lines
6.5 KiB
TypeScript
import { Pool } from "https://deno.land/x/postgres/mod.ts";
|
||
|
||
const GEBIETSTYPES: any = {
|
||
"Gewässer": 1,
|
||
"versickerungsdominiert": 1,
|
||
"verdunstungs- und versickerungsbestimmt": 0.8,
|
||
"verdunstungsdominiert": 0.5,
|
||
"verdunstungs- und abflussbestimmt": 0.2,
|
||
"abflussdominiert": 0.1,
|
||
"ausgewogen": 0.8,
|
||
};
|
||
|
||
const MODE = Deno.args[0];
|
||
if (!MODE) {
|
||
throw "No MODE arg";
|
||
}
|
||
|
||
const dbPool = new Pool({
|
||
user: "treeadvisor",
|
||
password: "123",
|
||
database: "treeadvisor",
|
||
|
||
hostname: "10.233.1.2",
|
||
}, 12);
|
||
async function withDb(f: any): Promise<any> {
|
||
const client = await dbPool.connect();
|
||
let result;
|
||
try {
|
||
result = await f(client);
|
||
} finally {
|
||
client.release();
|
||
}
|
||
return result;
|
||
}
|
||
|
||
const insertGeoenv = async (src: string, coords: any, attrs: any) => {
|
||
const coords_s = "(" + coords.map((coords: any) => `(${coords[0]},${coords[1]})`).join(",") + ")";
|
||
const attrs_s = JSON.stringify(attrs);
|
||
await withDb(async (db: any) => db.queryObject`INSERT INTO areas (src, coords, attrs) VALUES (${src}, ${coords_s}::polygon, ${attrs_s})`);
|
||
};
|
||
|
||
let pfafCache: any = {};
|
||
const insertTree = async (coords: any, props: any) => { await withDb(async (db: any) => {
|
||
const coords_s = `(${coords[0]},${coords[1]})`;
|
||
let botanic = props.art_botanisch
|
||
.replace(/,\s*/g, ", ")
|
||
.replace(/\.\s+/g, ". ")
|
||
.replace(/\s+/g, " ")
|
||
.replace(/'/g, "\"")
|
||
.replace(/\s+$/, "");
|
||
const german = props.art_deutsch && props.art_deutsch
|
||
.replace(/\s+-\s+/, "-")
|
||
.replace(/\.\s+/g, ". ")
|
||
.replace(/\s+x\s+/g, " × ")
|
||
.replace(/\s+/g, " ")
|
||
.replace(/'/g, "\"")
|
||
.replace(/\s+$/, "");
|
||
|
||
const age = parseInt(props.jalter, 10);
|
||
let planted = null;
|
||
if (age > 0) {
|
||
const m = props.aend_dat.match(/^(\d+)\.(\d+)\.(\d+) /);
|
||
if (!m) throw `Invalid date: ${props.aend_dat}`;
|
||
const y = parseInt(m[3], 10) - age;
|
||
planted = `${y}-${m[2]}-${m[1]}`;
|
||
};
|
||
|
||
let botanic_pfaf;
|
||
if (botanic == "Baumart noch nicht bestimmt" || botanic == "unbekannt" || !botanic) {
|
||
botanic = null;
|
||
} else if (!pfafCache[botanic]) {
|
||
const res: any = await db.queryObject`SELECT "Latin name" as botanic FROM "PlantsForAFuture" WHERE "Latin name" ILIKE CONCAT(${botanic.slice(0, 3)}::text, '%') ORDER BY levenshtein("Latin name", ${botanic}) ASC LIMIT 1`;
|
||
if (res.rows[0]) {
|
||
botanic_pfaf = pfafCache[botanic] = res.rows[0].botanic;
|
||
if (botanic != botanic_pfaf) console.log(botanic + " = " + botanic_pfaf);
|
||
}
|
||
} else {
|
||
botanic_pfaf = pfafCache[botanic];
|
||
}
|
||
|
||
let areaSrcSeen: any = {};
|
||
let areaInfo: any = {};
|
||
let res = await db.queryObject`SELECT src, attrs FROM areas WHERE coords @> $1::point ORDER BY coords <-> ${coords_s}::point ASC`;
|
||
res.rows.forEach(function(area: any) {
|
||
if (areaSrcSeen[area.src]) return;
|
||
|
||
Object.keys(area.attrs).forEach(function(k) {
|
||
areaInfo[k] = area.attrs[k];
|
||
});
|
||
});
|
||
let scores = [];
|
||
if (areaInfo.hasOwnProperty("versiegelung")) scores.push(1 - areaInfo.versiegelung);
|
||
if (areaInfo.hasOwnProperty("schutt")) scores.push(1 - areaInfo.schutt);
|
||
if (areaInfo.bodenqualitaet) scores.push((areaInfo.bodenqualitaet - 1) / 5);
|
||
if (areaInfo.wasserspeicher && areaInfo.wasserspeicher > 10) scores.push((50 - areaInfo.wasserspeicher) / 40)
|
||
else if (areaInfo.wasserspeicher) scores.push((5 - areaInfo.wasserspeicher) / 4);
|
||
if (areaInfo.gebietstyp) scores.push(GEBIETSTYPES[areaInfo.gebietstyp]);
|
||
let score: number | null = 0;
|
||
for(const s of scores) {
|
||
score += s;
|
||
}
|
||
if (scores.length > 0) {
|
||
score /= scores.length;
|
||
score = Math.max(0, Math.min(1, score));
|
||
} else {
|
||
score = null;
|
||
}
|
||
|
||
await db.queryObject`INSERT INTO trees (id, coord, score, botanic, botanic_pfaf, german, planted) VALUES (${props.id}, ${coords_s}, ${score}, ${botanic}, ${botanic_pfaf}, ${german}, ${planted})`;
|
||
}) };
|
||
|
||
const importFeatures = async (src: string, geojson: any) => {
|
||
if (geojson.type != "FeatureCollection") {
|
||
console.warn("Not a FeatureCollection");
|
||
return;
|
||
}
|
||
|
||
// var progress = 0, i = 0;
|
||
let promises = [];
|
||
for(const feature of geojson.features) { promises.push((async () => {
|
||
if (MODE == "GEOENV") {
|
||
if (feature.type == "Feature" &&
|
||
feature.geometry.type == "Polygon" &&
|
||
feature.geometry.crs.properties.name == "urn:ogc:def:crs:EPSG::4326") {
|
||
|
||
for(const coords of feature.geometry.coordinates) {
|
||
await insertGeoenv(src, coords, feature.properties);
|
||
}
|
||
// await insertGeoenv(feature.geometry.coordinates, feature.properties);
|
||
} else if (feature.type == "FeatureCollection") {
|
||
await importFeatures(src, feature);
|
||
} else if (feature.type == "Feature" &&
|
||
feature.geometry.type == "MultiPolygon" &&
|
||
feature.geometry.crs.properties.name == "urn:ogc:def:crs:EPSG::4326") {
|
||
|
||
for(const coordinates of feature.geometry.coordinates) {
|
||
for(const coords of coordinates) {
|
||
await insertGeoenv(src, coords, feature.properties);
|
||
}
|
||
}
|
||
} else {
|
||
console.warn("Not recognized:", feature);
|
||
// console.warn(`Not recognized: ${JSON.stringify(geojson)}`);
|
||
}
|
||
} else if (MODE == "TREES") {
|
||
if (feature.type == "Feature" &&
|
||
feature.geometry.type == "Point" &&
|
||
feature.geometry.crs.properties.name == "urn:ogc:def:crs:EPSG::4326") {
|
||
|
||
await insertTree(feature.geometry.coordinates, feature.properties);
|
||
} else {
|
||
console.warn("Not recognized:", feature);
|
||
// console.warn(`Not recognized: ${JSON.stringify(geojson)}`);
|
||
}
|
||
}
|
||
|
||
// i += 1;
|
||
// var newProgress = Math.floor(100 * i / geojson.features.length);
|
||
// if (newProgress != progress) {
|
||
// progress = newProgress;
|
||
// console.log(progress + "%");
|
||
// }
|
||
})()); }
|
||
|
||
console.log("wait for " + promises.length + " promises");
|
||
await Promise.all(promises);
|
||
};
|
||
|
||
for(const filename of Deno.args.slice(1)) {
|
||
console.log(`Load ${filename}`)
|
||
const geojson = JSON.parse(await Deno.readTextFile(filename));
|
||
|
||
await importFeatures(filename, geojson);
|
||
}
|