Visualization of the floating population
- java v1.8
- node.js v12.16.2
- npm v6.14.4
- VS Code ๋๋ ๊ทธ์ธ Frontend ๊ฐ๋ฐ ํด
- Spring Tool Suite 4
* ์ ์ฒด
frontend // Client ์น ์๋น์ค (node, npm, react ๊ตฌ์ฑ)
src // backend ์๋น์ค : java class (๋ก๊ทธ์ธ ์ฒ๋ฆฌ)
pom.xml // maven dependencies : springframework ์ฌ์ฉ
* Frontend (React App ๊ธฐ๋ณธ ๊ตฌ์ฑ)
external // Vector Tile ์๋น์ค ์ ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
node_external // node_module ๊ด๋ จ ๋ณ๋ ์ปค์คํ
ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
public
src // Web ๋ก์ง
component
AuthenticatedRoute.jsx // ๋ก๊ทธ์ธ ์ธ์ฆ ์ฒ๋ฆฌ
InstructorApp.jsx // ํ์ด์ง ๋ผ์ฐํฐ ์ฒ๋ฆฌ
LoginComponent.jsx // ๋ก๊ทธ์ธ ์ปดํฌ๋ํธ
LogoutComponent.jsx // ๋ก๊ทธ์์ ์ปดํฌ๋ํธ
MenuComponent.jsx // ์ต์๋จBar ๋ฉ๋ด ์ปดํฌ๋ํธ
MainComponent.jsx // ์ง๋/์ฐจํธ ์์ญ ์ปดํฌ๋ํธ
MapScript.js // ์ง๋๊ธฐ๋ฅ ์คํฌ๋ฆฝํธ
ChartScript.js // ์ฐจํธ๊ธฐ๋ฑ ์คํฌ๋ฆฝํธ
css
js
redux // react-redux ์ฌ์ฉ (์ํ ๊ฐ ์ ์ฅ)
service
AuthenticationService.js // ๋ก๊ทธ์ธ ํ์ด์ง์์ ๋ก๊ทธ์ธ ์ backend ํต์ ๋ฐ ์ธ์ฆ ์ฒ๋ฆฌ ํ์ธ
CustomFunc.js // ์ง๋/์ฐจํธ ๊ธฐ๋ฅ์ ํ์ํ ํจ์๋ค
App.css // ์ปค์คํ
css ํฌํจ
App.js
index.css
index.js
server.json // HTTP API ์๋ฒ์ Connect ํ๊ธฐ ์ํ ์ ๋ณด
serviceWorker.js
package.json // npm, node ๊ด๋ จ ์ค์
Frontend ์คํ : cd /root/population/frontend
- ์คํ
$ cd frontend
$ npm start
- ๋น๋
$ cd frontend
$ npm run build
* ๊ฐ๋ฐ ํ๊ฒฝ ์
- package.json => "proxy": "http://localhost:8066" ์ต์
ํ์ธ. (server port์ ๋ง๋์ง ํ์ธ)
backend ์คํ : spring boot tool ์ฌ์ฉ
- resources/application.properties ํ์ธ
- H2 DB ๊ฒฝ๋ก ์ค์ ๋ณ๊ฒฝ : spring.datasource.url -> {๋ก์ปฌ ๊ฒฝ๋ก}
- default port : 8066 (server.port ์ต์
์ผ๋ก ๋ณ๊ฒฝ ๊ฐ๋ฅ)
- ์คํ
- population ํ๋ก์ ํธ => Spring Boot App์ผ๋ก ์คํ
- ๋น๋
- maven build -> mvn clean install
- target ํด๋ ํ์ธ
- jar ๋ฐฐํฌ ๋ฐ ์คํ
- ๋ฐฐํฌํ ํน์ ๋๋ ํ ๋ฆฌ ์์ฑ.
- mvn build ํ target ํด๋์ ๋ชจ๋ ํ์ผ ๋ณต์ฌ.
- $ java -jar population-0.0.1-SNAPSHOT.jar ๋ช
๋ น์ด๋ก ์คํ
- http://localhost:8066 URL๋ก ์ ์ ๋์ ํ์ธ
- git clone ๋ฐ serve ๋ฐฉ๋ฒ
$ git clone https://github.com/bbbbbra/population.git
$ cd population/frontend
$ npm install
$ npm run build
$ serve -l 8066 -s build
- http://localhost:8066 URL๋ก ์ ์ ๋์ ํ์ธ
- VectorTile ๋ฐ Chart ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํ ์ ์ ์ ๋ณด
๊ฒฝ๋ก : cd population/frontend/src
์ ์์ ๋ณด : server.json ํ์ผ ํ์ธ
{
"user": "ltdb",
"password": "ltdb",
"database": "default",
"protocol": "http",
"host": "fbg01",
"port": [4762, 4763, 4764, 4765, 4766, 4767, 4768, 4769, 4770, 4771, 4772, 4773]
}
- ํ ์ด๋ธ DDL CREATE ๊ด๋ จํด์๋ HTTP API ์๋ฒ README ์ฐธ๊ณ .
* ltdb_fp ํ
์ด๋ธ : ์ ๋์ธ๊ตฌ ๋ฐ์ดํฐ (์ง๋/์ฐจํธ ๊ตฌํ ์ ์ฌ์ฉ)
CREATE table ltdb_fp (
adm_code string,
x double,
y double,
recordid integer,
block_cd long,
exist_m_00 double,
exist_m_10 double,
exist_m_20 double,
exist_m_30 double,
exist_m_40 double,
exist_m_50 double,
exist_m_60 double,
exist_m_70 double,
exist_m_80 double,
exist_m_90 double,
exist_f_00 double,
exist_f_10 double,
exist_f_20 double,
exist_f_30 double,
exist_f_40 double,
exist_f_50 double,
exist_f_60 double,
exist_f_70 double,
exist_f_80 double,
exist_f_90 double,
home_m_00 double,
home_m_10 double,
home_m_20 double,
home_m_30 double,
home_m_40 double,
home_m_50 double,
home_m_60 double,
home_m_70 double,
home_m_80 double,
home_m_90 double,
home_f_00 double,
home_f_10 double,
home_f_20 double,
home_f_30 double,
home_f_40 double,
home_f_50 double,
home_f_60 double,
home_f_70 double,
home_f_80 double,
home_f_90 double,
work_m_00 double,
work_m_10 double,
work_m_20 double,
work_m_30 double,
work_m_40 double,
work_m_50 double,
work_m_60 double,
work_m_70 double,
work_m_80 double,
work_m_90 double,
work_f_00 double,
work_f_10 double,
work_f_20 double,
work_f_30 double,
work_f_40 double,
work_f_50 double,
work_f_60 double,
work_f_70 double,
work_f_80 double,
work_f_90 double,
in_m_00 double,
in_m_10 double,
in_m_20 double,
in_m_30 double,
in_m_40 double,
in_m_50 double,
in_m_60 double,
in_m_70 double,
in_m_80 double,
in_m_90 double,
in_f_00 double,
in_f_10 double,
in_f_20 double,
in_f_30 double,
in_f_40 double,
in_f_50 double,
in_f_60 double,
in_f_70 double,
in_f_80 double,
in_f_90 double,
geohash string,
geometry string,
event_time string
)
USING r2 OPTIONS
(
table '900',
host 'fbg02',
port '18900',
partitions 'event_time geohash',
mode 'nvkvs',
rowstore 'false',
at_least_one_partition_enabled 'no',
group_query_enabled 'yes',
geometry_type 'point'
)
* ltdb_fp_history ํ
์ด๋ธ : ๋ ์ง๋ณ ์ ๋์ธ๊ตฌ ํ์คํ ๋ฆฌ ํ
์ด๋ธ (์ต์ ๋ ์ง ์
๋ฐ์ดํธ(์บ๋ฆฐ๋)์ )
* login ๋ฐ ์๋ก๊ณ ์นจ ์ ์ต์ ๋ ์ง๋ 'select max(event_time) from ltdb_fp_history'๋ก ๊ฐ์ ธ์ค๋ฏ๋ก, ๋ฐ์ดํฐ ์ ์ฌ ์ ltdb_fp_history ํ
์ด๋ธ์ ๋ฐ์ดํฐ๋ฅผ ์
๋ฐ์ดํธ(์ถ๊ฐ)ํด์ฃผ์ด์ผ ํ๋ค.
CREATE table ltdb_fp_history (
event_time string,
table_name string
)
USING r2 OPTIONS
(
table '910',
host 'fbg02',
port '18900',
partitions 'table_name event_time',
mode 'nvkvs',
rowstore 'false',
at_least_one_partition_enabled 'no'
)
- cd frontend/src/component
- ChartScript.js 34๋ฒ์งธ code line
- MapScript.js 1100๋ฒ์งธ code line
- circleRadius ๋ณ์ ๊ฐ ๋ณ๊ฒฝ => default = 2; (๊ธฐ๋ณธ๋จ์ Km)
-
Service ๊ด๋ จ source code -> population/src/main/java/kr/co/ltdb/controller/MemberController.java
-
javascript ์์ ํ ์คํธ๊ฐ ์๋๋ผ๋ฉด postman(https://www.postman.com/) ์ค์นํ์ฌ, URL ํ ์คํธํ๋ฉด ํธ๋ฆฌํฉ๋๋ค.
-
๊ณ์ ๋ฆฌ์คํธ ์กฐํ
- URL : localhost:{port}/api/members
- http request method : GET
- ๊ณ์ ์กฐํ
- URL : localhost:{port}/api/members
- http request method : GET
- form-data
- key value ํํ
- name : {๊ฐ}
- ๊ณ์ ์์ฑ
- URL : localhost:{port}/api/members/create
- http request method : POST
- form-data
- key value ํํ
- name : {๊ฐ}, pw : {๊ฐ}
- HTTP API Connection / SQL ๋ฐ ๊ฒฐ๊ณผ ํ์ธ
- ํด๋น ์๋น์ค๋ Web(Client) ํ๊ฒฝ์์ Thrift๋ฅผ ํตํ ๋ด์ฉ์ ๋๋ค. (html/javascript ๊ตฌํ)
1) Basic Query
- frontend/src/js ํด๋์ browser-connector.js import ํ์ฌ ์ฌ์ฉ
- Input Value : String (SQL๋ฌธ)
- Out Value : SQL๋ฌธ ๊ฒฐ๊ณผ์ ๋ํ Json Data
* MapdCon ๊ฐ์ฒด๋ฅผ ์ด์ฉํ ์ ์ ๋ฐ SQL ํ
์คํธ
new MapdCon() // API์๋ฒ ์ ์
.host("fbg01")
.port("4762")
.dbName("default")
.user("ltdb")
.password("ltdb")
.connectAsync()
.then(function (connector) { // ์ปค๋ฅ์
๊ฐ์ฒด
var query = "select max(event_time) from ltdb_fp_history where table_name='ltdb_fp'"; // SQL๋ฌธ ์์ฑ
connector.queryAsync(query, {columnarResults: false}).then(function (result) { // ์ปค๋ฅ์
๊ฐ์ฒด๋ฅผ ์ด์ฉํ SQL ์์ฒญ
console.log(result); // ๊ฒฐ๊ณผ ๊ฐ
});
})
2) Vector tile Query
* mapbox-gl api ์์ง ํ์
- ๊ด๋ จ ๋ผ์ด๋ธ๋ฌ๋ฆฌ Import ํ์
- frontend/src/js ํด๋์ mapbox-gl.js import ํ์ฌ ์ฌ์ฉ
- frontend/external ๋๋ frontend/build/static/js ํด๋์ global-mercator.js, pako.js, vectortile-utils.js import ํ์ฌ ์ฌ์ฉ
//mapboxgl ์ฌ์ฉํ๊ธฐ ์ํ ํ ํฐ ํ์
mapboxgl.accessToken = {ํ ํฐ๊ฐ(String)}; //'pk.eyJ1IjoibGVlc2giLCJhIjoiY0thWXdQbyJ9.fPGnL5s0k8ptNPY7P1S1aA';
//API์๋ฒ์ Tile ์์ฒญ ์ ์ฌ์ฉํ ํฌํธ๋ฐฐ์ด ํ์.
var ports = [4762, 4763, 4764, 4765, 4766, 4767, 4768, 4769, 4770, 4771, 4772, 4773];
//Map ๊ฐ์ฒด ์์ฑ
var map = new mapboxgl.Map({
container: {html div id},
hash: true,
style: {
'version': 8,
'sources': {
'raster-tiles': {
'type': 'raster',
'tiles': [
'http://a.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png'
],
'tileSize': 256
}
},
'layers': [{
'id': 'base-map',
'type': 'raster',
'source': 'raster-tiles',
'minzoom': 0,
'maxzoom': 22
}]
},
center: [127, 37.55], //126.986, 37.565
zoom: 11,
maxZoom: 16,
minZoom: 8.5,
tilesFunctionParams: function (tile) { // ์ปค์คํ
ํจ์ : tile ์์ฒญ ์ ์ฌ๋ฌ port๋ก ์์ฒญํ๊ธฐ ์ํ ์ค์
const port = ports.shift();
ports.push(port);
return {
host: config.host,
port: port,
eventTime1: null,
eventTime2: null
}
}
//interactive: false
});
//Map load ๋ฐ ํ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ฐ์ ธ์ค๊ธฐ
//Vector layer ์์ฑ
map.on('load', function() {
map.style.dispatcher.broadcast('loadWorkerSource', { //broadcast๋ฅผ ์ด์ฉํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ import
name: "pako",
url: `http://${window.location.host}/static/js/pako.js`
}, function (e) {
if (e) {
console.log(e);
}
});
map.style.dispatcher.broadcast('loadWorkerSource', {
name: "global-mercator",
url: `http://${window.location.host}/static/js/global-mercator.js`
}, function (e) {
if (e) {
console.log(e);
}
});
map.style.dispatcher.broadcast('loadWorkerSource', {
name: "vectortile-utils",
url: `http://${window.location.host}/static/js/vectortile-utils.js`
}, function (e) {
if (e) {
console.log(e);
}
});
//Vector Source ์์ฑ
//renderSqlPost ํจ์ : SQL๋ฌธ ํ๋๋ง ์์ฒญํ์ฌ ๊ฒฐ๊ณผ๊ฐ ๋ฆฌํด ( ex)ํ์ฌ ์ ๋์ธ๊ตฌ ํํ ์ ์ฌ์ฉ )
map.addSource('vector-tile', {
type: 'vector',
tilesFunction: `function (tile) { // ์ปค์คํ
ํจ์
var host = tile.tilesFunctionParams.host;
var port = tile.tilesFunctionParams.port;
var sql = ""; //๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ Query๋ฌธ ์์ฑ
var typeName = "ltdb_fp";
var aggrType = "sum";
var multiple = false;
return renderSqlPost(host, port, tile, sql, typeName, aggrType, multiple, null);
}`,
minzoom: 0,
maxzoom: 16.1
});
//์ฆ๊ฐ ์ ๋์ธ๊ตฌ ๋ฐ์ดํฐ ๊ฐ์ ธ์ฌ ๋ ์ฌ์ฉํ๋ ํจ์
* ํ๋์ ์์ด๋ฏ๋ก ๊ฐ์ map๊ฐ์ฒด ์ฌ์ฉ.
//renderSqlDiffPost ํจ์ : SQL๋ฌธ 2๊ฐ์ง ์์ฒญํ์ฌ subtractํ ๊ฒฐ๊ณผ๊ฐ ๋ฆฌํด ( ex)์ฆ๊ฐ ์ ๋์ธ๊ตฌ ํํ ์ ์ฌ์ฉ )
map.addSource('vector-tile', {
type: 'vector',
tilesFunction: `function (tile) { // ์ปค์คํ
ํจ์
var host = tile.tilesFunctionParams.host;
var port = tile.tilesFunctionParams.port;
//์ปฌ๋ผ ๋ณ subtract์ sql1 - sql2
var sql1 = ""; //๊ธฐ์ค ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ Query๋ฌธ ์์ฑ
var sql2 = ""; //๋น๊ต ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ Query๋ฌธ ์์ฑ
var typeName = "ltdb_fp";
var aggrType = "sum";
var multiple = false;
return renderSqlDiffPost(host, port, tile, sql1, sql2, typeName, aggrType, multiple, null);
}`,
minzoom: 0,
maxzoom: 16.1
});
//Vector Layer ์์ฑ
map.addLayer({...});
});
- ๊ตฌํ๋ Source์ HTTP API Service ์ฌ์ฉ ํ์ธ
- ์บ๋ฆฐ๋ ๊ด๋ จํ์ฌ, ltdb_fp_history ํ ์ด๋ธ์ ์ต์ ๋ ์ง Query
- ์์ค ๊ฒฝ๋ก : frontend/src/component/MenuComponent.jsx
- 69๋ฒ์งธ ๋ผ์ธ ํ์ธ
- ์ฌ์ฉ SQL
- "select max(event_time) from ltdb_fp_history where table_name='ltdb_fp' limit 1"
- Vector Tile Query
- ์์ค ๊ฒฝ๋ก : frontend/src/component/MenuComponent.jsx
1) 92๋ฒ์งธ ๋ผ์ธ ํ์ธ
- ์ฌ์ฉ SQL
- "SELECT (exist_m_00 + exist_m_10 + exist_m_20 + exist_m_30 + exist_m_40 + exist_m_50 + exist_m_60 + exist_m_70 + exist_m_80 + exist_m_90 + exist_f_00 + exist_f_10 + exist_f_20 + exist_f_30 + exist_f_40 + exist_f_50 + exist_f_60 + exist_f_70 + exist_f_80 + exist_f_90) as exist, geometry FROM ltdb_fp WHERE event_time = '${currPrevDateString.curr}'"
2) 192๋ฒ์งธ ๋ผ์ธ ํ์ธ
- "SELECT (exist_m_00 + exist_m_10 + exist_m_20 + exist_m_30 + exist_m_40 + exist_m_50 + exist_m_60 + exist_m_70 + exist_m_80 + exist_m_90 + exist_f_00 + exist_f_10 + exist_f_20 + exist_f_30 + exist_f_40 + exist_f_50 + exist_f_60 + exist_f_70 + exist_f_80 + exist_f_90) as exist, geometry FROM ltdb_fp WHERE event_time = '${currPrevDateString.curr}'"; //ํ์ฌ ๋ ์ง
- "SELECT (exist_m_00 + exist_m_10 + exist_m_20 + exist_m_30 + exist_m_40 + exist_m_50 + exist_m_60 + exist_m_70 + exist_m_80 + exist_m_90 + exist_f_00 + exist_f_10 + exist_f_20 + exist_f_30 + exist_f_40 + exist_f_50 + exist_f_60 + exist_f_70 + exist_f_80 + exist_f_90) as exist, geometry FROM ltdb_fp WHERE event_time = '${currPrevDateString.prev}'"; //์ด์ ๋ ์ง
- Chart Query
- ์์ค ๊ฒฝ๋ก : frontend/src/service/CustomFunc.jsx
1) ๋ง๋์ฐจํธ - ํ์ฌ๋ ์ง ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ. 329๋ฒ์งธ ๋ผ์ธ ํ์ธ
- `SELECT
(sum(exist_m_00) + sum(exist_m_10)) as exist_m_10, sum(exist_m_20) as exist_m_20, sum(exist_m_30) as exist_m_30,
sum(exist_m_40) as exist_m_40, sum(exist_m_50) as exist_m_50, (sum(exist_m_60) + sum(exist_m_70) +
sum(exist_m_80) + sum(exist_m_90)) as exist_m_60,
(sum(exist_f_00) + sum(exist_f_10)) as exist_f_10, sum(exist_f_20) as exist_f_20, sum(exist_f_30) as exist_f_30,
sum(exist_f_40) as exist_f_40, sum(exist_f_50) as exist_f_50, (sum(exist_f_60) + sum(exist_f_70) +
sum(exist_f_80) + sum(exist_f_90)) as exist_f_60, event_time
FROM ltdb_fp
WHERE ST_CONTAINS(ST_GEOMFROMTEXT('${wkt}'), geometry) AND event_time = '${eventTime1}'
GROUP BY event_time ORDER BY event_time`
1) ๋ผ์ธ์ฐจํธ - 24์๊ฐ ๊ธฐ์ค ์๊ฐ ๋ณ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ. 341๋ฒ์งธ ๋ผ์ธ ํ์ธ
- `SELECT
(sum(exist_m_00) + sum(exist_m_10)) as exist_m_10, sum(exist_m_20) as exist_m_20, sum(exist_m_30) as exist_m_30,
sum(exist_m_40) as exist_m_40, sum(exist_m_50) as exist_m_50, (sum(exist_m_60) + sum(exist_m_70) +
sum(exist_m_80) + sum(exist_m_90)) as exist_m_60,
(sum(exist_f_00) + sum(exist_f_10)) as exist_f_10, sum(exist_f_20) as exist_f_20, sum(exist_f_30) as exist_f_30,
sum(exist_f_40) as exist_f_40, sum(exist_f_50) as exist_f_50, (sum(exist_f_60) + sum(exist_f_70) +
sum(exist_f_80) + sum(exist_f_90)) as exist_f_60,
substring(event_time, 0, ${eventTimeFormat.length - 2}) as event_time
FROM ltdb_fp
WHERE ST_CONTAINS(ST_GEOMFROMTEXT('${wkt}'), geometry) AND event_time IN(${QueryTimeArray.toString()}) GROUP BY event_time ORDER BY event_time`;