text-to-map 1.1.16 → 1.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -0
- package/dist/bin/parse-ordinance.js +2 -2
- package/dist/db/address-points.d.ts +9 -10
- package/dist/db/address-points.js +41 -29
- package/dist/db/founders.d.ts +4 -0
- package/dist/db/founders.js +18 -5
- package/dist/street-markdown/municipality.d.ts +5 -3
- package/dist/street-markdown/municipality.js +29 -25
- package/dist/street-markdown/smd-parser.d.ts +2 -0
- package/dist/street-markdown/smd-parser.js +63 -13
- package/dist/street-markdown/smd.d.ts +5 -1
- package/dist/street-markdown/smd.js +119 -63
- package/dist/street-markdown/token-definition.d.ts +3 -1
- package/dist/street-markdown/token-definition.js +34 -21
- package/dist/street-markdown/types.d.ts +16 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,5 +16,54 @@ Text To Map usiluje o lepší, strojově zpracovatelné využití částí vyhl
|
|
|
16
16
|
|
|
17
17
|
Pro vyzkoušení je možné použít již vyčištěné vyhlášky ze složky *examples*.
|
|
18
18
|
|
|
19
|
+
## Street Markdown
|
|
20
|
+
Street Markdown je formát, který po vzoru značkovacího jazyka [Markdown](https://en.wikipedia.org/wiki/Markdown) má za cíl usnadnit psaní strukturovaných dat bez nutnosti učit se složité formátovací tagy nebo používat speciální znaky. Vznikl na základě [pražské spádové vyhlášky z roku 2021](https://www.praha.eu/file/3251117/vyhlaska_c._4.pdf) a snaží se být co nejkompatibilnější s již používaným formátem.
|
|
21
|
+
|
|
22
|
+
Street Markdown je dále možný rozšiřovat pro další případy použití - například protialkoholová vyhláška (do standardu by byla třeba přidat definice parků), tržní vyhláška, vyhláška o buskingu a další.
|
|
23
|
+
|
|
24
|
+
### Pravidla Street Markdownu
|
|
25
|
+
|
|
26
|
+
* **prázdné znaky a řádky**
|
|
27
|
+
* veškeré po sobě jdoucí mezery jsou ignorovány a počítají se jako jedna mezera
|
|
28
|
+
* prázdné řádky mají ve formátu speciální význam, několik prázdných řádků za sebou se ignoruje a počítá se jako jeden prázdný řádek
|
|
29
|
+
* **blok oblasti**
|
|
30
|
+
* v případě SMD pro více obcí či městských částí je uvozen blok oblasti pomocí _názvu oblasti_, následuje prázdný řádek a poté libovolný počet _bloků škol_
|
|
31
|
+
* _blok oblasti_ končí řádkem před _nadpisem oblasti_ následujícího _bloku obce_ nebo koncem souboru
|
|
32
|
+
* **název oblasti**
|
|
33
|
+
* začíná znakem "#" následovaný mezerou a textovým řetězcem, který obsahuje přesný název obce
|
|
34
|
+
* příklady:
|
|
35
|
+
* \# Praha 1
|
|
36
|
+
* \# Pelhřimov
|
|
37
|
+
* \# Poděbrady
|
|
38
|
+
* **blok školy**
|
|
39
|
+
* jde o souvislý blok bez vynechaných řádků
|
|
40
|
+
* bloky škol jsou od sebe odděleny jedním či více prázdnými řádky
|
|
41
|
+
* **název školy**
|
|
42
|
+
* první řádek bloku řádků je název školy
|
|
43
|
+
* libovolný neprázdný textový řetězec
|
|
44
|
+
* **definice ulice**
|
|
45
|
+
* definice ulice obsahuje _jméno ulice_ a volitelně _specifikaci orientačních čísel_
|
|
46
|
+
* pokud celá ulice patří do aktuálního bloku školy, pak se specifikace orientačních čísel neuvádí, uvede se pouze název ulice
|
|
47
|
+
* název ulice se uvádí v plně rozvinutém tvaru
|
|
48
|
+
* špatně: nám. Míru, Rašínovo nábř.
|
|
49
|
+
* správně: náměstí Míru, Rašínovo nábřeží
|
|
50
|
+
* jméno ulice a specifikace ulice je odděleno znaménkem minus (a volitelnými mezerami okolo pomlčky) - místo minus jsou přípustné i pomlčka a spojovník
|
|
51
|
+
* **specifikace orientačních čísel**
|
|
52
|
+
* za sebou jdoucí výčet _rozsahů čísel_
|
|
53
|
+
* _rozsahy čísel_ jsou odděleny čárkou a mezerou (", ") nebo spojkou " a "
|
|
54
|
+
* **rozsah čísel**
|
|
55
|
+
* jako rozsah čísel se považuje specifikace, zda jde o _typ čísel_ (všechna, sudá či lichá čísla) a _rozsah_ od-do, a to v následujícím formátu:
|
|
56
|
+
* **typ čísel**
|
|
57
|
+
* všechna = "č."
|
|
58
|
+
* lichá = "lichá č."
|
|
59
|
+
* sudá = "sudá č."
|
|
60
|
+
* čísla popisná = "č. p."
|
|
61
|
+
* **rozsah čísel**
|
|
62
|
+
* může jít o výčet (oddělený ", " nebo " a ") těchto možností:
|
|
63
|
+
* a) <číslo>, např. 7
|
|
64
|
+
* b) <číslo>-<číslo>, např. 2-66 (přípustné jsou pomlčka, spojovník, minus)
|
|
65
|
+
* c) (od) <číslo> (a) výše, např. od 20 výše, 14 a výše, od 7 a výše
|
|
66
|
+
* d) do (č.) <číslo>
|
|
67
|
+
* např. 1-9, 11, od 23 výše
|
|
19
68
|
|
|
20
69
|
[](https://ko-fi.com/E1E5JOMLT)
|
|
@@ -8,7 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { readFileSync, writeFileSync } from "fs";
|
|
11
|
-
import {
|
|
11
|
+
import { getNewMunicipalityByName, parseOrdinanceToAddressPoints, } from "../street-markdown/smd";
|
|
12
12
|
function main() {
|
|
13
13
|
return __awaiter(this, void 0, void 0, function* () {
|
|
14
14
|
// take first node argument as a file name
|
|
@@ -35,7 +35,7 @@ function main() {
|
|
|
35
35
|
warningCount++;
|
|
36
36
|
};
|
|
37
37
|
console.time("downloadAndImportAllLatestAddressPoints");
|
|
38
|
-
const { municipality } = yield
|
|
38
|
+
const { municipality } = yield getNewMunicipalityByName("Česká Lípa");
|
|
39
39
|
const addressPoints = yield parseOrdinanceToAddressPoints(lines, {
|
|
40
40
|
// currentMunicipality: municipality,
|
|
41
41
|
}, reportErrors, reportWarnings);
|
|
@@ -33,27 +33,26 @@ export declare const checkStreetExists: (streetName: string, founder: Founder) =
|
|
|
33
33
|
exists: boolean;
|
|
34
34
|
errors: SmdError[];
|
|
35
35
|
}>;
|
|
36
|
-
export declare enum FindAddressPointsType {
|
|
37
|
-
SmdLine = 0,
|
|
38
|
-
MunicipalityPart = 1,
|
|
39
|
-
WholeMunicipality = 2,
|
|
40
|
-
WholeMunicipalityNoStreetName = 3
|
|
41
|
-
}
|
|
42
36
|
export type FindAddressPointsParams = {
|
|
43
|
-
type:
|
|
37
|
+
type: "smdLine";
|
|
44
38
|
smdLine: SmdLine;
|
|
45
39
|
municipality: Municipality;
|
|
40
|
+
municipalityPartCode?: number;
|
|
46
41
|
} | {
|
|
47
|
-
type:
|
|
42
|
+
type: "wholeMunicipalityPart";
|
|
48
43
|
municipalityPartCode: number;
|
|
49
44
|
} | {
|
|
50
|
-
type:
|
|
45
|
+
type: "wholeMunicipality";
|
|
51
46
|
municipality: Municipality;
|
|
52
47
|
} | {
|
|
53
|
-
type:
|
|
48
|
+
type: "wholeMunicipalityNoStreetName";
|
|
54
49
|
municipality: Municipality;
|
|
55
50
|
};
|
|
56
51
|
export declare const findAddressPoints: (params: FindAddressPointsParams) => Promise<AddressPoint[]>;
|
|
52
|
+
export declare const filterAddressPoints: (addressPoints: AddressPoint[], params: FindAddressPointsParams) => AddressPoint[];
|
|
53
|
+
export declare const getQueryParams: (params: FindAddressPointsParams) => (string | number)[];
|
|
54
|
+
export declare const getStreetJoinCondition: (params: FindAddressPointsParams) => string;
|
|
55
|
+
export declare const getWhereCondition: (params: FindAddressPointsParams) => string;
|
|
57
56
|
export declare const isInRange: (number: number | null, letter: string | null, range: RangeSpec) => boolean;
|
|
58
57
|
export declare const fitsType: (number: number | null, type: SeriesType) => boolean;
|
|
59
58
|
export declare const equalsFullStreetNumber: (fullStreetNumber: FullStreetNumber, addressPoint: AddressPoint) => boolean;
|
|
@@ -183,29 +183,11 @@ const getAllStreets = (cityCode) => __awaiter(void 0, void 0, void 0, function*
|
|
|
183
183
|
const knex = getKnexDb();
|
|
184
184
|
return yield knex.pluck("name").from("street").where("city_code", cityCode);
|
|
185
185
|
});
|
|
186
|
-
export var FindAddressPointsType;
|
|
187
|
-
(function (FindAddressPointsType) {
|
|
188
|
-
FindAddressPointsType[FindAddressPointsType["SmdLine"] = 0] = "SmdLine";
|
|
189
|
-
FindAddressPointsType[FindAddressPointsType["MunicipalityPart"] = 1] = "MunicipalityPart";
|
|
190
|
-
FindAddressPointsType[FindAddressPointsType["WholeMunicipality"] = 2] = "WholeMunicipality";
|
|
191
|
-
FindAddressPointsType[FindAddressPointsType["WholeMunicipalityNoStreetName"] = 3] = "WholeMunicipalityNoStreetName";
|
|
192
|
-
})(FindAddressPointsType || (FindAddressPointsType = {}));
|
|
193
186
|
export const findAddressPoints = (params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
194
187
|
const knex = getKnexDb();
|
|
195
|
-
const queryParams = params
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
? [params.municipalityPartCode]
|
|
199
|
-
: [params.municipality.code];
|
|
200
|
-
const streetJoinCondition = params.type === FindAddressPointsType.SmdLine
|
|
201
|
-
? `JOIN street s ON a.street_code = s.code AND s.name = ? ${isSqlite(knex) ? "COLLATE NOCASE" : ""}`
|
|
202
|
-
: "LEFT JOIN street s ON a.street_code = s.code";
|
|
203
|
-
const whereCondition = params.type === FindAddressPointsType.MunicipalityPart
|
|
204
|
-
? "a.municipality_part_code = ?"
|
|
205
|
-
: getMunicipalityWhere("a", params.municipality) +
|
|
206
|
-
(params.type === FindAddressPointsType.WholeMunicipalityNoStreetName
|
|
207
|
-
? " AND a.street_code IS NULL"
|
|
208
|
-
: "");
|
|
188
|
+
const queryParams = getQueryParams(params);
|
|
189
|
+
const streetJoinCondition = getStreetJoinCondition(params);
|
|
190
|
+
const whereCondition = getWhereCondition(params);
|
|
209
191
|
const queryResult = yield rawQuery(`SELECT a.id, s.name AS street_name, o.name AS object_type_name, a.descriptive_number,
|
|
210
192
|
a.orientational_number, a.orientational_number_letter, c.name AS city_name,
|
|
211
193
|
d.name AS district_name, m.name AS municipality_part_name, p.name AS prague_district_name,
|
|
@@ -218,28 +200,32 @@ export const findAddressPoints = (params) => __awaiter(void 0, void 0, void 0, f
|
|
|
218
200
|
LEFT JOIN municipality_part m ON a.municipality_part_code = m.code
|
|
219
201
|
LEFT JOIN prague_district p ON a.prague_district_code = p.code
|
|
220
202
|
WHERE ${whereCondition}`, queryParams);
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
203
|
+
return filterAddressPoints(queryResult.map(rowToAddressPoint), params);
|
|
204
|
+
});
|
|
205
|
+
export const filterAddressPoints = (addressPoints, params) => {
|
|
206
|
+
if (params.type === "wholeMunicipality" ||
|
|
207
|
+
params.type === "wholeMunicipalityNoStreetName" ||
|
|
208
|
+
params.type === "wholeMunicipalityPart") {
|
|
209
|
+
return addressPoints;
|
|
224
210
|
}
|
|
225
211
|
const result = [];
|
|
226
212
|
const numberSpec = params.smdLine.numberSpec;
|
|
227
213
|
if (isSeriesSpecArray(numberSpec)) {
|
|
228
214
|
numberSpec.forEach((seriesSpec) => {
|
|
229
|
-
result.push(...filterAddressPointsByRanges(
|
|
215
|
+
result.push(...filterAddressPointsByRanges(addressPoints, seriesSpec));
|
|
230
216
|
});
|
|
231
217
|
// when no number specs are present, we take everything
|
|
232
218
|
if (numberSpec.length === 0) {
|
|
233
|
-
result.push(...
|
|
219
|
+
result.push(...addressPoints);
|
|
234
220
|
}
|
|
235
221
|
}
|
|
236
222
|
else if (isNegativeSeriesSpec(numberSpec)) {
|
|
237
223
|
// negative number spec (e.g. odd numbers except 13-17)
|
|
238
|
-
const toExclude = filterAddressPointsByRanges(
|
|
239
|
-
result.push(...
|
|
224
|
+
const toExclude = filterAddressPointsByRanges(addressPoints, numberSpec);
|
|
225
|
+
result.push(...addressPoints.filter((addressPoint) => !toExclude.includes(addressPoint)));
|
|
240
226
|
}
|
|
241
227
|
return result;
|
|
242
|
-
}
|
|
228
|
+
};
|
|
243
229
|
const filterAddressPointsByRanges = (addressPoints, seriesSpec) => {
|
|
244
230
|
const result = [];
|
|
245
231
|
seriesSpec.ranges.forEach((range) => {
|
|
@@ -263,6 +249,32 @@ const filterAddressPointsByRanges = (addressPoints, seriesSpec) => {
|
|
|
263
249
|
}
|
|
264
250
|
return result;
|
|
265
251
|
};
|
|
252
|
+
export const getQueryParams = (params) => {
|
|
253
|
+
if (params.type === "smdLine" && params.smdLine.type === "street") {
|
|
254
|
+
return [params.smdLine.street, params.municipality.code];
|
|
255
|
+
}
|
|
256
|
+
if ((params.type === "smdLine" && params.smdLine.type === "municipalityPart") ||
|
|
257
|
+
params.type === "wholeMunicipalityPart") {
|
|
258
|
+
return [params.municipalityPartCode];
|
|
259
|
+
}
|
|
260
|
+
return [params.municipality.code];
|
|
261
|
+
};
|
|
262
|
+
export const getStreetJoinCondition = (params) => {
|
|
263
|
+
if (params.type === "smdLine" && params.smdLine.type === "street") {
|
|
264
|
+
return `JOIN street s ON a.street_code = s.code AND s.name = ? ${isSqlite(getKnexDb()) ? "COLLATE NOCASE" : ""}`;
|
|
265
|
+
}
|
|
266
|
+
return "LEFT JOIN street s ON a.street_code = s.code";
|
|
267
|
+
};
|
|
268
|
+
export const getWhereCondition = (params) => {
|
|
269
|
+
if ((params.type === "smdLine" && params.smdLine.type === "municipalityPart") ||
|
|
270
|
+
params.type === "wholeMunicipalityPart") {
|
|
271
|
+
return "a.municipality_part_code = ?";
|
|
272
|
+
}
|
|
273
|
+
if (params.type === "wholeMunicipalityNoStreetName") {
|
|
274
|
+
return `${getMunicipalityWhere("a", params.municipality)} AND a.street_code IS NULL`;
|
|
275
|
+
}
|
|
276
|
+
return getMunicipalityWhere("a", params.municipality);
|
|
277
|
+
};
|
|
266
278
|
export const isInRange = (number, letter, range) => {
|
|
267
279
|
if (number === null) {
|
|
268
280
|
return !range.from && !range.to;
|
package/dist/db/founders.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { DbMunicipalityResult, MunicipalityPartResult, SmdError } from "../street-markdown/types";
|
|
2
2
|
import { Founder, MunicipalityType } from "./types";
|
|
3
3
|
export declare const insertFounders: (founders: Founder[]) => Promise<number>;
|
|
4
|
+
export declare const getFounderById: (id: number) => Promise<{
|
|
5
|
+
founder: Founder;
|
|
6
|
+
errors: SmdError[];
|
|
7
|
+
}>;
|
|
4
8
|
export declare const findFounder: (nameWithHashtag: string) => Promise<{
|
|
5
9
|
founder: Founder;
|
|
6
10
|
errors: SmdError[];
|
package/dist/db/founders.js
CHANGED
|
@@ -223,14 +223,27 @@ const extractFounderName = (line) => {
|
|
|
223
223
|
return line.trim();
|
|
224
224
|
}
|
|
225
225
|
};
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const name = extractFounderName(nameWithHashtag);
|
|
229
|
-
const result = yield getKnexDb()
|
|
226
|
+
const getBaseFounderQuery = () => {
|
|
227
|
+
return getKnexDb()
|
|
230
228
|
.select("f.id", "f.name", "f.ico", "f.founder_type_code", "f.city_code", "f.city_district_code")
|
|
231
229
|
.from("founder as f")
|
|
232
230
|
.leftJoin("city as c", "c.code", "f.city_code")
|
|
233
|
-
.leftJoin("city_district as d", "d.code", "f.city_district_code")
|
|
231
|
+
.leftJoin("city_district as d", "d.code", "f.city_district_code");
|
|
232
|
+
};
|
|
233
|
+
export const getFounderById = (id) => __awaiter(void 0, void 0, void 0, function* () {
|
|
234
|
+
const result = yield getBaseFounderQuery().where("f.id", id).first();
|
|
235
|
+
const founder = yield resultToFounder(result);
|
|
236
|
+
return {
|
|
237
|
+
founder: founder !== null && founder !== void 0 ? founder : null,
|
|
238
|
+
errors: founder
|
|
239
|
+
? []
|
|
240
|
+
: [wholeLineError(`Zřizovatel s id ${id} neexistuje.`, "")],
|
|
241
|
+
};
|
|
242
|
+
});
|
|
243
|
+
export const findFounder = (nameWithHashtag) => __awaiter(void 0, void 0, void 0, function* () {
|
|
244
|
+
const errors = [];
|
|
245
|
+
const name = extractFounderName(nameWithHashtag);
|
|
246
|
+
const result = yield getBaseFounderQuery()
|
|
234
247
|
.where("f.name", name)
|
|
235
248
|
.orWhere("c.name", name)
|
|
236
249
|
.orWhere("d.name", name)
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Founder } from "../db/types";
|
|
2
2
|
import { DbMunicipalityResult, MunicipalityPartResult } from "./types";
|
|
3
|
-
export declare const isMunicipalityPart: (line: string) => boolean;
|
|
4
|
-
export declare const getMunicipalityPart: (line: string, founder: Founder) => Promise<MunicipalityPartResult>;
|
|
5
3
|
export declare const isMunicipalitySwitch: (line: string) => boolean;
|
|
6
4
|
export declare const getSwitchMunicipality: (line: string, founder: Founder) => Promise<DbMunicipalityResult>;
|
|
7
5
|
export declare const isWholeMunicipality: (line: string) => boolean;
|
|
8
|
-
export declare const
|
|
6
|
+
export declare const isRestWithNoStreetNameLine: (line: string) => boolean;
|
|
7
|
+
export declare const isRestOfMunicipalityLine: (line: string) => boolean;
|
|
8
|
+
export declare const isRestOfMunicipalityPartLine: (line: string) => boolean;
|
|
9
|
+
export declare const getRestOfMunicipalityPart: (line: string, founder: Founder) => Promise<MunicipalityPartResult>;
|
|
9
10
|
export declare const getWholeMunicipality: (line: string, founder: Founder) => Promise<DbMunicipalityResult>;
|
|
11
|
+
export declare const getMunicipalityPartResult: (name: string, line: string, founder: Founder) => Promise<MunicipalityPartResult>;
|
|
@@ -9,32 +9,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { findMunicipalityByNameAndType, findMunicipalityPartByName, } from "../db/founders";
|
|
11
11
|
import { MunicipalityType } from "../db/types";
|
|
12
|
-
const municipalityPartPattern = /^část (?<type>obce|města) (?<name>.+)$/;
|
|
13
12
|
const municipalitySwitchStartPattern = /^navíc ulice /;
|
|
14
13
|
const municipalitySwitchPattern = /^navíc ulice (z )?(?<type>městské části|městského obvodu|obce|města) (?<name>.+):$/;
|
|
15
14
|
const wholeMunicipalityStartPattern = /^území /;
|
|
16
15
|
const wholeMunicipalityPattern = /^území (?<type>městské části|městského obvodu|obce|města) (?<name>.+)$/;
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
};
|
|
21
|
-
export const getMunicipalityPart = (line, founder) => __awaiter(void 0, void 0, void 0, function* () {
|
|
22
|
-
const match = municipalityPartPattern.exec(line);
|
|
23
|
-
if (match === null) {
|
|
24
|
-
return {
|
|
25
|
-
errors: [
|
|
26
|
-
{
|
|
27
|
-
message: "Neplatný zápis pro přidání části obce. Správný zápis je: části <obce/města> <název části obce>. Např.: 'části obce Kladno' (název části obce musí být v 1. pádě).",
|
|
28
|
-
startOffset: 0,
|
|
29
|
-
endOffset: line.length + 1,
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
municipalityPartCode: null,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
const { name } = match.groups;
|
|
36
|
-
return yield getMunicipalityPartResult(cleanName(name), line, founder);
|
|
37
|
-
});
|
|
16
|
+
const restWithNoStreetNamePattern = /^všechny ostatní budovy bez názvu ulice\s*$/;
|
|
17
|
+
const restOfMunicipalityPattern = /^zbytek (?<type>městské části|městského obvodu|obce|města)\s*$/;
|
|
18
|
+
const restOfMunicipalityPartPattern = /^zbytek části (obce|města) (?<name>.+)\s*$/;
|
|
38
19
|
export const isMunicipalitySwitch = (line) => {
|
|
39
20
|
return municipalitySwitchStartPattern.test(line);
|
|
40
21
|
};
|
|
@@ -58,9 +39,32 @@ export const getSwitchMunicipality = (line, founder) => __awaiter(void 0, void 0
|
|
|
58
39
|
export const isWholeMunicipality = (line) => {
|
|
59
40
|
return wholeMunicipalityStartPattern.test(line);
|
|
60
41
|
};
|
|
61
|
-
export const
|
|
62
|
-
return
|
|
42
|
+
export const isRestWithNoStreetNameLine = (line) => {
|
|
43
|
+
return restWithNoStreetNamePattern.test(line);
|
|
44
|
+
};
|
|
45
|
+
export const isRestOfMunicipalityLine = (line) => {
|
|
46
|
+
return restOfMunicipalityPattern.test(line);
|
|
63
47
|
};
|
|
48
|
+
export const isRestOfMunicipalityPartLine = (line) => {
|
|
49
|
+
return restOfMunicipalityPartPattern.test(line);
|
|
50
|
+
};
|
|
51
|
+
export const getRestOfMunicipalityPart = (line, founder) => __awaiter(void 0, void 0, void 0, function* () {
|
|
52
|
+
const match = restOfMunicipalityPartPattern.exec(line);
|
|
53
|
+
if (match === null) {
|
|
54
|
+
return {
|
|
55
|
+
errors: [
|
|
56
|
+
{
|
|
57
|
+
message: "Neplatný zápis pro přidání zbytku části obce. Správný zápis je: zbytek části <typ> <název obce>. Např.: 'zbytek části obce Malšovice' (název části obce musí být v 1. pádě).",
|
|
58
|
+
startOffset: 0,
|
|
59
|
+
endOffset: line.length + 1,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
municipalityPartCode: null,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const { name } = match.groups;
|
|
66
|
+
return yield getMunicipalityPartResult(name, line, founder);
|
|
67
|
+
});
|
|
64
68
|
export const getWholeMunicipality = (line, founder) => __awaiter(void 0, void 0, void 0, function* () {
|
|
65
69
|
const match = wholeMunicipalityPattern.exec(line);
|
|
66
70
|
if (match === null) {
|
|
@@ -89,7 +93,7 @@ const getMunicipalityResult = (type, name, line, founder) => __awaiter(void 0, v
|
|
|
89
93
|
endOffset }))),
|
|
90
94
|
};
|
|
91
95
|
});
|
|
92
|
-
const getMunicipalityPartResult = (name, line, founder) => __awaiter(void 0, void 0, void 0, function* () {
|
|
96
|
+
export const getMunicipalityPartResult = (name, line, founder) => __awaiter(void 0, void 0, void 0, function* () {
|
|
93
97
|
const { municipalityPartCode, errors } = yield findMunicipalityPartByName(name, founder);
|
|
94
98
|
const startOffset = line.indexOf(name);
|
|
95
99
|
const endOffset = startOffset + name.length + 1;
|
|
@@ -4,6 +4,7 @@ export declare class SmdParser extends EmbeddedActionsParser {
|
|
|
4
4
|
constructor(tokenVocabulary: TokenVocabulary);
|
|
5
5
|
street: import("chevrotain").ParserMethod<[], SmdLine[]>;
|
|
6
6
|
private streetNameAndNumbersSpecs;
|
|
7
|
+
private municipalityPartNameAndNumbersSpecs;
|
|
7
8
|
private numberSpecs;
|
|
8
9
|
private seriesSpecs;
|
|
9
10
|
private _gatePostfixTypeSeriesSpec;
|
|
@@ -19,3 +20,4 @@ export declare class SmdParser extends EmbeddedActionsParser {
|
|
|
19
20
|
private fullStreetNumber;
|
|
20
21
|
}
|
|
21
22
|
export declare const parseRichNumber: (number: string) => RichNumber;
|
|
23
|
+
export declare const parseMunicipalityPartName: (name: string) => string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EmbeddedActionsParser, EOF } from "chevrotain";
|
|
2
2
|
import { splitStreetViaRomanNumerals } from "./roman-numerals";
|
|
3
|
-
import {
|
|
3
|
+
import { AllType, AndAbove, AndBelow, DescriptiveType, EvenType, From, Hyphen, MainSeparator, MunicipalityPartName, Number, OddType, Separator, Slash, StreetName, To, Without, } from "./token-definition";
|
|
4
4
|
import { SeriesType } from "./types";
|
|
5
5
|
export class SmdParser extends EmbeddedActionsParser {
|
|
6
6
|
constructor(tokenVocabulary) {
|
|
@@ -15,14 +15,39 @@ export class SmdParser extends EmbeddedActionsParser {
|
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
ALT: () => {
|
|
18
|
-
result = {
|
|
18
|
+
result = {
|
|
19
|
+
type: "street",
|
|
20
|
+
street: this.CONSUME(StreetName).image,
|
|
21
|
+
numberSpec: [],
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
ALT: () => {
|
|
27
|
+
result = this.SUBRULE(this.municipalityPartNameAndNumbersSpecs);
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
ALT: () => {
|
|
32
|
+
const municipalityPart = parseMunicipalityPartName(this.CONSUME(MunicipalityPartName).image);
|
|
33
|
+
result = {
|
|
34
|
+
type: "municipalityPart",
|
|
35
|
+
municipalityPart,
|
|
36
|
+
numberSpec: [],
|
|
37
|
+
};
|
|
19
38
|
},
|
|
20
39
|
},
|
|
21
40
|
]);
|
|
22
|
-
|
|
23
|
-
street
|
|
24
|
-
|
|
25
|
-
|
|
41
|
+
if (result.type === "street") {
|
|
42
|
+
return splitStreetViaRomanNumerals(result.street).map((street) => ({
|
|
43
|
+
type: "street",
|
|
44
|
+
street,
|
|
45
|
+
numberSpec: result.numberSpec,
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
return [result];
|
|
50
|
+
}
|
|
26
51
|
});
|
|
27
52
|
this.streetNameAndNumbersSpecs = this.RULE("streetNameAndNumbersSpecs", () => {
|
|
28
53
|
const street = this.CONSUME(StreetName).image;
|
|
@@ -30,10 +55,23 @@ export class SmdParser extends EmbeddedActionsParser {
|
|
|
30
55
|
this.CONSUME(MainSeparator);
|
|
31
56
|
});
|
|
32
57
|
const numberSpec = this.SUBRULE(this.numberSpecs);
|
|
33
|
-
return { street, numberSpec };
|
|
58
|
+
return { type: "street", street, numberSpec };
|
|
59
|
+
});
|
|
60
|
+
this.municipalityPartNameAndNumbersSpecs = this.RULE("municipalityPartNameAndNumbersSpecs", () => {
|
|
61
|
+
const municipalityPart = parseMunicipalityPartName(this.CONSUME(MunicipalityPartName).image);
|
|
62
|
+
this.OPTION(() => {
|
|
63
|
+
this.CONSUME(MainSeparator);
|
|
64
|
+
});
|
|
65
|
+
const numberSpec = this.SUBRULE(this.numberSpecs);
|
|
66
|
+
return {
|
|
67
|
+
type: "municipalityPart",
|
|
68
|
+
municipalityPart,
|
|
69
|
+
numberSpec,
|
|
70
|
+
};
|
|
34
71
|
});
|
|
35
72
|
this.numberSpecs = this.RULE("numberSpecs", () => {
|
|
36
|
-
const
|
|
73
|
+
const positiveResult = [];
|
|
74
|
+
let negativeResult;
|
|
37
75
|
this.OR([
|
|
38
76
|
{
|
|
39
77
|
GATE: () => this.LA(2).tokenType === Without,
|
|
@@ -55,14 +93,14 @@ export class SmdParser extends EmbeddedActionsParser {
|
|
|
55
93
|
GATE: this._gatePostfixTypeSeriesSpec,
|
|
56
94
|
ALT: () => {
|
|
57
95
|
const { type, ranges } = this.SUBRULE(this.postfixTypeSeriesSpec);
|
|
58
|
-
|
|
96
|
+
negativeResult = { negative: true, type, ranges };
|
|
59
97
|
},
|
|
60
98
|
},
|
|
61
99
|
{
|
|
62
100
|
ALT: () => {
|
|
63
101
|
const type = this.SUBRULE2(this.seriesType);
|
|
64
102
|
const ranges = this.SUBRULE2(this.rangeList);
|
|
65
|
-
|
|
103
|
+
negativeResult = { negative: true, type, ranges };
|
|
66
104
|
},
|
|
67
105
|
},
|
|
68
106
|
]);
|
|
@@ -73,13 +111,16 @@ export class SmdParser extends EmbeddedActionsParser {
|
|
|
73
111
|
this.AT_LEAST_ONE_SEP({
|
|
74
112
|
SEP: Separator,
|
|
75
113
|
DEF: () => {
|
|
76
|
-
|
|
114
|
+
positiveResult.push(this.SUBRULE(this.seriesSpecs));
|
|
77
115
|
},
|
|
78
116
|
});
|
|
79
117
|
},
|
|
80
118
|
},
|
|
81
119
|
]);
|
|
82
|
-
|
|
120
|
+
if (negativeResult) {
|
|
121
|
+
return negativeResult;
|
|
122
|
+
}
|
|
123
|
+
return positiveResult;
|
|
83
124
|
});
|
|
84
125
|
this.seriesSpecs = this.RULE("seriesSpecs", () => {
|
|
85
126
|
let ranges = [];
|
|
@@ -321,7 +362,10 @@ export class SmdParser extends EmbeddedActionsParser {
|
|
|
321
362
|
const descriptiveNumber = parseRichNumber(this.CONSUME(Number).image);
|
|
322
363
|
this.CONSUME(Slash);
|
|
323
364
|
const orientationNumber = parseRichNumber(this.CONSUME2(Number).image);
|
|
324
|
-
return {
|
|
365
|
+
return {
|
|
366
|
+
descriptionNumber: descriptiveNumber,
|
|
367
|
+
orientationalNumber: orientationNumber,
|
|
368
|
+
};
|
|
325
369
|
});
|
|
326
370
|
this.performSelfAnalysis();
|
|
327
371
|
}
|
|
@@ -341,3 +385,9 @@ export const parseRichNumber = (number) => {
|
|
|
341
385
|
};
|
|
342
386
|
}
|
|
343
387
|
};
|
|
388
|
+
const municipalityPartPattern = /^část (?<type>obce|města) (?<name>.+)$/;
|
|
389
|
+
export const parseMunicipalityPartName = (name) => {
|
|
390
|
+
var _a, _b;
|
|
391
|
+
const match = municipalityPartPattern.exec(name);
|
|
392
|
+
return (_b = (_a = match === null || match === void 0 ? void 0 : match.groups.name) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : "";
|
|
393
|
+
};
|
|
@@ -2,7 +2,11 @@ import { Founder } from "../db/types";
|
|
|
2
2
|
import { ErrorCallbackParams, Municipality, MunicipalityWithFounder, ProcessLineCallbackParams, School, SmdError, SmdState } from "./types";
|
|
3
3
|
export declare const parseOrdinanceToAddressPoints: (lines: string[], initialState?: Partial<SmdState>, onError?: (params: ErrorCallbackParams) => void, onWarning?: (params: ErrorCallbackParams) => void, onProcessedLine?: (params: ProcessLineCallbackParams) => void) => Promise<Municipality[]>;
|
|
4
4
|
export declare const convertMunicipality: (municipality: MunicipalityWithFounder) => Municipality;
|
|
5
|
-
export declare const
|
|
5
|
+
export declare const getNewMunicipalityByName: (name: string) => Promise<{
|
|
6
|
+
municipality: MunicipalityWithFounder;
|
|
7
|
+
errors: SmdError[];
|
|
8
|
+
}>;
|
|
9
|
+
export declare const getNewMunicipalityByFounderId: (founderId: number) => Promise<{
|
|
6
10
|
municipality: MunicipalityWithFounder;
|
|
7
11
|
errors: SmdError[];
|
|
8
12
|
}>;
|
|
@@ -7,17 +7,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import {
|
|
10
|
+
import { checkStreetExists, findAddressPoints, getAddressPointById, } from "../db/address-points";
|
|
11
11
|
import { disconnectKnex } from "../db/db";
|
|
12
|
-
import { findFounder } from "../db/founders";
|
|
12
|
+
import { findFounder, getFounderById } from "../db/founders";
|
|
13
13
|
import { findSchool } from "../db/schools";
|
|
14
14
|
import { founderToMunicipality } from "../db/types";
|
|
15
|
-
import {
|
|
15
|
+
import { getMunicipalityPartResult, getRestOfMunicipalityPart, getSwitchMunicipality, getWholeMunicipality, isMunicipalitySwitch, isRestOfMunicipalityLine, isRestOfMunicipalityPartLine, isRestWithNoStreetNameLine, isWholeMunicipality, } from "./municipality";
|
|
16
16
|
import { parseLine } from "./smd-line-parser";
|
|
17
17
|
import { isAddressPoint, } from "./types";
|
|
18
18
|
export const parseOrdinanceToAddressPoints = (lines, initialState = {}, onError = () => { }, onWarning = () => { }, onProcessedLine = () => { }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
19
|
try {
|
|
20
|
-
const state = Object.assign({ currentMunicipality: null, currentFilterMunicipality: null, currentSchool: null,
|
|
20
|
+
const state = Object.assign({ currentMunicipality: null, currentFilterMunicipality: null, currentSchool: null, rests: {
|
|
21
|
+
noStreetNameSchoolIzo: null,
|
|
22
|
+
municipalityParts: [],
|
|
23
|
+
wholeMunicipalitySchoolIzo: null,
|
|
24
|
+
}, municipalities: [] }, initialState);
|
|
21
25
|
let lineNumber = 1;
|
|
22
26
|
for (const rawLine of lines) {
|
|
23
27
|
const line = cleanLine(rawLine);
|
|
@@ -37,12 +41,7 @@ export const parseOrdinanceToAddressPoints = (lines, initialState = {}, onError
|
|
|
37
41
|
}
|
|
38
42
|
state.currentMunicipality.schools.push(mapSchoolForExport(state.currentSchool));
|
|
39
43
|
}
|
|
40
|
-
|
|
41
|
-
if (state.noStreetNameSchoolIzo) {
|
|
42
|
-
yield addNoStreetNameToSchool(state);
|
|
43
|
-
}
|
|
44
|
-
state.municipalities.push(convertMunicipality(state.currentMunicipality));
|
|
45
|
-
}
|
|
44
|
+
yield completeCurrentMunicipality(state);
|
|
46
45
|
return state.municipalities;
|
|
47
46
|
}
|
|
48
47
|
catch (error) {
|
|
@@ -82,17 +81,29 @@ const processOneLine = (params) => __awaiter(void 0, void 0, void 0, function* (
|
|
|
82
81
|
yield processSchoolLine(params);
|
|
83
82
|
return;
|
|
84
83
|
}
|
|
85
|
-
if (
|
|
86
|
-
yield processMunicipalityPartLine(params);
|
|
87
|
-
}
|
|
88
|
-
else if (isMunicipalitySwitch(line)) {
|
|
84
|
+
if (isMunicipalitySwitch(line)) {
|
|
89
85
|
processMunicipalitySwitchLine(params);
|
|
90
86
|
}
|
|
91
87
|
else if (isWholeMunicipality(line)) {
|
|
92
88
|
yield processWholeMunicipalityLine(params);
|
|
93
89
|
}
|
|
94
|
-
else if (
|
|
95
|
-
state.noStreetNameSchoolIzo = state.currentSchool.izo;
|
|
90
|
+
else if (isRestWithNoStreetNameLine(line)) {
|
|
91
|
+
state.rests.noStreetNameSchoolIzo = state.currentSchool.izo;
|
|
92
|
+
}
|
|
93
|
+
else if (isRestOfMunicipalityLine(line)) {
|
|
94
|
+
state.rests.wholeMunicipalitySchoolIzo = state.currentSchool.izo;
|
|
95
|
+
}
|
|
96
|
+
else if (isRestOfMunicipalityPartLine(line)) {
|
|
97
|
+
const { municipalityPartCode, errors } = yield getRestOfMunicipalityPart(line, state.currentMunicipality.founder);
|
|
98
|
+
if (errors.length > 0) {
|
|
99
|
+
params.onError({ lineNumber: params.lineNumber, line, errors });
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
state.rests.municipalityParts.push({
|
|
103
|
+
municipalityPartCode,
|
|
104
|
+
schoolIzo: state.currentSchool.izo,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
96
107
|
}
|
|
97
108
|
else {
|
|
98
109
|
yield processAddressPointLine(params);
|
|
@@ -103,13 +114,8 @@ const processMunicipalityLine = ({ line, lineNumber, state, onError, }) => __awa
|
|
|
103
114
|
state.currentMunicipality.schools.push(state.currentSchool);
|
|
104
115
|
state.currentSchool = null;
|
|
105
116
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
yield addNoStreetNameToSchool(state);
|
|
109
|
-
}
|
|
110
|
-
state.municipalities.push(convertMunicipality(state.currentMunicipality));
|
|
111
|
-
}
|
|
112
|
-
const { municipality, errors } = yield getNewMunicipality(line);
|
|
117
|
+
yield completeCurrentMunicipality(state);
|
|
118
|
+
const { municipality, errors } = yield getNewMunicipalityByName(line);
|
|
113
119
|
if (errors.length > 0) {
|
|
114
120
|
onError({ lineNumber, line, errors });
|
|
115
121
|
}
|
|
@@ -117,6 +123,12 @@ const processMunicipalityLine = ({ line, lineNumber, state, onError, }) => __awa
|
|
|
117
123
|
state.currentFilterMunicipality = founderToMunicipality(state.currentMunicipality.founder);
|
|
118
124
|
state.currentSchool = null;
|
|
119
125
|
});
|
|
126
|
+
const completeCurrentMunicipality = (state) => __awaiter(void 0, void 0, void 0, function* () {
|
|
127
|
+
if (state.currentMunicipality !== null) {
|
|
128
|
+
yield addRests(state);
|
|
129
|
+
state.municipalities.push(convertMunicipality(state.currentMunicipality));
|
|
130
|
+
}
|
|
131
|
+
});
|
|
120
132
|
const processEmptyLine = ({ state }) => {
|
|
121
133
|
if (state.currentSchool !== null) {
|
|
122
134
|
state.currentMunicipality.schools.push(state.currentSchool);
|
|
@@ -137,19 +149,6 @@ const processSchoolLine = ({ line, lineNumber, state, onError, }) => __awaiter(v
|
|
|
137
149
|
state.currentSchool = yield getNewSchool(line, state.currentMunicipality.founder, lineNumber, onError);
|
|
138
150
|
state.currentFilterMunicipality = founderToMunicipality(state.currentMunicipality.founder);
|
|
139
151
|
});
|
|
140
|
-
const processMunicipalityPartLine = ({ line, state, lineNumber, onError, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
141
|
-
const { municipalityPartCode, errors } = yield getMunicipalityPart(line, state.currentMunicipality.founder);
|
|
142
|
-
if (errors.length > 0) {
|
|
143
|
-
onError({ lineNumber, line, errors });
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
const addressPoints = yield findAddressPoints({
|
|
147
|
-
type: FindAddressPointsType.MunicipalityPart,
|
|
148
|
-
municipalityPartCode,
|
|
149
|
-
});
|
|
150
|
-
state.currentSchool.addresses.push(...filterOutSchoolAddressPoint(addressPoints, state.currentSchool).map(mapAddressPointForExport));
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
152
|
const processMunicipalitySwitchLine = ({ line, state, lineNumber, onError, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
154
153
|
const { municipality, errors } = yield getSwitchMunicipality(line, state.currentMunicipality.founder);
|
|
155
154
|
if (errors.length > 0) {
|
|
@@ -166,7 +165,7 @@ const processWholeMunicipalityLine = ({ line, state, lineNumber, onError, }) =>
|
|
|
166
165
|
}
|
|
167
166
|
else {
|
|
168
167
|
const addressPoints = yield findAddressPoints({
|
|
169
|
-
type:
|
|
168
|
+
type: "wholeMunicipality",
|
|
170
169
|
municipality,
|
|
171
170
|
});
|
|
172
171
|
state.currentSchool.addresses.push(...filterOutSchoolAddressPoint(addressPoints, state.currentSchool).map(mapAddressPointForExport));
|
|
@@ -179,48 +178,105 @@ const processAddressPointLine = ({ line, state, lineNumber, onError, onWarning,
|
|
|
179
178
|
}
|
|
180
179
|
else {
|
|
181
180
|
for (const smdLine of smdLines) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
181
|
+
if (smdLine.type === "street") {
|
|
182
|
+
const { exists, errors } = yield checkStreetExists(smdLine.street, state.currentMunicipality.founder);
|
|
183
|
+
if (errors.length > 0) {
|
|
184
|
+
onWarning({ lineNumber, line, errors });
|
|
185
|
+
}
|
|
186
|
+
if (exists) {
|
|
187
|
+
const addressPoints = yield findAddressPoints({
|
|
188
|
+
type: "smdLine",
|
|
189
|
+
smdLine,
|
|
190
|
+
municipality: state.currentFilterMunicipality,
|
|
191
|
+
});
|
|
192
|
+
state.currentSchool.addresses.push(...filterOutSchoolAddressPoint(addressPoints, state.currentSchool).map(mapAddressPointForExport));
|
|
193
|
+
}
|
|
185
194
|
}
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
195
|
+
else if (smdLine.type === "municipalityPart") {
|
|
196
|
+
const { municipalityPartCode, errors } = yield getMunicipalityPartResult(smdLine.municipalityPart, line, state.currentMunicipality.founder);
|
|
197
|
+
if (errors.length > 0) {
|
|
198
|
+
onWarning({ lineNumber, line, errors });
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
const addressPoints = yield findAddressPoints({
|
|
202
|
+
type: "smdLine",
|
|
203
|
+
smdLine,
|
|
204
|
+
municipality: state.currentFilterMunicipality,
|
|
205
|
+
municipalityPartCode,
|
|
206
|
+
});
|
|
207
|
+
state.currentSchool.addresses.push(...filterOutSchoolAddressPoint(addressPoints, state.currentSchool).map(mapAddressPointForExport));
|
|
208
|
+
}
|
|
193
209
|
}
|
|
194
210
|
}
|
|
195
211
|
}
|
|
196
212
|
});
|
|
197
|
-
const
|
|
213
|
+
const addRestToSchool = (restPoints, schoolIzo, state) => __awaiter(void 0, void 0, void 0, function* () {
|
|
198
214
|
const addressPoints = state.currentMunicipality.schools.flatMap((school) => school.addresses);
|
|
215
|
+
// filter out address points already present
|
|
216
|
+
const remainingPoints = restPoints.filter((point) => !addressPoints.some((ap) => ap.address === point.address));
|
|
217
|
+
// find the right school and add the remaining address points
|
|
218
|
+
const schoolIndex = state.currentMunicipality.schools.findIndex((school) => school.izo === schoolIzo);
|
|
219
|
+
state.currentMunicipality.schools[schoolIndex].addresses.push(...remainingPoints);
|
|
220
|
+
});
|
|
221
|
+
const addRests = (state) => __awaiter(void 0, void 0, void 0, function* () {
|
|
222
|
+
if (state.rests.noStreetNameSchoolIzo) {
|
|
223
|
+
yield addRestWithNoStreetNameToSchool(state);
|
|
224
|
+
}
|
|
225
|
+
if (state.rests.wholeMunicipalitySchoolIzo) {
|
|
226
|
+
yield addRestOfMunicipality(state);
|
|
227
|
+
}
|
|
228
|
+
for (const rest of state.rests.municipalityParts) {
|
|
229
|
+
yield addRestOfMunicipalityPart(state, rest.municipalityPartCode, rest.schoolIzo);
|
|
230
|
+
}
|
|
231
|
+
state.rests.noStreetNameSchoolIzo = null;
|
|
232
|
+
state.rests.wholeMunicipalitySchoolIzo = null;
|
|
233
|
+
state.rests.municipalityParts = [];
|
|
234
|
+
});
|
|
235
|
+
const addRestWithNoStreetNameToSchool = (state) => __awaiter(void 0, void 0, void 0, function* () {
|
|
199
236
|
// get all address points without street name for current municipality
|
|
200
237
|
const pointsNoStreetName = yield findAddressPoints({
|
|
201
|
-
type:
|
|
238
|
+
type: "wholeMunicipalityNoStreetName",
|
|
202
239
|
municipality: {
|
|
203
240
|
code: state.currentMunicipality.founder.cityOrDistrictCode,
|
|
204
241
|
type: state.currentMunicipality.founder.municipalityType,
|
|
205
242
|
},
|
|
206
243
|
});
|
|
207
|
-
|
|
208
|
-
const remainingPoints = pointsNoStreetName.filter((point) => !addressPoints.some((ap) => ap.address === point.address));
|
|
209
|
-
// find the right school and add the remaining address points
|
|
210
|
-
const schoolIndex = state.currentMunicipality.schools.findIndex((school) => school.izo === state.noStreetNameSchoolIzo);
|
|
211
|
-
state.currentMunicipality.schools[schoolIndex].addresses.push(...remainingPoints);
|
|
212
|
-
state.noStreetNameSchoolIzo = null;
|
|
244
|
+
yield addRestToSchool(pointsNoStreetName, state.rests.noStreetNameSchoolIzo, state);
|
|
213
245
|
});
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
246
|
+
const addRestOfMunicipality = (state) => __awaiter(void 0, void 0, void 0, function* () {
|
|
247
|
+
// get all address points for current municipality
|
|
248
|
+
const allPoints = yield findAddressPoints({
|
|
249
|
+
type: "wholeMunicipality",
|
|
217
250
|
municipality: {
|
|
218
|
-
|
|
219
|
-
founder,
|
|
220
|
-
schools: [],
|
|
251
|
+
code: state.currentMunicipality.founder.cityOrDistrictCode,
|
|
252
|
+
type: state.currentMunicipality.founder.municipalityType,
|
|
221
253
|
},
|
|
222
|
-
|
|
223
|
-
|
|
254
|
+
});
|
|
255
|
+
yield addRestToSchool(allPoints, state.rests.wholeMunicipalitySchoolIzo, state);
|
|
256
|
+
});
|
|
257
|
+
const addRestOfMunicipalityPart = (state, municipalityPartCode, schoolIzo) => __awaiter(void 0, void 0, void 0, function* () {
|
|
258
|
+
// get all address points for current municipality
|
|
259
|
+
const allPoints = yield findAddressPoints({
|
|
260
|
+
type: "wholeMunicipalityPart",
|
|
261
|
+
municipalityPartCode,
|
|
262
|
+
});
|
|
263
|
+
yield addRestToSchool(allPoints, schoolIzo, state);
|
|
264
|
+
});
|
|
265
|
+
export const getNewMunicipalityByName = (name) => __awaiter(void 0, void 0, void 0, function* () {
|
|
266
|
+
const { founder, errors } = yield findFounder(name);
|
|
267
|
+
return getNewMunicipality(founder, errors);
|
|
268
|
+
});
|
|
269
|
+
export const getNewMunicipalityByFounderId = (founderId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
270
|
+
const { founder, errors } = yield getFounderById(founderId);
|
|
271
|
+
return getNewMunicipality(founder, errors);
|
|
272
|
+
});
|
|
273
|
+
const getNewMunicipality = (founder, errors) => ({
|
|
274
|
+
municipality: {
|
|
275
|
+
municipalityName: founder ? founder.name : "Neznámá obec",
|
|
276
|
+
founder,
|
|
277
|
+
schools: [],
|
|
278
|
+
},
|
|
279
|
+
errors,
|
|
224
280
|
});
|
|
225
281
|
export const getNewSchool = (name, founder, lineNumber, onError) => __awaiter(void 0, void 0, void 0, function* () {
|
|
226
282
|
let exportSchool = {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TokenVocabulary } from "chevrotain";
|
|
2
2
|
export declare const resetTokenState: () => void;
|
|
3
3
|
export declare const prepareLine: (text: string) => string;
|
|
4
|
+
export declare const nameMatcherFactory: (pattern: RegExp) => (text: string, startOffset: number) => RegExpExecArray | [string] | null;
|
|
4
5
|
export declare const streetNameMatcher: (text: string, startOffset: number) => RegExpExecArray | [string] | null;
|
|
5
6
|
declare const MainSeparator: import("chevrotain").TokenType;
|
|
6
7
|
declare const From: import("chevrotain").TokenType;
|
|
@@ -13,6 +14,7 @@ declare const AllType: import("chevrotain").TokenType;
|
|
|
13
14
|
declare const OddType: import("chevrotain").TokenType;
|
|
14
15
|
declare const EvenType: import("chevrotain").TokenType;
|
|
15
16
|
declare const Without: import("chevrotain").TokenType;
|
|
17
|
+
declare const MunicipalityPartName: import("chevrotain").TokenType;
|
|
16
18
|
declare const StreetName: import("chevrotain").TokenType;
|
|
17
19
|
declare const Number: import("chevrotain").TokenType;
|
|
18
20
|
declare const Hyphen: import("chevrotain").TokenType;
|
|
@@ -20,4 +22,4 @@ declare const Slash: import("chevrotain").TokenType;
|
|
|
20
22
|
declare const Space: import("chevrotain").TokenType;
|
|
21
23
|
declare const smdTokens: import("chevrotain").TokenType[];
|
|
22
24
|
declare const tokenVocabulary: TokenVocabulary;
|
|
23
|
-
export { MainSeparator, Separator, OddType, EvenType, DescriptiveType, AllType, Number, From, To, AndAbove, AndBelow, Without, Hyphen, Slash, Space, StreetName, smdTokens, tokenVocabulary, };
|
|
25
|
+
export { MainSeparator, Separator, OddType, EvenType, DescriptiveType, AllType, Number, From, To, AndAbove, AndBelow, Without, Hyphen, Slash, Space, StreetName, MunicipalityPartName, smdTokens, tokenVocabulary, };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createToken, Lexer } from "chevrotain";
|
|
2
|
-
let
|
|
2
|
+
let nameWasMatched = false;
|
|
3
3
|
export const resetTokenState = () => {
|
|
4
|
-
|
|
4
|
+
nameWasMatched = false;
|
|
5
5
|
};
|
|
6
6
|
export const prepareLine = (text) => {
|
|
7
7
|
return text
|
|
@@ -9,10 +9,12 @@ export const prepareLine = (text) => {
|
|
|
9
9
|
.replace(/;/g, ",")
|
|
10
10
|
.replace(/,$/, "");
|
|
11
11
|
};
|
|
12
|
+
// this function prevents numbers that make part of the street name to be matched before the
|
|
13
|
+
// street/municipality part name is completely matched
|
|
12
14
|
const tokenBlockerFactory = (pattern) => {
|
|
13
15
|
const stickyPattern = new RegExp(pattern, "y");
|
|
14
16
|
return (text, startOffset) => {
|
|
15
|
-
if (
|
|
17
|
+
if (nameWasMatched) {
|
|
16
18
|
stickyPattern.lastIndex = startOffset;
|
|
17
19
|
return stickyPattern.exec(text);
|
|
18
20
|
}
|
|
@@ -35,26 +37,32 @@ const interruptPatterns = [
|
|
|
35
37
|
"s výjimkou",
|
|
36
38
|
"kromě",
|
|
37
39
|
];
|
|
40
|
+
// this pattern allows a single space or a single hyphen (without an adjacent space) between words
|
|
38
41
|
const streetNamePattern = /[^ -]+([ -]?[^ -]+)*/;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
// same but with a prefix of municipality part
|
|
43
|
+
const municipalityPartNamePattern = /část (obce|města) [^ -]+([ -]?[^ -]+)*/;
|
|
44
|
+
export const nameMatcherFactory = (pattern) => {
|
|
45
|
+
return (text, startOffset) => {
|
|
46
|
+
if (nameWasMatched || startOffset > 0) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const result = pattern.exec(text);
|
|
51
|
+
if (result !== null) {
|
|
52
|
+
nameWasMatched = true;
|
|
53
|
+
const firstIndex = interruptPatterns.reduce((currentMin, pattern) => {
|
|
54
|
+
const index = text.search(new RegExp(` ${pattern}( |$)`));
|
|
55
|
+
return index >= 0 ? Math.min(index, currentMin) : currentMin;
|
|
56
|
+
}, Infinity);
|
|
57
|
+
if (firstIndex < Infinity) {
|
|
58
|
+
return [result[0].substring(0, firstIndex).trim()];
|
|
59
|
+
}
|
|
53
60
|
}
|
|
61
|
+
return result;
|
|
54
62
|
}
|
|
55
|
-
|
|
56
|
-
}
|
|
63
|
+
};
|
|
57
64
|
};
|
|
65
|
+
export const streetNameMatcher = nameMatcherFactory(streetNamePattern);
|
|
58
66
|
const MainSeparator = createToken({ name: "MainSeparator", pattern: / - / });
|
|
59
67
|
const From = createToken({ name: "From", pattern: /od/ });
|
|
60
68
|
const To = createToken({ name: "To", pattern: /do/ });
|
|
@@ -93,9 +101,13 @@ const Without = createToken({
|
|
|
93
101
|
name: "Without",
|
|
94
102
|
pattern: /bez|vyjma|mimo|s výjimkou|kromě/,
|
|
95
103
|
});
|
|
104
|
+
const MunicipalityPartName = createToken({
|
|
105
|
+
name: "MunicipalityPartName",
|
|
106
|
+
pattern: nameMatcherFactory(municipalityPartNamePattern),
|
|
107
|
+
});
|
|
96
108
|
const StreetName = createToken({
|
|
97
109
|
name: "StreetName",
|
|
98
|
-
pattern:
|
|
110
|
+
pattern: nameMatcherFactory(streetNamePattern),
|
|
99
111
|
});
|
|
100
112
|
const Number = createToken({
|
|
101
113
|
name: "Number",
|
|
@@ -131,6 +143,7 @@ const smdTokens = [
|
|
|
131
143
|
Slash,
|
|
132
144
|
Space,
|
|
133
145
|
// Colon,
|
|
146
|
+
MunicipalityPartName,
|
|
134
147
|
StreetName,
|
|
135
148
|
];
|
|
136
149
|
MainSeparator.LABEL = "' - '";
|
|
@@ -151,4 +164,4 @@ const tokenVocabulary = {};
|
|
|
151
164
|
smdTokens.forEach((tokenType) => {
|
|
152
165
|
tokenVocabulary[tokenType.name] = tokenType;
|
|
153
166
|
});
|
|
154
|
-
export { MainSeparator, Separator, OddType, EvenType, DescriptiveType, AllType, Number, From, To, AndAbove, AndBelow, Without, Hyphen, Slash, Space, StreetName, smdTokens, tokenVocabulary, };
|
|
167
|
+
export { MainSeparator, Separator, OddType, EvenType, DescriptiveType, AllType, Number, From, To, AndAbove, AndBelow, Without, Hyphen, Slash, Space, StreetName, MunicipalityPartName, smdTokens, tokenVocabulary, };
|
|
@@ -4,10 +4,16 @@ export interface ProcessedSmdLines {
|
|
|
4
4
|
smdLines: SmdLine[];
|
|
5
5
|
errors: SmdError[];
|
|
6
6
|
}
|
|
7
|
-
export
|
|
7
|
+
export type SmdLineType = "street" | "wholeMunicipalityLine" | "municipalitySwitch" | "municipalityPart";
|
|
8
|
+
export type SmdLine = {
|
|
9
|
+
type: "street";
|
|
8
10
|
street: string;
|
|
9
11
|
numberSpec: SeriesSpec[] | NegativeSeriesSpec;
|
|
10
|
-
}
|
|
12
|
+
} | {
|
|
13
|
+
type: "municipalityPart";
|
|
14
|
+
municipalityPart: string;
|
|
15
|
+
numberSpec: SeriesSpec[] | NegativeSeriesSpec;
|
|
16
|
+
};
|
|
11
17
|
export interface SeriesSpec {
|
|
12
18
|
type: SeriesType;
|
|
13
19
|
ranges: (RangeSpec | FullStreetNumber)[];
|
|
@@ -74,7 +80,14 @@ export interface SmdState {
|
|
|
74
80
|
currentMunicipality: MunicipalityWithFounder;
|
|
75
81
|
currentFilterMunicipality: DbMunicipality;
|
|
76
82
|
currentSchool: School;
|
|
77
|
-
|
|
83
|
+
rests: {
|
|
84
|
+
noStreetNameSchoolIzo: string;
|
|
85
|
+
municipalityParts: {
|
|
86
|
+
municipalityPartCode: number;
|
|
87
|
+
schoolIzo: string;
|
|
88
|
+
}[];
|
|
89
|
+
wholeMunicipalitySchoolIzo: string;
|
|
90
|
+
};
|
|
78
91
|
municipalities: Municipality[];
|
|
79
92
|
}
|
|
80
93
|
export interface ProcessLineCallbackParams {
|
package/package.json
CHANGED
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
},
|
|
58
58
|
"name": "text-to-map",
|
|
59
59
|
"description": "Text To Map usiluje o lepší, strojově zpracovatelné využití částí vyhlášek s výčtem ulic a dalších lokací. Jde o rozšiřitelnou sadu konceptů a nástrojů, které zajistí hladký převod výčtu ulic a jejich rozsahů v lidsky srozumitelném jazyce do strojově zpracovatelného, uchopitelného formátu.",
|
|
60
|
-
"version": "1.1.
|
|
60
|
+
"version": "1.1.18",
|
|
61
61
|
"main": "dist/index.js",
|
|
62
62
|
"types": "dist/index.d.ts",
|
|
63
63
|
"files": [
|