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);
|
|||
|
}
|