# Angular+Mqtt+PHP+MariaDB+Leaflet ## [先參照前篇完成基本leafmap](https://hackmd.io/@109213067/BJBlg0Mio/https%3A%2F%2Fhackmd.io%2FuIjQMqtqQM64Z1LnXKzOgw%3Fview) ## 流程 Angular前端新增標記 -> PHP(傳給Mqtt) -> Angular接收Mqtt訊息 -> PHP(存進MariaDB) + 在Leaflet上新增標記 -> 顯示在Angular前端 ## 程式碼 ### MariaDB * 新增table存放地圖標記(id,座標1,座標2,座標名稱) ```sql=1 CREATE TABLE `mqttMap` (`id` int(10) NOT NULL PRIMARY KEY auto_increment,`p1` varchar(50) NOT NULL,`p2` varchar(50) NOT NULL,`text` varchar(50) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` ### PHP * 新增sendMqttMap.php(傳送地圖標記給Mqtt): ```php=1 <?php header('Access-Control-Allow-Origin: *'); header('Content-Type: application/json; charset=UTF-8'); header('Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS'); header('Access-Control-Max-Age: 3600'); header('Access-Control-Allow-Headers: Content-Type,Access-Control-Allow-Headers, Authorization, X-Requested-With'); header('Cache-Control: no-cache, must-revalidate'); header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); header('Access-Control-Allow-Credentials: true'); $data = json_decode(file_get_contents("php://input")); if (!isset($data->message)) { echo json_encode(['success' => 0, 'message' => 'field is empty!']); exit; }; $message = $data->message; //$message = "321"; require('vendor/autoload.php'); use \PhpMqtt\Client\MqttClient; use \PhpMqtt\Client\ConnectionSettings; $server = '192.168.1.244'; // ip $port = 1883; $clientId = rand(5, 15); $username = 'emqx_user'; $password = null; $clean_session = false; $connectionSettings = new ConnectionSettings(); $connectionSettings ->setUsername($username) ->setPassword(null) ->setKeepAliveInterval(60) // Last Will 設定 ->setLastWillTopic('emqx/test/last-will') ->setLastWillMessage('client disconnect') ->setLastWillQualityOfService(1); $mqtt = new MqttClient($server, $port, $clientId); $mqtt->connect($connectionSettings); // 發布 (topic_name, context) $mqtt->publish('Map', $message, 0); // 放開連線 $mqtt->disconnect(); ?> ``` * 新增storeMqttMap.php(地圖標記存進資料庫): ```php=1 <?php header('Access-Control-Allow-Origin: *'); header('Content-Type: application/json; charset=UTF-8'); header('Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS'); header('Access-Control-Max-Age: 3600'); header('Access-Control-Allow-Headers: Content-Type,Access-Control-Allow-Headers, Authorization, X-Requested-With'); header('Cache-Control: no-cache, must-revalidate'); header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); header('Access-Control-Allow-Credentials: true'); $servername = "localhost"; $username = "kenny"; $password = "Kenny061256"; $dbname = "test"; // Create connection $conn = new mysqli($servername, $username, $password, $dbname); // $data = json_decode(file_get_contents("php://input")); //echo json_encode($data->message); if (!isset($data->p1)||!isset($data->p2)||!isset($data->text)) { echo json_encode(['success' => 0, 'message' => 'some fields are empty!']); exit; }; // Check connection if ($conn->connect_error) { die("Connection failed: " . $conn->connect_error); } $p1 = $data->p1; $p2 = $data->p2; $text = $data->text; $sql = "insert into `mqttMap`(p1,p2,text)values("; $sql .= "'".$p1."',"; $sql .= "'".$p2."',"; $sql .= "'".$text."')"; //$conn->execute($sql); $conn->query($sql); $conn->close(); ?> ``` ### Angular * data.service.ts新增呼叫sendMqttMap.php: ```c#=1 import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; @Injectable() export class DataService { constructor(private http: HttpClient) {} getData() { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + localStorage.getItem('token'), 'Access-Control-Alllow-Origin': '*', 'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE"' }) }; return this.http.post('http://localhost/data.php', httpOptions); } addData(job: Object){ return this.http.post('http://localhost/insert.php', job); } searchData(job: Object){ return this.http.post('http://localhost/search.php', job); } editData(job: Object){ return this.http.post('http://localhost/edit.php', job); } deleteData(job: Object){ return this.http.post('http://localhost/delete.php', job); } sendMqtt(text: Object){ return this.http.post('http://localhost/connectMqtt.php',text); } storeMqtt(text:Object){ return this.http.post('http://localhost/storeMqtt.php',text); } sendMqttMap(text: Object){ return this.http.post('http://localhost/sendMqttMap.php',text); } storeMqttMap(mark:Object){ return this.http.post('http://localhost/storeMqttMap.php',mark); } } ``` * leafmap.component.html新增輸入欄位: ```html=1 <h1>Map</h1> <input #p1 placeholder="p1"/> <input #p2 placeholder="p2"/> <input #p3 placeholder="text"/> <button (click)="sendMqtt(p1.value,p2.value,p3.value); p1.value='';p2.value='';p3.value=''">send</button> <div class="map-container"> <div class="map-frame"> <div id="map"></div> </div> </div> ``` * leafmap.component.ts新增接收、傳遞Mqtt、新增標記: ```c#=1 import { Component,OnInit} from '@angular/core'; import * as L from 'leaflet'; import { icon, Marker } from 'leaflet'; import { MqttService } from 'ngx-mqtt'; import { DataService } from '../data.service'; @Component({ selector: 'app-leafmap', templateUrl: './leafmap.component.html', styleUrls: ['./leafmap.component.css'] }) export class LeafmapComponent{ map:any; newMark:string=""; MarkList:string[]=[]; constructor(private dataService: DataService,private mqttService: MqttService) {} ngOnInit() { this.mqttService.connect(); this.mqttService.observe('Map').subscribe((message)=>{ this.newMark = message.payload.toString(); this.MarkList = this.newMark.split(","); const mark = {p1:this.MarkList[0],p2:this.MarkList[1],text:this.MarkList[2]} this.dataService.storeMqttMap(mark).subscribe(response=>{ }); this.addMarker(this.MarkList[0],this.MarkList[1],this.MarkList[2]); }); const iconRetinaUrl = 'assets/marker-icon-2x.png'; const iconUrl = 'assets/marker-icon.png'; const shadowUrl = 'assets/marker-shadow.png'; const iconDefault = icon ({ iconRetinaUrl, iconUrl, shadowUrl, iconSize: [25,41], iconAnchor: [12,41], popupAnchor: [1,-34], tooltipAnchor: [16,-28], shadowSize: [41,41] }); Marker.prototype.options.icon = iconDefault; this.map = L.map('map', { center: [23.952672279106505, 120.92716871995295], zoom: 16 }); const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, minZoom: 3, attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' }); const marker1 = L .marker([23.952672279106505, 120.92716871995295],{title:'管院'}) .addTo(this.map) .bindPopup("<h1>管院在這裡</h1>"); const marker2 = L .marker([23.941804203963905, 120.9308004137981],{title:''}) .addTo(this.map) .bindPopup("<h1>住的地方</h1>") const marker3 = L .marker([22.69037999999579, 120.32945971836486],{title:'管院'}) .addTo(this.map) .bindPopup("<h1>我家啦</h1>") const marker4 = L .marker([23.128122197367723, 113.34752774482281],{title:'管院'}) .addTo(this.map) .bindPopup("<h1>暨南大学</h1>") marker1.openPopup(); tiles.addTo(this.map); } addMarker(p1:string,p2:string,text:string) { const marker4 = L .marker([this.convertNum(p1), this.convertNum(p2)],{title:text}) .addTo(this.map) .bindPopup("<h1>"+text+"</h1>") } sendMqtt(p1:string,p2:string,text:string) { const addmark = {message:p1+","+p2+","+text}; this.dataService.sendMqttMap(addmark).subscribe(response => { alert('send mark to Mqtt!'); //window.location.reload(); }); } convertNum(text:string) { return parseFloat(text); } } ```