2020년 2월 16일 일요일

OpenStreetMap 타일 서버 개발

이 글은 OSM(OpenStreetMap) 타일 서버 개발하는 예제를 간략히 다룬다. 

결과

OSM이란
OSM은 전세계 지도를 커버하는 사용자가 작성하는 맵이다. OSM은 매분 업데이트되고, 무료이다. 그리고, 상세한 데이터를 제공한다.

HW 환경
영국 지도만 4G RAM, 60GB 디스크가 필요하다. 전체 planet map은 32G RAM, 1TB SSD가 필요하다.

소프트웨어 준비
sudo apt update; sudo apt upgrade

PostgreSQL Database Server와 PostGIS Extension 설치
sudo apt install postgresql postgresql-contrib postgis postgresql-10-postgis-2.4
sudo -u postgres -i
createuser osm
createdb -E UTF8 -O osm gis

psql -c "CREATE EXTENSION postgis;" -d gis
psql -c "CREATE EXTENSION hstore;" -d gis
Set osm as the table owner.

psql -c "ALTER TABLE spatial_ref_sys OWNER TO osm;" -d gis
Exit from the postgres user.

exit

sudo adduser osm

맵 스타일시트와 맵 데이터 다운로드
su - osm

wget https://github.com/gravitystorm/openstreetmap-carto/archive/v4.20.0.tar.gz
tar xvf v4.20.0.tar.gz

만약 planet data 다운로드하려면 아래와 같이 진행한다.
wget -c http://planet.openstreetmap.org/pbf/planet-latest.osm.pbf

다음은 영국지도이고 985M 용량이 필요하다.

wget -c http://download.geofabrik.de/europe/great-britain-latest.osm.pbf
Exit from osm user.

exit

맵 데이터 임포트
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile

Output:
Setting up swapspace version 1, size = 2097148 KiB
no label, UUID=h32b3e10-0779-4865-9ea0-6e2af8f3kea9
Enable the swap file

sudo swapon /swapfile

sudo nano /etc/ssh/ssh_config
ServerAliveInterval 60

PostgreSQL에 맵 데이터 임포트 
맵 데이터 임포트를 위해, osm2pgsql을 사용해 OSM 데이터를 postGIS 지원용 PostgreSQL DB로 변환한다.
sudo apt install osm2pgsql
su - osm
osm2pgsql --slim -d gis --hstore --multi-geometry --number-processes 8 --tag-transform-script /home/osm/openstreetmap-carto-4.20.0/openstreetmap-carto.lua --style /home/osm/openstreetmap-carto-4.20.0/openstreetmap-carto.style -C 12000 /home/osm/great-britain-latest.osm.pbf
exit

mod_tile와 Renderd 설치
sudo add-apt-repository ppa:osmadmins/ppa
sudo apt install libapache2-mod-tile renderd
systemctl status renderd

Mapnik Stylesheet 생성
sudo apt install curl unzip gdal-bin mapnik-utils libmapnik-dev nodejs npm
sudo npm install -g carto
su - osm
cd /home/osm/openstreetmap-carto-4.20.0/
scripts/get-shapefiles.py
carto project.mml > style.xml
exit

Fonts 설치
sudo apt install ttf-dejavu
sudo apt install fonts-noto-cjk fonts-noto-hinted fonts-noto-unhinted ttf-unifont

renderd 설정
sudo nano /etc/renderd.conf
num_threads=4
XML=/home/osm/openstreetmap-carto-4.20.0/style.xml
HOST=map.your-domain.com

plugins_dir=/usr/lib/mapnik/3.0/input/
mapnik-config --input-plugins

font_dir=/usr/share/fonts/truetype
font_dir_recurse=true

sudo nano /etc/init.d/renderd
RUNASUSER=osm

sudo chown osm:osm /var/lib/mod_tile/ -R
sudo systemctl daemon-reload
sudo systemctl restart renderd
sudo journalctl -eu renderd

Apache 설정
sudo nano /etc/apache2/sites-available/tileserver_site.conf
ServerName map.yourdomain.com
sudo systemctl restart apache2
map.your-domain.com/osm/0/0/0.png
osm-tile-for-world-map

Tiled Web Map 표시
OpenLayer
cd /var/www/
sudo wget https://github.com/openlayers/openlayers/releases/download/v5.3.0/v5.3.0.zip
sudo unzip v5.3.0.zip

index.html file 생성
sudo nano /var/www/index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Accessible Map</title>
<link rel="stylesheet" href="http://map.yourdomain.com/v5.3.0/css/ol.css" type="text/css">
<script src="http://map.yourdomain.com/v5.3.0/build/ol.js"></script>
<style>
  a.skiplink {
    position: absolute;
    clip: rect(1px, 1px, 1px, 1px);
    padding: 0;
    border: 0;
    height: 1px;
    width: 1px;
    overflow: hidden;
  }
  a.skiplink:focus {
    clip: auto;
    height: auto;
    width: auto;
    background-color: #fff;
    padding: 0.3em;
  }
  #map:focus {
    outline: #4A74A8 solid 0.15em;
  }
</style>
</head>
<body>
  <a class="skiplink" href="#map">Go to map</a>
  <div id="map" class="map" tabindex="0"></div>
  <button id="zoom-out">Zoom out</button>
  <button id="zoom-in">Zoom in</button>
  <script>
    var map = new ol.Map({
      layers: [
        new ol.layer.Tile({
          source: new ol.source.OSM({
             url: 'http://map.yourdomain.com/osm/{z}/{x}/{y}.png'
          })
       })
     ],
     target: 'map',
     controls: ol.control.defaults({
        attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
          collapsible: false
        })
     }),
    view: new ol.View({
       center: [244780.24508882355, 7386452.183179816],
       zoom:5
    })
 });

  document.getElementById('zoom-out').onclick = function() {
    var view = map.getView();
    var zoom = view.getZoom();
    view.setZoom(zoom - 1);
  };

  document.getElementById('zoom-in').onclick = function() {
     var view = map.getView();
     var zoom = view.getZoom();
     view.setZoom(zoom + 1);
  };
</script>
</body>
</html>

map.yourdomain.com/index.html
Leaflet

cd /var/www/
sudo wget http://cdn.leafletjs.com/leaflet/v1.4.0/leaflet.zip
sudo unzip leaflet.zip

sudo nano /var/www/index.html

<html>
<head>
<meta charset="UTF-8">
<title>My first osm</title>
<link rel="stylesheet" type="text/css" href="leaflet.css"/>
<script type="text/javascript" src="leaflet.js"></script>
<style>
   #map{width:100%;height:100%}
</style>
</head>

<body>
  <div id="map"></div>
  <script>
    var map = L.map('map').setView([53.555,9.899],5);
    L.tileLayer('http://map.yourdomain.com/osm/{z}/{x}/{y}.png',{maxZoom:18}).addTo(map);
</script>
</body>
</html>

map.yourdomain.com/index.html

Pre-render Tiles
render_list -m default -a -z 0 -Z 15 --num-threads=8
render_list -m default -a -z 0 -Z 15 --num-threads=8 --force
sudo add-apt-repository ppa:certbot/certbot
sudo apt install certbot
sudo apt install python3-certbot-apache
sudo certbot --apache --agree-tos --redirect --hsts --staple-ocsp --must-staple --email
osm tile server ubuntu 18.04 install

sudo a2enmod http2
sudo nano /etc/apache2/sites-enabled/tileserver_site-le-ssl.conf
Protocols h2 http/1.1
sudo systemctl restart apache2
nano /home/osm/openstreetmap-carto-4.20.0/project.mml

osm2pgsql: &osm2pgsql
  type: "postgis"
  dbname: "gis"
  key_field: ""
  geometry_field: "way"
  extent: "-20037508,-20037508,20037508,20037508"
Specify the IP address of PostgreSQL database server.

osm2pgsql: &osm2pgsql
  type: "postgis"
  host: "10.0.0.2"
  dbname: "gis"
  key_field: ""
  geometry_field: "way"
  extent: "-20037508,-20037508,20037508,20037508"

carto project.mml > style.xml
sudo nano /etc/postgresql/10/main/postgresql.conf
sudo nano /etc/postgresql/10/main/pg_hba.conf
host   gis   osm   10.0.0.1/32   trust
sudo systemctl restart postgresql
sudo systemctl restart renderd

sudo journalctl -eu renderd
sudo ufw allow in from 10.0.0.1 to any port 5432

레퍼런스

2020년 2월 15일 토요일

nodejs기반 Open data와 LAStools 활용한 포인트 클라우드 가시화

이 글은 nodejs기반 Open data와 LAStools 활용에 대한 간단한 예제를 소개한다.

결과


머리말
현재는 데이터가 석유보다 귀중한 자원이다. 이 글은 그래픽 분야에서 많은 관심을 가지는 점군(point cloud. 포인트 클라우드) 데이터를 지도 위에서 다루는 방법을 이야기할 것이다. 

이 데이터는 매우 크고(massive) 다루기가 쉽지 않다. 여기서는 LAStools, GDAL, QGIS를 사용해 이 데이터를 다룰 것이다. 

점군 전처리 과정
이 예시에서 사용한 점군 데이터는 원래 색상이 없었다. 이런 이유로, gdal_translate 를 이용해  지도를 GeoTIFF로 변환하고, 정사 영상을 이용해 각 점군을 색칠하여 lasmerge 로 병합하였다. 

이제 관심있는 영역만 lasclip도구로 잘라낸다. QGIS에서 적절한 경계 모양 파일을 만들어 사용했다. 

Pointscene 데이터 업로드
점군 데이터를 벡터와 함께 Pointscene에 업로드하였다. Pointscene nodejs 도구를 이용해 콘솔에서 직접 업로드한다. 방법은 이 링크를 참고한다.

다음 명령으로 pointscene-nodejs-cli 폴더로 이동해 설치한다. 사용법은 node pointscene --help로 확인한다.
cd pointscene-nodejs-cli 
npm install

이제 점군을 업로드한다.

pointscene 계정에 로긴하면 장면 설정에서 좌표 시스템 정보를 추가할 수 있다. 이후, 점군 범위가 기본맵에 표시된다. 

점군에 건물 면적과 도로 중심선 shp 파일을 추가한다. 

레퍼런스

cesium 기반 3차원 공간정보 데이터 그래픽 표시 방법

이 글은 3차원 cesium 기반 공간정보맵 그래픽 분석 방법을 개발하는 예제를 간략히 소개한다. 이 글은 cesium workshop 예제들을 참고하였다.
3차원 공간정보 데이터 그래픽 표시 결과

사용방법은 다음과 같다. 
cd cesium-workshop
npm install
npm start

그럼 다음과 같은 결과를 확인할 수 있다. 
실행 결과

레퍼런스

webpack기반 cesium.js 앱 개발

이 글은 webpack기반 cesium.js 앱 개발에 관한 간단한 예제를 다룬다.

cesium.js 로 만든 웹 앱

머리말
CesiumJS는 자바스크립트 라이브러리로 3D globe와 2D 맵을 웹 브라이저에서 가시화할 수 있다. 이 기술은 WebGL을 사용한다. CesiumJS 라이센스는 Apache 2.0이며, 상업용 및 비상업용으로 무료이다. 

CesiumJS는 지형, 이미지, 3D 타일을 상업용 Cesiumion플랫폼으로 스트리밍해 얻을 수 있다. Cesiumion은 CesiumJS 개발을 지원한다. 


개발
다음과 같이 설치한다.
npm install cesium

데모 예제 코드는 다음 링크를 참고한다. 

레퍼런스

2020년 2월 2일 일요일

도커 기반 워드프레스로 홈페이지 구축하기

이 글은 도커 기반 워드프레스로 홈페이지 구축하는 방법을 간단히 알아본다. 워드프레스는 손쉽게 홈페이지 컨텐츠를 개발하고 관리할 수 있는 도구이다. 워드프레스는 가장 많이 사용되는 블로그 시스템이다. 이 글에서 도커는 다음 이미지를 사용한다.
도커 이미지를 다운로드 받고, 다음 명령을 입력한다. 
docker run --name some-wordpress -e WORDPRESS_DB_NAME=word -e WORDPRESS_DB_USER=word -e WORDPRESS_DB_PASSWORD=word123 -p 8080:80 -d wordpress

그리고, localhost:8080 을 접속하면 다음 화면을 볼 수 있다. 

레퍼런스

2020년 2월 1일 토요일

기상관측소 만들기 - MQTT, Node-RED, Google Chart

이 글은 기상관측소를 만들기 위해 어떻게 MQTT, Node-RED, Google Chart 를 서로 연동하는 지에 대한 내용을 간단히 다루어 본다.
Node-RED 디자인

개요
이 예제는 다음 도구를 사용한다.
Microcontroller
  Cactus Micro Rev2 – Arduino w/ ESP8266 WiFi built-in
Display
  LCD 16×2
Sensors
  DHT11- Humidity and Temperature
Photoresistor – Light
  Messaging
MQTT
  Mosca – MQTT broker
Data Storage
  MongoDB
Data Analysis
  Google Chart
Programming
  Arduino Sketch (C++)
  Node-RED
Gallery
  Pictures
Bonus: Real-time Graphs
  ThingSpeak

개발 순서
이 예제는 ESP8266을 사용한다. 다음과 같이 펌웨어를 준비한다.
git clone https://github.com/tuanpmt/espduino
cd espduino
./esptool.py -p /dev/tty.usbmodem1421 write_flash 0x00000 esp8266/release/0x00000.bin 0x40000 esp8266/release/0x40000.bin

다음과 같이 코딩한다.
// ESP8266 WiFi 
#include <espduino.h>
#define PIN_ENABLE_ESP 13
#define SSID "yourSSID"
#define PASS "yourPassword"

void wifiCb(void* response)
{
 uint32_t status;
 RESPONSE res(response);

 if(res.getArgc() == 1) {
 res.popArgs((uint8_t*)&status, 4);
 if(status == STATION_GOT_IP) {
 Serial.println("WIFI CONNECTED");
 wifiConnected = true;
 } else {
 Serial.println("WIFI OFFLINE");
 wifiConnected = false;
 } 
 }
}

void setup() {

 // Write to ESP8266
 Serial1.begin(19200);
 // Write to Console
 Serial.begin(19200);

 // Enable ESP8266
 esp.enable();
 delay(500);
 esp.reset();
 delay(500);
 while(!esp.ready());

 // Setup WiFi
 esp.wifiCb.attach(&wifiCb);
 esp.wifiConnect(SSID, PASS);

}

void loop() {
 esp.process();
 if(wifiConnected) {
 // Do Something 
 }
}

다음과 같이 회로를 개발한다.

다음과 같이 통신 시스템을 개발한다. IoT 사용 프로토콜은 MQTT, AMQP, øMQ 등이 있다. 또한, 웹 기반 REST, WebSocket 통신도 있다. 이는 PubNub, Pusher, HiveMQ, OctoBlu 등과 같은 도구에서 지원한다.

여기서는 MQTT를 사용한다. 브로커는 Mosca, Mosquito가 가장 유명하다. HiveMQ는 SLA과 확장성을 지원하는 엔터프라이즈 플랫폼이다. 여기서는 Mosca를 선택했으며 이는 NodeJS를 사용한다.

MQTT 클라이언트는 Arduino, RPi 등을 선택할 수 있다.

데이터 저장은 SQL, NoSQL DB를 사용한다. NoSQL은 MongoDB나 DynamoDB 등이 있다. 저장은 key:value 페어 형식이다. 이 형식은 보통 JSON 포맷으로 데이터 주고 받는다. 여기서는 MongoDB를 사용한다.

메시징 흐름은 다음과 같다.
데이터 분석은 Google Chart node를 Node-RED에 설치해 사용한다.

아두이노 코딩은 여기를 참고한다. 노드 레드는 다음과 같이 디자인한다.

실시간 그래프는 ThingSpeak를 사용한다.

마무리
이 글은 간단히 IoT 기반 weather station을 개발하는 예제를 설명하였다. 요즘 많은 도구가 오픈소스로 나와 있어, 저비용으로 손쉽게 개발이 가능하다. 

NDT 기반 SLAM

이 글은 NDT(normal distribution transform) 기반 SLAM을 간략히 설명하고 개발방법을 정리한 것이다.


개요
로봇이 주변 공간 위치를 파악할 때 실시간으로 생성되는 지도가 필요하다. 보통, 로봇에 마운트된 거리 센서 등을 스캔 센서에서 얻은 상대 거리 좌표(x, y, z) 정보가 포함된 점군(point cloud)에서 지도를 생성해야 하므로, 이는 정확히 말하면 각 장면별로 획득한 스캔 데이터를 정합하는 문제로 치환할 수 있다.

각 장면에서 얻은 점군들이 {PCD1...PCDn} 이라면, 서로 중첩했을 때 오차가 제일 적은 정합 결과물이 제일 좋은 것이다. 이렇게 정합하는 방식 중 가장 유명한 것이 ICP(Iterative Closest Points)이다. ICP에서 모든 점들에 대해 정합 오차를 계산하면 시간이 너무 오래 걸리므로, 각 장면별 유일한 특징이 될만한 특징점을 먼저 추출한 후 ICP를 실행한다.

LiDAR를 이용한 실시간 정합은 보통 LiDAR SLAM으로 알려져 있는 데, 그 동작 메커니즘은 비슷하다. 다만, 빠른 계산 속도를 만족해야 한다는 조건이 더 붙는다.

NDT는 3D 공간에서 voxel 공간 인덱싱을 처리해, 각 복셀내 점군들 3D 가우시안 분포로 근사화시켜 빠른 계산 속도를 만족시킨다.

복셀은 스캔된 점군 밀도와 SLAM 계산성능 간의 tradeoff 함수이다. 이는 상황에 따라 다르다. 복셀에서 신뢰성 있는 특징들을 신속하게 얻는 것은 SLAM에서 매우 중요하다. 각 장면의 특징들을 얻은 후 이 특징들이 장면마다 일관성(consistency)을 유지하는 지 계산한다.

높은 일관성있는 특징들(평면과 모서리 부분. 색상이 밝은 점들은 일관성이 있다고 판정된 데이터임)(Stanford University, 2019)

결국 NDT SLAM 구조를 잘 설계하기 위해서는 특징 매칭 수준의 오류를 줄이는 것이 중요하다. 각 장면 연속된 점군 PCD1, PCD2의 NDT SLAM odmetry(주행괘적) 계산 및 매 키프레임(keyframe) 에 따른 지도 갱신 아키텍처는 다음과 같다.
NDT SLAM algorithm(Stanford University, 2019)

LiDAR odometry 부분은 다음과 같다. 처리 순서는 입력된 점군 PCD에서 얻은 특징들을 대략적으로 정합해 최적화한 후, 각 복셀에 포함된 점군을 좀 더 높은 정확도로 정합 계산하는 과정이 수행된다. 이를 미리 지정된 정확도까지 만족하도록 최적화해 Fine optimization 솔류션을 얻도록 반복 수행한다.
LiDAR odometry module(Stanford University, 2019)

Retain high consensus voxels는 맵핑 최적화를 위해 지역적으로 최적해를 만족하는 consensus metric를 계산하는 과정을 포함한다.
Map Update(Stanford University, 2019)

Consensus Metric은 각 점군이 후보 위치로 변환되었을 때를 테스트한다. 주어진 후보 위치에 대해 모든 복셀 컨센서스 매트릭스를 결합하고, 테스트하는 과정을 거친다.

이 알고리즘을 수행하면 다음과 같은 결과를 얻을 수 있다.

개발
NDT를 사용한 Graph SLAM을 간단히 개발해 본다. 여기에서는 다음 패키지를 사용하였다.
이 패키지는 다음 3가지 하위 패키지를 포함한다.
  • ndt_feature: GraphSLAM 패키지
  • ndt_offline_ndt_feature: ndt_feature를 사용한 rosbag 파일 처리 패키지
  • ndt_rviz_visualisation_ndt_feature: Rviz 가시화 패키지
소스 빌드를 위해 다음과 같이 실행하고 빌드한다.
git clone https://github.com/MalcolmMielle/ndt_feature_graph

마무리
이 글에서 간단하게 NDT SLAM의 개념과 개발 방법을 다루어 보았다. SLAM은 각 장면별 점군 실시간 정합을 위한 다양한 방법이 있다. 이는 응용 목적에 따라 다를 수 있다.

레퍼런스
이 내용은 다음 링크를 참고하였다.