diff --git a/.gitignore b/.gitignore
index 58349bf..b1ae217 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,7 @@ dist/
downloads/
eggs/
.eggs/
+lib/
lib64/
parts/
sdist/
diff --git a/accuracy_test/run_test.js b/accuracy_test/run_test.js
index 27d317a..8f815ef 100644
--- a/accuracy_test/run_test.js
+++ b/accuracy_test/run_test.js
@@ -1,4 +1,4 @@
-const addressParser = require('./../web/src/lib/ogcio-parser');
+const addressParser = require('./../web/src/lib/address-parser');
const Promise = require('bluebird');
const request = Promise.promisifyAll(require('request'));
diff --git a/package-lock.json b/package-lock.json
deleted file mode 100644
index b7a18a2..0000000
--- a/package-lock.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "requires": true,
- "lockfileVersion": 1,
- "dependencies": {
- "mgrs": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz",
- "integrity": "sha1-+5FYjnjJACVnI5XLQLJffNatGCk="
- },
- "proj4": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.5.0.tgz",
- "integrity": "sha512-XZTRT7OPdLzgvtTqL8DG2cEj8lYdovztOwiwpwRSYayOty5Ipf3H68dh/fiL+HKDEyetmQSMhkkMGiJoyziz3w==",
- "requires": {
- "mgrs": "1.0.0",
- "wkt-parser": "1.2.3"
- }
- },
- "wkt-parser": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.2.3.tgz",
- "integrity": "sha512-s7zrOedGuHbbzMaQOuf8HacuCYp3LmmrHjkkN//7UEAzsYz7xJ6J+j/84ZWZkQcrRqi3xXyuc4odPHj7PEB0bw=="
- }
- }
-}
diff --git a/web/src/App.vue b/web/src/App.vue
index 0931962..a93e0c9 100644
--- a/web/src/App.vue
+++ b/web/src/App.vue
@@ -4,7 +4,8 @@
香港地址解析器 Hong Kong Address Parser (Beta - Release 0.3)
- 解析地址
+ 單次處理
+ 大量處理
反映意見
diff --git a/web/src/assets/pin-selected.png b/web/src/assets/pin-selected.png
deleted file mode 100644
index c63ad0b..0000000
Binary files a/web/src/assets/pin-selected.png and /dev/null differ
diff --git a/web/src/components/SearchFilter.vue b/web/src/components/SearchFilter.vue
index 6e8cc6e..621c8f4 100644
--- a/web/src/components/SearchFilter.vue
+++ b/web/src/components/SearchFilter.vue
@@ -1,5 +1,5 @@
-
+
-
+
+};
+
\ No newline at end of file
diff --git a/web/src/lib/ogcio-parser.js b/web/src/lib/address-parser.js
similarity index 98%
rename from web/src/lib/ogcio-parser.js
rename to web/src/lib/address-parser.js
index f503406..3f58a4b 100644
--- a/web/src/lib/ogcio-parser.js
+++ b/web/src/lib/address-parser.js
@@ -107,13 +107,7 @@ function eliminateLangKeys(data) {
const result = {};
for (const key of Object.keys(data)) {
const refinedKey = key.replace(/(^Chi|^Eng)/,'');
- // eliminate with recursion
- if (typeof(data[key]) === 'object') {
- result[refinedKey] = eliminateLangKeys(data[key]);
- } else {
- result[refinedKey] = data[key];
- }
-
+ result[refinedKey] = data[key];
}
return result;
}
diff --git a/web/src/lib/address-resolver.js b/web/src/lib/address-resolver.js
deleted file mode 100644
index 297ef06..0000000
--- a/web/src/lib/address-resolver.js
+++ /dev/null
@@ -1,87 +0,0 @@
-
-import ogcioParser from './ogcio-parser';
-import * as AddressFactory from './models/address-factory';
-import proj4 from 'proj4';
-import ProjConvertor from '../utils/proj-convertor';
-
-const OGCIO_RECORD_COUNT = 200;
-const NEAR_THRESHOLD = 0.03; // 30 metre
-
-export default {
- queryAddress: async (address) => {
- // Fetch result from OGCIO
- const ogcioURL = `https://www.als.ogcio.gov.hk/lookup?q=${address}&n=${OGCIO_RECORD_COUNT}`;
-
- const ogcioRes = await fetch(ogcioURL, {
- headers: {
- "Accept": "application/json",
- "Accept-Language": "en,zh-Hant",
- "Accept-Encoding": "gzip"
- }
- });
- const ogcioData = await ogcioRes.json();
-
- // Fetch result from GeoData Portal of Land Department
- const landsURL = `https://geodata.gov.hk/gs/api/v1.0.0/locationSearch?q=${address}`;
- const landsRes = await fetch(landsURL);
- const landsData = await landsRes.json();
-
- const landRecords = [];
- for (const data of landsData) {
- let wgsLng, wgslat;
- [wgsLng, wgslat] = ProjConvertor.projTransform('EPSG:2326', 'EPSG:4326', [data.x, data.y]);
- data.lat = wgslat
- data.lng = wgsLng
- landRecords.push(AddressFactory.createAddress('land', data));
- }
- const sortedResults = [];
- const sortedOgcioRecords = (await ogcioParser.searchResult(address, ogcioData)).map(record => AddressFactory.createAddress('ogcio', record));
- // P.S. Result source (OGCIO/Land Department) should be displayed to user
- // this.results['source'] = ...
- console.log('OGCIO Best match: ' + sortedOgcioRecords[0].fullAddress('chi'));
-
- // 1. Best Case: Land result and ogcio return the same address
- if (sortedOgcioRecords[0].distanceTo(landRecords[0]) < NEAR_THRESHOLD) {
- console.log('1. Best Case: Land result and ogcio return the same address');
- return sortedOgcioRecords;
- }
-
- // 2. best result from OGCIO is not the land result but somehow within the 200 records and we would find it out
- // Do 200 * n coordinates calculation
- console.log('2. best result from OGCIO is not the land result but somehow within the 200 records and we would find it out');
- console.log('Distance shorter than ' + NEAR_THRESHOLD + ' km');
- sortedOgcioRecords.forEach(ogcioRecord => {
- if (ogcioRecord.distanceTo(landRecords[0]) < NEAR_THRESHOLD) {
- sortedResults.push(ogcioRecord);
- console.log(ogcioRecord.distanceTo(landRecords[0]) + ' | ' + ogcioRecord.fullAddress('chi'))
- }
- });
-
- if (sortedResults.length > 0) {
- return sortedResults;
- }
-
-
- // 3. ogcio not found but there is land result. We use the record then.
- console.log('3. ogcio not found but there is land result.');
- return landRecords;
- // TODO:
- // in ResultSelector, compare the distance between ogcioData Bestmatch and first result of landResult,
- // Assumption: landResult is more accurate, and cover more location of HK
-
- // tried and got correct result from landResult :
- // 政府總部
- // 立法會綜合大樓
- // 寶鄉邨
- // 九龍啟德承啟道28號
-
- // If the distance is larger than certain value (i.e. 0.1km), which means there are discrepancies between 2 APIs
- // ResultSelector will return landResult, coz we assumpe that landResult is more accurate
-
- // Then the SingleMatch will return one landResult only, instead of 200 results
- // For that particular landResult, only coord, addressZH and nameZH would be shown at this moment
- // (Logic of retriving extra address component (Region, Sub District, DCCA etc) would be implmeneted later)
-
-
- }
-}
\ No newline at end of file
diff --git a/web/src/lib/models/address-factory.js b/web/src/lib/models/address-factory.js
deleted file mode 100644
index 66054a9..0000000
--- a/web/src/lib/models/address-factory.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import OGCIOAddress from './ogcio-adress';
-import Address from './address';
-import LandAddress from './land-address';
-
-export const createAddress = function (type, record) {
- switch (type) {
- case 'ogcio': return new OGCIOAddress(record);
- case 'land' : return new LandAddress(record);
- default: return new Address(record);
- }
-
-}
\ No newline at end of file
diff --git a/web/src/lib/models/address.js b/web/src/lib/models/address.js
deleted file mode 100644
index 3d962d0..0000000
--- a/web/src/lib/models/address.js
+++ /dev/null
@@ -1,62 +0,0 @@
-import * as turf from "@turf/turf"
-
-export default class Address {
- constructor() {
- }
-
- /**
- * Return the detailed components of the parsed address
- * each element should be in form of
- * {
- * translatedLabel:
- * key:
- * translatedValue:
- * }
- */
- components(lang) {
- return [];
- }
-
- componentLabelForKey(key, lang) {
- const component = this.components(lang).find(component => component.key === key);
- return component === undefined ? '' : component.translatedLabel;
- }
-
- componentValueForKey(key, lang) {
- const component = this.components(lang).find(component => component.key === key);
- return component === undefined ? '' : component.translatedValue;
- }
-
- fullAddress(lang) {
- return null;
- }
-
- coordinate() {
- return {
- lat: 0,
- lng: 0,
- }
- }
-
- coordinates() {
- return [];
- }
-
- /**
- * Return a normalized confident level from 0 - 10
- */
- confidence() {
- return 0;
- }
-
- distanceTo(address) {
- const cord1 = turf.point([this.coordinate().lat, this.coordinate().lng]);
- const cord2 = turf.point([address.coordinate().lat, address.coordinate().lng]);
-
- return turf.distance(cord1, cord2, {units: 'kilometers'});
- }
-}
-
-Address.LANG_EN = 'eng';
-Address.LANG_ZH = 'chi';
-
diff --git a/web/src/lib/models/land-address.js b/web/src/lib/models/land-address.js
deleted file mode 100644
index 12d6eae..0000000
--- a/web/src/lib/models/land-address.js
+++ /dev/null
@@ -1,67 +0,0 @@
-import Address from './address';
-
-export default class LandAddress extends Address{
- constructor(landRecord) {
- super();
- this.record = landRecord;
- }
-
- /**
- * Return the detailed components of the parsed address
- * each element should be in form of
- * {
- * translatedLabel:
- * key:
- * translatedValue:
- * }
- */
- components(lang) {
- if (lang === Address.LANG_EN) {
- return [{
- translatedValue: this.record.nameEN,
- key: name,
- translatedLabel: "Name"
- }];
- } else if (lang === Address.LANG_ZH) {
- return [{
- translatedValue: this.record.nameZH,
- key: name,
- translatedLabel: "Name"
- }];
- }
- }
-
- fullAddress(lang) {
- if (lang === Address.LANG_EN) {
- return this.record.addressEN;
- } else if (lang === Address.LANG_ZH) {
- return this.record.addressZH;
- }
- }
-
- coordinate() {
- return {
- lat: this.record.lat,
- lng: this.record.lng,
- }
- }
-
- coordinates() {
-
- return [{
- lat: this.record.lat,
- lng: this.record.lng,
- }];
- }
-
- /**
- * Return a normalized confident level from 0 - 10
- */
- confidence() {
- return 0;
- }
-}
-
-Address.LANG_EN = 'eng';
-Address.LANG_ZH = 'chi';
-
diff --git a/web/src/lib/models/ogcio-adress.js b/web/src/lib/models/ogcio-adress.js
deleted file mode 100644
index 0ef7e11..0000000
--- a/web/src/lib/models/ogcio-adress.js
+++ /dev/null
@@ -1,76 +0,0 @@
-import Address from './address';
-import ogcioHelper from "./../../utils/ogcio-helper.js";
-
-export default class OGCIOAddress extends Address {
- constructor(ogcioRecord) {
- super();
- this.record = ogcioRecord;
- this.flattenedComponents = null;
- }
-
- components(lang) {
- if (this.flattenedComponents === null) {
- this.flattenedComponents = this.flattenComponents();
- }
- if (lang === Address.LANG_EN) {
- return this.flattenedComponents['eng'];
- } else {
- return this.flattenedComponents['chi'];
- }
- }
-
- flattenComponents() {
- const flattenedComponents = {
- [Address.LANG_EN]: [],
- [Address.LANG_ZH]: [],
- };
- const langs = [Address.LANG_ZH, Address.LANG_EN]
- for (const lang of langs) {
- for (const key of Object.keys(this.record[lang])) {
- flattenedComponents[lang].push({
- key,
- translatedLabel: ogcioHelper.textForKey(key, lang),
- translatedValue: ogcioHelper.textForValue(this.record, key, lang),
- });
- }
- }
-
- return flattenedComponents;
- }
-
- fullAddress(lang) {
- if (lang === Address.LANG_EN) {
- return ogcioHelper.fullChineseAddressFromResult(this.record['eng']);
- } else {
- return ogcioHelper.fullChineseAddressFromResult(this.record['chi']);
- }
- }
-
- coordinate() {
- const geo = {
- lat: 0,
- lng: 0,
- };
- if (this.record.geo !== undefined && this.record.geo.length > 0) {
- geo.lat = this.record.geo[0].Latitude;
- geo.lng = this.record.geo[0].Longitude;
- }
- return geo;
- }
-
- coordinates() {
- return [];
- }
-
- confidence() {
- return Math.min(
- 4,
- (this.record.matches
- .filter(match => match.matchedKey === key)
- .map(match => match.confident)
- .reduce((p, c) => c, 0) *
- 5) |
- 0
- );
- }
-}
\ No newline at end of file
diff --git a/web/src/pages/AddressSearcher.vue b/web/src/pages/AddressSearcher.vue
new file mode 100644
index 0000000..75b06b6
--- /dev/null
+++ b/web/src/pages/AddressSearcher.vue
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+ 我哋幫你解決難搞地址
+
+
+
+ 輸入中英文香港地址,我們幫你解析成
+ 地區、
+ 街道門牌、
+ 大廈、
+ 坐標,連
+ 區議會選區都有
+
+
+
+
+
+
+
+ 進階選項
+
+
+
+
+ 拆地址
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 識揼code?
+
+
+ 如果你識Python, node.js, vue.js,歡迎過嚟俾意見幫吓手!
+
+
+ Github
+
+
+
+
+
+
+
+
+ 想一次過轉大量地址?
+
+
+ 零技術含量,簡單Copy and Paste就得,仲可以下載CSV
+
+
+ 即刻試
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web/src/pages/BatchAddressSearcher.vue b/web/src/pages/BatchAddressSearcher.vue
index b4c57e5..7856dec 100644
--- a/web/src/pages/BatchAddressSearcher.vue
+++ b/web/src/pages/BatchAddressSearcher.vue
@@ -1,6 +1,7 @@
-
+
+
我哋幫你解決難搞地址
@@ -14,237 +15,264 @@
坐標,連
區議會選區都有
-
-
-
+
+
+
進階選項
-
-
- 拆地址
-
- 下載 CSV
-
-
-
-
-
-
+
+
+ 拆地址
+
+ 下載 CSV
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ field }} |
-
+
+
+
+
- {{ this.errorMessage }}
-
-
-
-
-
+
+{{ this.errorMessage }}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
+ return records;
+}
+
diff --git a/web/src/pages/ResultTable.vue b/web/src/pages/ResultTable.vue
deleted file mode 100644
index 3458dc2..0000000
--- a/web/src/pages/ResultTable.vue
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
- {{ field }} |
-
-
-
-
-
-
\ No newline at end of file
diff --git a/web/src/route.js b/web/src/route.js
index 8675b02..4a3af87 100644
--- a/web/src/route.js
+++ b/web/src/route.js
@@ -1,5 +1,6 @@
import Vue from 'vue'
import Router from 'vue-router'
+import AddressSearcher from './pages/AddressSearcher'
import BatchAddressSearcher from './pages/BatchAddressSearcher'
Vue.use(Router)
@@ -8,6 +9,11 @@ export default new Router({
{
path: '/',
name: 'search',
+ component: AddressSearcher
+ },
+ {
+ path: '/batch',
+ name: 'search_batch',
component: BatchAddressSearcher
}
]
diff --git a/web/src/utils/ogcio-helper.js b/web/src/utils/ogcio-helper.js
index 3ff3e43..90c23fd 100644
--- a/web/src/utils/ogcio-helper.js
+++ b/web/src/utils/ogcio-helper.js
@@ -1,9 +1,3 @@
-/**
- * Some helper functions for showing OGCIO address result
- * definations:
- * https://www.als.ogcio.gov.hk/docs/Data_Dictionary_for_ALS_EN.pdf
- */
-
const OGCIO_KEY_BLOCK = 'Block';
const OGCIO_KEY_PHASE = 'Phase';
const OGCIO_KEY_ESTATE = 'Estate';
@@ -53,11 +47,6 @@ const keys = {
}
}
-
-function safeFieldValue(obj, key) {
- return obj && obj[key] ? obj[key]: '';
-}
-
function textForKey(key, lang) {
return keys[lang]
? (keys[lang][key]
@@ -66,6 +55,35 @@ function textForKey(key, lang) {
: key;
}
+function textForValue(record, key, lang) {
+
+ if (!record[lang]) {
+ return '';
+ }
+
+ if (typeof(record[lang][key]) === 'string') {
+ return record[lang][key];
+ }
+
+ const obj = Object.values(record[lang][key]);
+ let val = obj[0] || '';
+
+ if(obj[1]) {
+ if(obj[1].PhaseName) {
+ val = val + (val == '' ? '' : ', ') + obj[1].PhaseName;
+ } else {
+ val = val + (val == '' ? '' : ', ') + obj[1];
+ }
+ }
+
+ return val;
+}
+
+
+
+function safeFieldValue(obj, key) {
+ return obj && obj[key] ? obj[key]: '';
+}
function engBuildingNumberFromField(field) {
if (!field || (!field.BuildingNoFrom && !field.BuildingNoTo)) {
@@ -89,63 +107,6 @@ function chineseBuildingNumberFromField(field) {
return `${field.BuildingNoTo ? field.BuildingNoTo : field.BuildingNoFrom}號`;
}
}
-
-function prettyPrintBlock(blockObj, lang) {
- if (lang === 'chi') {
- return `${blockObj.BlockNo}${blockObj.BlockDescriptor}`;
- } else if (lang === 'eng') {
- return `${blockObj.BlockDescriptor}${blockObj.BlockNo}`;
- }
-}
-
-function prettyPrintEstate(estateObj, lang) {
- let estateName = estateObj.EstateName;
- const phase = estateObj[OGCIO_KEY_PHASE];
- if (lang === 'chi') {
- if (phase) {
- estateName = `${estateName}${safeFieldValue(estateObj[OGCIO_KEY_PHASE], 'PhaseNo')}${safeFieldValue(estateObj[OGCIO_KEY_PHASE], 'PhaseName')}`;
- }
-
- } else if (lang === 'eng') {
- if (phase) {
- estateName = `${safeFieldValue(estateObj[OGCIO_KEY_PHASE], 'PhaseName')}${safeFieldValue(estateObj[OGCIO_KEY_PHASE], 'PhaseNo')},${estateName}`;
- }
- }
- return estateName;
-}
-
-function prettyPrintStreet(streetObj, lang) {
- if (lang === 'chi') {
- return `${safeFieldValue(streetObj, 'StreetName')}${chineseBuildingNumberFromField(streetObj)}`;
- } else if (lang === 'eng') {
- return `${engBuildingNumberFromField(streetObj)} ${safeFieldValue(streetObj, 'StreetName')}`;
- }
-}
-
-function textForValue(record, key, lang) {
-
- if (!record[lang]) {
- return '';
- }
-
- if (typeof(record[lang][key]) === 'string') {
- return record[lang][key];
- }
-
- if (key === OGCIO_KEY_ESTATE) {
- return prettyPrintEstate(record[lang][key], lang);
- } else if (key === OGCIO_KEY_BLOCK) {
- return prettyPrintBlock(record[lang][key], lang);
- } else if (key === OGCIO_KEY_STREET) {
- return prettyPrintStreet(record[lang][key], lang);
- }
-
- return Object.values(record[lang][key]).join();
-}
-
-
-
-
/**
* Format the chinese address from the given result set
* @param {*} result
diff --git a/web/src/utils/proj-convertor.js b/web/src/utils/proj-convertor.js
deleted file mode 100644
index ef826af..0000000
--- a/web/src/utils/proj-convertor.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import proj4 from 'proj4';
-
-proj4.defs([
- [
- 'EPSG:2326',
- '+proj=tmerc +lat_0=22.31213333333334 +lon_0=114.1785555555556 +k=1 +x_0=836694.05 +y_0=819069.8 +ellps=intl +towgs84=-162.619,-276.959,-161.764,0.067753,-2.24365,-1.15883,-1.09425 +units=m +no_defs'],
- [
- 'EPSG:4326',
- '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'
- ]
- ]);
-
-export default {
- projTransform: (fromProjection, toProjection, coordinates) => {
- return proj4(proj4(fromProjection), proj4(toProjection), coordinates)
- }
-}
\ No newline at end of file