經過上一篇 實戰智慧插座 21 - 用 Google Maps 開關燈 ( 地圖原理篇 ) 的洗禮之後,對於地圖控制應該都能掌握了,再來就要進入「實戰」的部分,在地圖的程式裡頭加入 Webduino 的程式碼,就可以在地圖裡顯示燈泡狀態。
因為要顯示燈泡訊息,所以我們要預備兩張燈泡圖片給 Marker 使用 ( 一張開燈,一張關燈 ),這樣在地圖上才會透過 Marker ,清楚看到這個地點的燈泡是亮還是暗,然後記得圖片要用成背景透明的 png 檔案。
沿用上一篇 實戰智慧插座 21 - 用 Google Maps 玩燈泡 ( 地圖原理篇 ) 最後一個範例的程式碼,如果更換 Marker 圖片,只需要在 Marker 的設定檔多增加一個 icon 的屬性,然後把圖片網址放進去,我這邊預設放入的圖片是「關燈」的圖片。
var marker_config = [{
address: '總統府',
icon:'http://example.oxxostudio.tw/it2016/it2016-day22-off.png'
}];
然後記得更換玩,在下方的屬性也要加進去 icon 屬性
marker_config.forEach(function(e,i){
_geocoder(e.address,function(address){
//下方新增 icon 屬性
var marker = {
position:address,
map:map,
icon:e.icon
}
markers[i] = new google.maps.Marker(marker);
markers[i].setMap(map);
markers[i].addListener('click', function() {
infoWindows[i].open(map, markers[i]);
});
});
});
執行網頁應該就可以看到一個燈泡圖案出現在地圖上了。
( 程式:http://bin.webduino.io/favut/1/edit?html,output ,記得換成自己的金鑰 )
只有這樣還不夠,如果我們希望觸發某個事件之後就更換燈泡圖案,該怎麼做呢?這時候我們就會需要用到 .setIcon
這個方法,下面這段程式,表示在兩秒之後,會把地圖上的燈泡從熄滅換成打開。
setTimeout(function(){
markers[0].setIcon('http://example.oxxostudio.tw/it2016/it2016-day22-on.png');
},2000);
不過其實這樣還是會有點風險,因為我們不能確保兩秒後 marker 的初始化已經完成,所以最好的做法是在初始化 marker 的當下,就進行 setIcon
的動作,下面這段程式表示當我們打開網頁,Marker 初始化完成之後,就會自動變成開燈的圖案。( 如果你有許多的 marker 也是一樣,要確保所有 marker 都初始化完成才能繼續 )
marker_config.forEach(function(e,i){
_geocoder(e.address,function(address){
var marker = {
position:address,
map:map,
icon:e.icon
};
markers[i] = new google.maps.Marker(marker);
markers[i].setMap(map);
markers[i].addListener('click', function() {
infoWindows[i].open(map, markers[i]);
});
_setIcon(markers[i],'http://example.oxxostudio.tw/it2016/it2016-day22-on.png');
});
});
function _setIcon(e,icon){
e.setIcon(icon);
}
( 程式:http://bin.webduino.io/jekoj/1/edit?html,js,output )
接著我想要在資訊視窗 info window 裡面顯示「地點名稱」、「時間」,並放入一個按鈕,這樣如果不點選燈泡圖案開關的話,點選這個按鈕一樣可以作切換,所以我這邊也需要另外兩張按鈕的圖片,一張打開,一張關起來,這邊就不一定要 png 了,用 jpg 也可以,而且也不見得要「兩張」,如果會寫 CSS 的話,做在同一張然後用 background-position
切換就可以。
要編輯資訊視窗內容可以編輯前一個範例的資訊視窗設定檔 info_config
,內容基本上就是純 HTML,這邊我一樣用一個 h2
放地點位置,span
顯示現在時間,img
則是開關圖片,兩者都可以透過 CSS 來進行設定。
var info_config = [
'<div id="infoDiv1" class=”infoDiv>'+
' <h2>總統府</h2>'+
' <span></span><br/>'+
' <div></div>'+
'</div>'
];
新增加兩個流程,一個是資訊視窗內容的流程,另外一個則是獲取當前時間的流程,這邊透過 setInterval
每秒獲取新的時間來做改變,然而為了因應多個 Marker,所以我們在顯示的名稱上勢必要有所區隔,這裡在每個顯示 div 的後方都加個數字,這些數字是由 Marker 產生,所以可以確定每個資訊視窗都會對應到自己的 Marker。
function _content(e){
setInterval(function(){
$('#infoDiv'+e+' span').text(getTime());
},1000);
}
function getTime() {
var date = new Date();
var h = date.getHours();
var m = date.getMinutes();
var s = date.getSeconds();
if (h < 10) {
h = '0' + h;
}
if (m < 10) {
m = '0' + m;
}
if (s < 10) {
s = '0' + s;
}
var now = h + ':' + m + ':' + s;
return now;
}
記得我們可以透過 google.maps.event.addListener
監聽資訊視窗的內容是否準備好,如果準備好,就綁定對應的流程。
//設定資訊視窗內容
info_config.forEach(function(e,i){
infoWindows[i] = new google.maps.InfoWindow({
content: e
});
//設定監聽事件,在資訊視窗準備好之後,就綁定對應的流程
google.maps.event.addListener(infoWindows[i], 'domready', function() {
_content(i);
});
});
( 程式:http://bin.webduino.io/zule/1/edit?html,js,output )
為了讓資訊視窗內的按鈕可以和 Marker 連動,這邊多做了一些修改,由 s 變數判斷是否按下開關,並且決定開關的背景圖片位置 ( 看起來就會是打開和關起來 ),然後透過 _setIcon
來更改 Marker 的樣子。
function _content(e){
timer[e] = setInterval(function(){
$('#infoDiv'+e+' span').text(getTime()); //顯示當下時間
},1000);
$('#infoDiv'+e+' div').on('click',function(){
s = s * -1;
if(s<0){
$(this).css({
'background-position':'-70px 0'
});
_setIcon(markers[e],'http://example.oxxostudio.tw/it2016/it2016-day22-off.png');
}else{
$(this).css({
'background-position':'0 0'
});
_setIcon(markers[e],'http://example.oxxostudio.tw/it2016/it2016-day22-on.png');
}
});
}
因為一開始就考量到多個 Marker 的影響,所以如果你有兩個 Marker,基本上執行之後也會是正常運作的。
( 程式:http://bin.webduino.io/ninac/1/edit?html,js,output )
首先一樣在 HTML 的 head 要引入對應的 JavaScript
<script src="https://code.jquery.com/jquery-1.9.1.js"></script>
<script src="https://webduino.io/components/webduino-js/dist/webduino-all.min.js"></script>
<script src="https://blockly.webduino.io/webduino-blockly.js"></script>
JavaScript 一開始先多兩個全域變數,因為我們有些流程是自己獨立,用全域比較不會有問題,led 宣告為陣列,因為我們的 Marker 不只一個,要指定對應的 Marker 給對應的 led,boardNum 則是用來判斷板子是否都上線。
var led = [];
var boardNum = 0;
再來就是把 boardReady
在地圖開始之後在執行,避免裝置沒上線地圖也出不來的窘境,然後把剛剛內容的流程獨立成一個 ready
的流程。
//使用地址或名稱標出起始中心點位置
_geocoder('總統府',function(address){
map = new google.maps.Map(document.getElementById('map'), {
center: address,
zoom: 14
});
//設定資訊視窗內容
info_config.forEach(function(e,i){
infoWindows[i] = new google.maps.InfoWindow({
content: e
});
//設定監聽事件,在資訊視窗準備好之後,就綁定對應的流程
google.maps.event.addListener(infoWindows[i], 'domready', function() {
_content(i);
});
});
//設定開發版裝置連線
boardReady('第一塊裝置 ID', function (board) {
board.systemReset();
board.samplingInterval = 250;
led[0] = getLed(board, 10); //設定第一塊裝置的電燈接腳
boardNum = boardNum + 1;
if(boardNum===2){
ready(); //裝置都上線後執行
}
});
boardReady('第二塊裝置 ID', function (board) {
board.systemReset();
board.samplingInterval = 250;
led[1] = getLed(board, 10); //設定第二塊裝置的電燈接腳
boardNum = boardNum + 1;
if(boardNum===2){
ready(); //裝置都上線後執行
}
});
});
裝置都上線之後要執行的 ready
流程長這樣,在上線之後再定義 marker。
//開發裝置連線後要執行
function ready(){
//定義各個marker
marker_config.forEach(function(e,i){
_geocoder(e.address,function(address){
var marker = {
position:address,
map:map,
icon:e.icon
};
//把每個 marker 按照設定檔標記到地圖上
markers[i] = new google.maps.Marker(marker);
markers[i].setMap(map);
//綁定每個 marker 的點擊事件
markers[i].addListener('click', function() {
infoWindows[i].open(map, markers[i]);
});
});
});
}
最後不要忘記在點選開關的程式,要加入 led.on
和 led.off
的指令,這樣才能夠真正控制電燈。
//設定 marker 圖案
function _setIcon(e,icon){
e.setIcon(icon);
}
function _content(e){
//顯示時間 ( 每秒變動 )
setInterval(function(){
$('#infoDiv'+e+' span').text(getTime());
},1000);
$('#infoDiv'+e+' div').on('click',function(){
s = s * -1;
if(s<0){
led[e].off(); //關閉電燈
$(this).css({
'background-position':'-70px 0'
});
//更換 marker 圖案為關燈
_setIcon(markers[e],'http://example.oxxostudio.tw/it2016/it2016-day22-off.png');
}else{
led[e].on(); //點亮電燈
$(this).css({
'background-position':'0 0'
});
//更換 marker 圖案為開燈
_setIcon(markers[e],'http://example.oxxostudio.tw/it2016/it2016-day22-on.png');
}
});
}
到這邊執行網頁後,我們就可以在地圖上操控電燈,地圖上也會顯示電燈目前的狀態囉!
( 完整程式:http://bin.webduino.io/nofok/1/edit?html,js,output )
其實 Google 提供了相當多好用的服務給我們使用,如果能夠熟悉這些網路服務,相信跟生活周遭用品結合,才是真正「物聯網」的精髓吧!
最後,Google Maps 的 API 非常有用,一定要好好閱讀的 ^_^
https://developers.google.com/maps/documentation/javascript/reference?hl=zh-tw#InfoWindow