ラベル programming の投稿を表示しています。 すべての投稿を表示
ラベル programming の投稿を表示しています。 すべての投稿を表示

2018/04/30

無線LANモジュールESP-WROOM-02と傾斜スイッチでポストへの投函を検知する電池駆動センサを作ってみた

個人でも安く容易に入手できて、かつ技適取得済みで安心の、ESP-WROOM-02(ESP8266)。恥ずかしながらつい先日教えてもらって初めてその存在を知ったので、使い方の勉強がてら動くものを作ってみました。

今回作ったのは、家のポストにモノが投函されたことを検知してIFTTT経由でLINE通知してくれる電池駆動のセンサ。仕上がりはこんな感じ。


我が家のポストは外にあって電源が取れない場所にあるので、ESP-WROOM-02の「deep sleep」機能(超低消費電力でのスリープ動作)を使って、乾電池での長期間駆動を目指します。似たようなことをやっているINTERNET Watchの記事などを参考に、以下の方針で作りました。
  • 普段はESP-WROOM-02はdeep sleepモードで寝かせとく
  • ポストにモノが投函されたことをESP-WROOM-02のリセットピンにつないだ傾斜スイッチで検知、deep sleepから起こす
  • 起きたら、自宅の無線LAN APにつないでIFTTTにWEBHOOK、通信終わったらすぐまたdeep sleepモードで寝かす

傾斜センサの部分は「型想い」と両面テープでポストのフタの内側へ、センサ本体と電池ケースは3Mの面ファスナーで着脱できるようにポストの内側へ、それぞれ固定しました。



ESP-WROOM-02上で動いているコード(Arduinoのコード)は以下に置いておきます。
nikotan/esp8266_PostMonitor.ino - GitHubGist

Arduinoのコードで動かせるってすばらしい。手軽にネットワーク機能を使ったデバイスを試作できて楽しいです。今回作ったものは今のところちゃんと動いていますが、どれくらい長期間駆動できるかはまだ不明。しばらく様子を見てみます。


※今回のハードとソフトを作るにあたっては、以下の記事などを参考にしました。

2018/04/22

Raspberry PiとWEBカメラで洗濯機の動作状況をモニタリングして、洗濯終了をLINEに通知するIoTボットを作ってみた

家の一階にある洗濯機で洗濯が終わったかどうかを、二階のリビングにいても分かるようにしたい!というわけで、タイトルのとおりです。

洗濯終了を判定するための手がかりとしては、洗濯終了時に鳴るビープ音、洗濯機の使用電力(電流)、などがありそうですが、今回は操作パネルの表示を使います。我が家の洗濯機には、操作パネルの中に洗濯終了時にだけ点灯する場所があったので、この場所の点灯状況で洗濯終了が自動判定できちゃいます。


Raspberry PiにつないだWEBカメラで操作パネルの特定位置の点灯状況を判定するだけなんですが、操作パネルをうまく写せるカメラの固定場所があまりない、というのが実は問題です。ちょっと離れた場所から監視せざるを得ないので、向きをざっくり合わせるだけでも使える、とか、カメラが多少動いたりしても使える、とかするための工夫が必要です。


基本的な方針は以下の通りですが、点灯状況を監視したい場所の近くにARマーカーを貼って、これを基準に監視場所の画像を切り出して点灯状況の判定をするようにしました。
  • ARマーカを点灯状況を監視すべき場所の近くに貼付
  • WEBカメラで撮影した画像からARマーカを検出
  • 検出したARマーカを基準に監視場所の画像を切り出して点灯判定
  • 点灯していると判定された場合にはIFTTT経由でLINEに「洗濯が終わっています」を通知
Raspberry Pi用のPythonコードは備忘録がてら以下に置きます。
nikotan/WashingMonitor

誤報対策で苦労しましたが、今は安定して動いてます。結構便利。


ARマーカーの検出のために使ったOpenCVのarucoライブラリや、arucoライブラリを使うためのRaspberry Pi上でのOpenCVのビルドや、IFTTTを使ったプッシュ通知などについては、以下記事を参考にしました。

2016/04/09

LINE BOT APIで翻訳ボットを作ってみた

巷で話題のLINE BOT API、面白そうなので、試しに翻訳ボット(送った文章を、日本語は英語に、日本語以外は日本語に、それぞれ自動翻訳して返すだけのLINEアカウント)を作ってみました。単純だけど反応良く動いてて楽しいです(*^^)v


翻訳のところはMicrosoft Translator Text APIを使って、webscript.ioで動かしてます。諸々、以下の記事などを参考にさせていただきました。

webscript.io上のコードはこちらに備忘録。

2015/09/06

OSX10.10.5にCaffe環境構築

久しぶりの週末プログラミングでDeep Learning触ってみました。
Deep Learningのフレームワークとしては、無難にCaffeを選択。
以下はmacbook airで動かすまでの手順メモです。

homebrewをインストール

$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

pyenvをインストール

$ brew install pyenv
.bash_profileeval "$(pyenv init -)"を追加

Xcode command line toolsをインストール

pythonのインストール時に"Missing the OpenSSL lib?"とかエラー出たらインストール。
$ xcode-select --install

Anaconda Pythonをインストール

最新のバージョンを調べてインストール。
Anacondaは機械学習のライブラリ。
$ pyenv install -l
$ pyenv install 3.4.3
$ pyenv install anaconda-2.3.0
$ pyenv rehash
$ pyenv local anaconda-2.3.0
$ pyenv global anaconda-2.3.0
$ pyenv version

Caffeに必要な諸々とCaffeのビルド

Caffeのサイトにある手順通りでOK。
以上で環境構築完了。
Caffeのサイトにあるチュートリアルで動作を確認できます。

2011/08/16

OpenCVで顔検出してリアルタイムモザイク

もはや定番となってますが、顔検出のサンプルコード "samples/c/facedetect.cpp" をベースに、検出された顔の領域にモザイクをかけるようにしてみた。コードはgist:1148913へ。

本格的な画像処理や画像認識の機能をお手軽に試せてしまうの楽しい。
すごいぞ、OpenCV!;-)

モザイクかけるだけで何やら怪しい雰囲気になるなw

2011/08/15

OSX Lion (MacBook Air - Mid 2011) 上の Xcode4.1 で OpenCV2.2 を使えるようにする

MacBook Air (Mid 2011) で OpenCV を使ったコードを動かすまでのメモ。
手順は以下のとおり。

[1] Xcode をインストール

AppStore で検索&クリック。あとは待つだけ。

[2] MacPorts をインストール

公式サイトから Lion 用のイメージをダウンロードしてきてインストール。
インストールが終わったら、一応アップデートしておく。
$ sudo port selfupdate

[3] OpenCV をインストール

MacPorts を使ってしまえばコマンド一発。時間はかなりかかる。
$ sudo port install opencv

[4] Xcode のプロジェクト設定

ポイントは3つ。
  • 「Valid Architectures」を"x86_64"のみにする。
    ※"i386"を含めたままでは、ビルドでエラーが出た。
  • 「Header Search Paths」に"/opt/local/include"を追加。
  • 「Other Linker Flags」に必要なライブラリのパスを追加。例えば、以下の3つ。
    • "/opt/local/lib/libopencv_core.2.2.0.dylib"
    • "/opt/local/lib/libopencv_highgui.2.2.0.dylib"
    • "/opt/local/lib/libopencv_imgproc.2.2.0.dylib"
    ※ここで設定せずにプロジェクトに直接追加してしまってもOK

というわけで、C でこんなコードを書いて動かしてみると、、、
#include <opencv/cv.h>
#include <opencv/highgui.h>

IplImage* doCvGray(IplImage* in);
IplImage* doCanny(IplImage* in, double lowThresh, double highThresh, double aperture);

int main (int argc, const char * argv[])
{
  cvNamedWindow ("Capture", CV_WINDOW_AUTOSIZE);
  cvNamedWindow ("Edge", CV_WINDOW_AUTOSIZE);

  CvCapture *capture = cvCreateCameraCapture(0);
  assert(capture != NULL);

  IplImage *frame = 0;
  IplImage *out = 0;
  int c;

  while (1) {
    frame = cvQueryFrame (capture);

    out = doCvGray(frame);
    out = doCanny(out, 10, 100, 3);
    cvShowImage("Capture", frame);
    cvShowImage("Edge", out);
    cvReleaseImage(&out);

    c = cvWaitKey (2);
    if (c == '\x1b')
      break;
  }

  cvReleaseCapture (&capture);
  cvDestroyWindow ("Capture");
  cvDestroyWindow ("Edge");

  return 0;
}

IplImage* doCvGray(IplImage* in){
  IplImage* out = cvCreateImage(cvGetSize(in), IPL_DEPTH_8U, 1);
  cvCvtColor(in, out, CV_BGR2GRAY);
  return out;
};

IplImage* doCanny(IplImage* in, double lowThresh, double highThresh, double aperture){
  IplImage* out = cvCreateImage(cvGetSize(in), IPL_DEPTH_8U, 1);
  cvCanny(in, out, lowThresh, highThresh, aperture);
  return out;
};
はい、エッジ検出できておりますw

2011/05/21

Project Euler をやり始めてみた

数日前に存在を初めて知ったProject Euler
Project Euler is a series of challenging mathematical/computer programming problems that will require more than just mathematical insights to solve. Although mathematics will help you arrive at elegant and efficient methods, the use of a computer and programming skills will be required to solve most problems.
About Project Euler - Project Euler
要は、プログラミングで解く数学パズル、みたいなもの。WEB上に用意された沢山の問題を一つづつ解いていく。ちなみに、他のユーザが書いたコードを見ることもできて、ちょっと勉強になったりもする。
全部で300以上ある問題のうち、まだ10問しかクリアしてないけど、解き方を思いついたり、うまく一発で答えを当てられたときの気持よさはなかなかのもの。少しでもプログラミングできる人は是非お試しを。

さて、プログラミングにはどんな言語を使ってもいいんだけど、僕は結局Javaを選択して少しずつ問題を消化していこうかと。ちょっとだけクラスの設計もしたので、使ってみたかったgithubにリポジトリ作ってみた(こちら)。この設計に基づくと、記念すべき1問目、Problem1を解くSolverはこんな感じになる。
package euler;

public class Solver001 implements Solver
{
  public String solve()
  {
    int sum = sum(3, 1000) + sum(5, 1000) - sum(15, 1000);
    return Integer.toString(sum);
  }

  public int sum(int base, int max)
  {
    int n = (max - 1) / base;
    return base * n * (n+1) / 2;
  }
}
ま、あまり設計とか考えずにmain関数ベタ書きでよかったかも -_-;

まずは25問クリアして"Level1"になるのが目標です。

2009/09/23

Java on OSXでWiiリモコン

今さら感は拭えないが、Wiiリモコンとarduinoを組み合わせて遊びたい。
そこでまずは、MacBook上でJavaからWiiリモコンを触れるようにした。
wii remote

準備

JavaとWiiリモコンをBluetoothでつなげてみよう - ブログ: 岡崎 - Okazaki's blog」や「WiiLi.org Wii Linux - JP:WiiremoteJ/ReadMe」などにあるように、必要なものは、Java版Bluetooth API(JSR-82)のオープンソース実装bluecoveと、WiiプロトコルのJavaライブラリWiiRemoteJだけ。2009年9月22日現在での最新バージョン、BlueCove2.1.0とWiiRemoteJ v1.6を使ってみた。

JavaVMの起動オプション

いきなりつまずいた。リモコンを探しに行くところで、
java.lang.IllegalArgumentException: PCM values restricted by JAR82 to minimum 4097
とエラーメッセージが出て止まってしまう。
Googleで検索してみると色々勉強になる(例えばここ)が、結局、「JavaVMの起動オプションに以下を加えることで問題を回避できる」そうだ。
-Dbluecove.jsr82.psm_minimum_off=true
理由は問わない。魔法だと思おう...

実装例 for 安定動作?

とりあえずWiiRemoteJの配布物に含まれているサンプルコードを動かしてみる。
リモコン探して、つないで、ボタンのイベントや加速度センサのデータなどを取得する、一通りの方法はとりあえず分かった。が、このWiiRemoteJ、安定して動作させるのがなかなか難しい、ということも分かった。特にリモコンとの接続確立時に、アプリ側で対処できない(と思われる)エラーがよく起きる。

色々やってみた結果、サンプルコードで使われているfindRemote()でなく、findRemotes(WiiDeviceDiscoveryListener listener)を使った方が、動作が安定する、っぽい。あくまで「っぽい」だけど。WiiDeviceDiscoveryListenerを使ったコードは、例えば以下のようになる。
import wiiremotej.WiiRemote;
import wiiremotej.WiiRemoteJ;
import wiiremotej.event.WRAccelerationEvent;
import wiiremotej.event.WRButtonEvent;
import wiiremotej.event.WRStatusEvent;
import wiiremotej.event.WiiDeviceDiscoveredEvent;
import wiiremotej.event.WiiDeviceDiscoveryListener;
import wiiremotej.event.WiiRemoteAdapter;

public class TestWiiRemote extends WiiRemoteAdapter implements WiiDeviceDiscoveryListener
{
public static void main(String[] args)
{
WiiRemoteJ.setConsoleLoggingAll();
TestWiiRemote test = new TestWiiRemote();
try{
WiiRemoteJ.findRemotes(test,1);
}catch(Exception e){
e.printStackTrace();
}
}

public TestWiiRemote()
{
}

public void findFinished(int numFound)
{
System.out.println(numFound+" remotes has been found!");
}

public void wiiDeviceDiscovered(WiiDeviceDiscoveredEvent evt)
{
System.out.println("remote(#"+evt.getNumber()+") was discovered!");
WiiRemote remote = (WiiRemote)evt.getWiiDevice();

try{
remote.setAccelerometerEnabled(true);
remote.setSpeakerEnabled(true);
remote.setLEDIlluminated(0,true);
}catch(Exception e){
e.printStackTrace();
if(remote!=null && remote.isConnected()){
remote.disconnect();
}
}

final WiiRemote remoteF = remote;
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable(){
public void run(){
if( remoteF!=null && remoteF.isConnected() ){
remoteF.disconnect();
}
}
}));

remote.addWiiRemoteListener(this);
}

public void disconnected()
{
System.out.println("Remote disconnected...");
System.exit(0);
}

public void statusReported(WRStatusEvent evt)
{
System.out.println("Battery level: " + (double)evt.getBatteryLevel()/2+ "%");
System.out.println("Continuous: " + evt.isContinuousEnabled());
System.out.println("Remote continuous: " + evt.getSource().isContinuousEnabled());
}

public void accelerationInputReceived(WRAccelerationEvent evt)
{
double x = evt.getXAcceleration();
double y = evt.getYAcceleration();
double z = evt.getZAcceleration();

System.out.println(x+","+y+","+z);
}

public void buttonInputReceived(WRButtonEvent evt)
{
if (evt.wasPressed(WRButtonEvent.TWO))System.out.println("2");
if (evt.wasPressed(WRButtonEvent.ONE))System.out.println("1");
if (evt.wasPressed(WRButtonEvent.B))System.out.println("B");
if (evt.wasPressed(WRButtonEvent.A))System.out.println("A");
if (evt.wasPressed(WRButtonEvent.MINUS))System.out.println("Minus");
if (evt.wasPressed(WRButtonEvent.HOME))System.out.println("Home");
if (evt.wasPressed(WRButtonEvent.LEFT))System.out.println("Left");
if (evt.wasPressed(WRButtonEvent.RIGHT))System.out.println("Right");
if (evt.wasPressed(WRButtonEvent.DOWN))System.out.println("Down");
if (evt.wasPressed(WRButtonEvent.UP))System.out.println("Up");
if (evt.wasPressed(WRButtonEvent.PLUS))System.out.println("Plus");
}

}
少なくとも自分の環境では、接続時にエラーが出ることはほとんどなくなった。ただ、アプリ終了時のdisconnect()がどうしてもうまくいかない。Ctrl+Cとかでアプリを終了させると、ShutdownHookが利いてdisconnect()が呼ばれるところまでは行くのだが、きちんと切断しきってくれない。アプリが終了した後、Bluetooth接続が残ってしまう。まぁそのままでもいいんだけど、これ、きれいに切りたいなぁ...

2009/09/22

I2C LCD用ライブラリを作ってみた

ビーコン菅原の組み込みがらみ | Arduinoで I2C LCD」を参考にして、ストロベリーリナックスのI2C低電圧キャラクタ液晶モジュール(16x2行)を動かしてみた。
I2C LCD on arduino
ピン2本だけでLCDが制御できてしまうのは楽チンだ。
ついでにアイコンの表示制御機能も追加した上で、ライブラリ化してみた。
スケッチはとにかく簡潔にしたいのである...;-)

I2C LCD(ST7032i)用ライブラリ:NsLCD-0.1.zip

ちなみに、ライブラリを置く場所は、IDEのバージョンが0017なら、「Sketchbook location」直下の「libraries」(無ければ自分で作ればよい)、IDEのバージョンが0016以前なら、IDEインストール場所の「Arduino\hardware\libraries」。

NsLCDを使ったスケッチはこんな感じになる。
#include <Wire.h>
#include <NsLCD.h>

// NsLCD library for I2C LCD(ST7032i) sample sketch
// YKO[2009/09/21]

void setup()
{
// initialize I2C LCD
Wire.begin( 0 ); // I2C master mode
NsLCD.init();
}

void loop()
{
NsLCD.clear();

NsLCD.moveCursor(0,0);
NsLCD.print("cyclic landscape");

NsLCD.moveCursor(0,1);
NsLCD.print(" by YKO");

NsLCD.displayAntennaIcon(true);
NsLCD.displayTelIcon(true);
NsLCD.displaySoundIcon(true);
NsLCD.displayOutputIcon(true);
NsLCD.displayUpDownIcon(true,true);
NsLCD.displayKeyIcon(true);
NsLCD.displayMuteIcon(true);
NsLCD.displayBatteryIcon(true,3);
NsLCD.displayNumIcon(true);

delay(1000);

NsLCD.home();
for(int i=0;i<16;i++) NsLCD.write('@');

NsLCD.displayAntennaIcon(false);
NsLCD.displayTelIcon(false);
NsLCD.displaySoundIcon(false);
NsLCD.displayOutputIcon(false);
NsLCD.displayUpDownIcon(false,false);
NsLCD.displayKeyIcon(false);
NsLCD.displayMuteIcon(false);
NsLCD.displayBatteryIcon(true,0);
NsLCD.displayNumIcon(false);

delay(1000);
}
使い方のポイントは以下の通り。
  • init()する前に、必ずI2Cライブラリの方でWire.begin(0)すること。

  • init()では500msecほど、clear()では200msecほど、それぞれ待ち時間が発生する。

  • moveCursor()でカーソルを動かして、print()で文字列、write()で文字、を表示。

  • display??Icon()で各アイコンの表示状態を制御。

とりあえず問題無く動いてるみたい。

2009/09/20

GPS受信機シールド用ライブラリを作ってみた

シルバーウィークのうちに、arduinoで少しまとめて遊びたい。
というわけで、先日入手したGPSシールド用のライブラリを書いて、動かしてみている。
GPS受信機シールド動作中
ベースはgpsrobocarブログで公開されているテスト用スケッチ
ライブラリ化してみたら、スケッチがすっきりして気持ちがいい。
せっかく作ったので、とりあえず公開しておく。

GPS受信機シールド用ライブラリ:NsGH81-0.1.zip

以下にNsGH81を使ったスケッチのサンプルを載せておく。
シリアルが一本しかないDuemilanoveなどで動くよう、NewSoftSerialを使っている。純正のSerialライブラリを使ってももちろん動くし、手持ちのMegaでも正常に動作した。また、GH-81用に作ったものだが、古野バイナリでデータをやり取りする他の機種(GH-80など)でも動くはず。確認はできないけど。
#include <NsGH81.h>
#include <NewSoftSerial.h>

// NsGH81 sample sketch
// YKO[2009/09/20]

// serial pin
#define SER_RX 2
#define SER_TX 3

// serial baud rate
#define BAUD_RATE_LOCAL 9600 // Serial
#define BAUD_RATE_GPS 9600 // NewSoftSerial

// GPS module's LED pins
#define GPS_LED1 9
#define GPS_LED2 8

// setup SoftwareSerial port
NewSoftSerial mySerial = NewSoftSerial(SER_RX,SER_TX);

// buffer
char buff[50];

void setup()
{
// initialize pin modes
pinMode(SER_RX,INPUT);
pinMode(SER_TX,OUTPUT);

// initialize serial ports
Serial.begin(BAUD_RATE_LOCAL);
mySerial.begin(BAUD_RATE_GPS);

// initialize GPS
NsGH81.init(recieveLocation,recieveTime,GPS_LED1,GPS_LED2);
NsGH81.setStartCmd(buff);
mySerial.print(buff);
}

void loop()
{
int d = mySerial.read();
if( d>=0 ){
NsGH81.update(d);
}
}

void recieveLocation(NSGH81_LOCATION g)
{
int alt_0 = int(floor(g.alt));
int alt_1 = int(round((g.alt-alt_0)*10.0));
sprintf(buff, "lat=%+03d-%02d-%02d.%03d, ", g.lat.deg, g.lat.min, g.lat.sec, g.lat.msec);
Serial.print(buff);
sprintf(buff, "lon=%+04d-%02d-%02d.%03d, ", g.lon.deg, g.lon.min, g.lon.sec, g.lon.msec);
Serial.print(buff);
sprintf(buff, "alt=%d.%01d[m], nsat=%d, status=%d", alt_0, alt_1, g.nsat, g.status );
Serial.println(buff);
}

void recieveTime(NSGH81_TIME g)
{
sprintf(buff, "(20%02d/%02d/%02d %02d:%02d:%02d)", g.year, g.month, g.day, g.hour, g.min, g.sec);
Serial.println(buff);
Serial.println("-----");
}
ライブラリの使い方を整理すると以下の通り。
  • setup()で...

    • init()でコールバック関数とシールド上のLEDにつないだピン番号を設定

    • setStartCmd()で測位動作設定用のコマンドを作成し、シリアルを使ってモジュールへ書き込み

  • loop()で...

    • シリアルからデータを1バイト読んで、読んだ1バイトのデータを引数にしてupdate()をコール、を延々繰り返し。ライブラリ内部で読み込んだデータはパースされ、パース結果(測位結果と時刻)が得られる度に、結果を引数にしてコールバック関数がコールされる。つまり、コールバック関数の中に所望の処理を書いておけばよい。

さて、GPSモジュールは無事つながった。何しよう...笑

2007/10/31

距離算出APIデモの動作を修正

LatLng2Distance:::緯度経度から距離算出API」のGoogle Mapsを使ったデモ、久しぶりに(数ヶ月ぶりに?)見てみたら動作がおかしい。

どうやらGoogle Maps APIの仕様が少し変わっていたようです。仕様変更の時期が良く分かりませんが、動作がおかしいまま長く放置してしまっていたかも・・・

今回の問題発生の原因になっていた仕様変更は以下の二点。
  • 【存在しないoverlayをremoveOverlayするとエラーが出るようになった】
    前はエラー出なかったんですよね・・・本来、エラー出すのが正しいんでしょうけど。
  • 【GPolyline上でのクリックイベントの扱いが変わった】
    具体的にどう変わったか、ちゃんと確かめられてませんが、とにかく、何か変わったみたい。
とりあえずこれらの変更に対応してデモを修正しました。問題なく動いていると思います。

2007/03/25

img要素のtitle属性を画像に重畳表示するjavascriptライブラリ

※以下、当初「alt属性を重畳表示」していたものを「title属性を重畳表示」に修正して再アップしました。[2007/3/26]

表題の内容を実現するためのjavascriptライブラリを作ってみました。
似たようなことをうまく解決できる方法を見つけられなかったので、久しぶりに週末プログラミング。なかなか楽しかったです。

作ってみたのは、htmlページの中から簡易CSSセレクタでimg要素を選択して、選択されたimg要素について、title属性を画像に無理やり重畳表示する機能。動作例は、こんな感じです。

まだまだ問題のある可能性が高いですが、折角なので公開しておきます。あらかじめ断っておきますが、ご利用はあくまで自己責任で。
imgtitle_v1.zip

以下、使い方を簡単にご紹介。
興味のある方は、是非ご試用の上、ご意見・ご要望などお寄せください。

[1]
上のZIPファイルを解凍して得られる「imgtitle.js」「imgtitle.css」を適当な場所にアップ。

[2]
htmlページのheader要素内に以下の二行を追加。
<link rel="stylesheet" type="text/css" href="imgtitle.css" />
<script type="text/javascript" src="imgtitle.js"></script>


[3]
onLoad時にimgtitle関数を呼び出すように設定。
例えば、header要素内に以下を追加する。
<script type="text/javascript">
<!--
window.onload=function(){
imgtitle(セレクタ,オプション文字列);
imgtitle(セレクタ,オプション文字列);
}
// -->
</script>
ここで、セレクタとは、img要素を抽出するための簡易CSSセレクタで、現時点では以下の5パターンが利用可能。
「img」「img#id」「tag#id img」「img.class」「tag.class img」
文字列で指定してください。
セレクタで指定された全imgについて、title属性を画像に重畳表示します。
また、おまけとして、title属性とは別の文字列をオプション文字列として指定して、画像へ重畳表示できます。オプション文字列の方は省略可能です。

[4]
「imgtitle.css」を適宜修正することで、重畳表示の仕方を制御できます。
簡単な構造なので、ちょっといじれば理解してもらえると思います。

[注]
まだバグや問題が残ってると思いますが、
それ以前に以下の二つの制限事項には注意してください。
  • 処理対象のimg要素は、スクリプト実行時にdivで囲ってブロック要素化します。
  • 元のimg要素に設定された属性(縦横サイズなど)やスタイルは、alt属性以外すべて無視します(alt属性だけは維持します)。
というわけで、既存のページに適用すると、レイアウトが多少(場合によっては非常に)乱れるので、ご注意を。特に既存のページへ適用する場合には、「imgtitle.css」の修正だけでは所望のレイアウトを実現できない場合があるかと思います。

2006/12/16

緯度経度から『標高』を算出するAPIを公開

「緯度経度から○○算出API」シリーズ第二弾(?)ということで、次は『標高』を算出するAPIを公開してみました。

LatLng2Height:::緯度経度から標高算出API

標高の算出には、NASAが無料で公開しているSRTMを利用しています。スペースシャトル「エンデバー」のミッション(2000年2月)で得られたデータから作られたものだそうです。スペースシャトルって打ち上げのシーンが注目されがちですが、宇宙でも色々とやってるんですね・・・

このSRTM、そのデータ粒度にいくつかバリエーションがあるんですが、本APIでは、その中の「SRTM3」というのを使用。これには、陸地部分の緯度経度3秒毎の標高データが格納されていて、地球全域をカバーしています。3秒ということは、だいたい90メートル間隔くらい。残念ながら今回は、レンタルサーバのストレージ容量制限のため、対象領域を日本だけとしました。
(そのうち、日本以外はもう少し粒度の粗いデータを使うことで、対象領域を地球全域にしようとも思ってます。)

こんなリッチなデータが自由に使えるのは非常に喜ばしいんですが、とても重大な問題が・・・
それは、このデータで得られる標高の値が、日本で正式な測量成果として用いられている標高の値と、だいぶずれてしまっているということ。これには、センサ自体の誤差などの原因もあるとは思いますが、もっとも大きな原因(と思われるの)は、参照しているジオイドモデル(標高を0とする面)が違う(SRTMではWGS84/EGM96、日本の標高ではJGEOID2000)ということ。どうやら、最大で50mほどずれてしまうようです。この辺はどうぞご容赦を・・・

そもそも、日本国内の標高を得るために、わざわざNASAのデータを使う破目に陥ってるのは何故か?
それは、国土地理院のデータジオイドも含めて)を使うためには、面倒な手続きが必要だったり、制限が色々とあるためです。もっと自由に使えるようにしてくれればいいのになぁ・・・ぜひ、規制緩和して欲しいです。

距離算出APIを若干修正

LatLng2Distance:::緯度経度から距離算出API」を若干修正。
修正箇所は以下の二点。
  • 返される距離は小数点以下二桁まで
  • JSONPの出力内容をXMLのものと整合
以上、備忘録まで。

2006/12/12

距離算出の精度改善

距離算出APIですが、距離の算出精度を若干改善しました。
ここで説明されている「Lambert-Andoyer Method」を利用しています。ま、ほとんど大差ないです。
ついでにデザインなど若干修正。カウンタは自動更新をやめました。

2006/12/11

『緯度経度から距離算出API』利用回数カウンタを設置

件のページ右上に、APIの利用回数を表示するようにしてみました。
これにもJSONPを使ってます。こんな感じ
とりあえず、5秒おきにカウンタを更新するように設定。誰かが使ってると数字が増えていくのが見えたり、、、するといいな(笑

2006/12/10

『緯度経度から距離算出API』を公開

JSONPのお勉強のつもりで、簡単なAPIを作って公開してみました。
LatLng2Distance:::緯度経度から距離算出
機能が単純すぎてわざわざAPI化する必要はないものなんですが、作ってて結構楽しかったので自分的には満足です。
そもそも、恥ずかしながら、JSONPってものに触れるの初体験。「Dynamic Script Tag」の仕組み、とても新鮮でした。面白いですね。AJAXとうまく使い分けられれば、「できる子」っぽい、かな?
# GoogleMapsAPIにもだいぶ慣れてきました。
# そろそろちゃんとしたもの作ってみたい。んだけど、時間作るの難しい・・・

2006/12/09

道の駅分布図を作ってみた

郵便番号を緯度経度に変換してプロットすると日本地図みたいになる、っていう以下のネタを見かけて、似たようなことをやってみました。

作ってみたのは、「道の駅」の分布図。バイクに乗ってた関係で、道の駅に絡めて何かやってみたいなぁ、と思ってたので、いいきっかけになりました。

現在、道の駅は全国に845箇所あるそうで、まずはその元データを
全国 道の駅一覧 -「道の駅」
からダウンロード。
このデータには省略された住所と電話番号しか入ってないので、電話番号から住所を検索できるサイトで、ちゃんとした住所を取得。
無料電話案内 104.com
住所から緯度経度への変換には、以下の二つのサービスを使ってみました。
Geocoding API - 住所から緯度経度を検索
Geocoding Tools & Utilities - 位置参照技術を用いたツールとユーティリティ
以上のもろもろをやってくれるスクリプトをPHPで書いて、出来上がった845件分の緯度経度を、単純に座標上にマッピングしてみると・・・
道の駅分布図
ちゃんと日本地図っぽいものができあがりました。なかなか面白い。
せっかく作ったデータ、これだけでほっとくのももったいないので、GoogleMapsにのっけたりしてもう少し遊んでみようと思います。

2006/07/28

uribouMAPSをちょいと改良

uribouMAPSを少し改良。
AJAXにつきものの(?)、通信中であることを明示する仕組みと、InfoWindowから、zoom in、zoom outできる仕組みを追加してみました。

2006/07/27

AJAX体験

ベタですが、Google Maps APIGoogle AJAX Search APIprototype.jsを使って、地図上にトラックバックを打てるサイトを作ってみました。

[[uribouMAPS]]

いわゆるAJAX的なプログラミングは初めてだったんですが、なかなか面白い。サーバ側のXML入出力やトラックバック受付の部分はPHP、ブラウザ側はPHPとJavascriptで実装してます。今回のようにAPIやライブラリを使うことで、そこそこ面白げなものが結構簡単に実現してしまうんですね。細かいところで、GoogleMapsのイベント処理周りなんかは、ドキュメントに出てないところで試行錯誤する破目に陥りましたが、、、
それにしても、APIなどは使ってますが、0からアプリ書き上げたの久しぶりだったので、ちょっと楽しかったです。とりあえず「beta」版ということにしておいて、まだまだ色々試してみる予定。まずは、「ルート」の登録、「ルート」へのトラックバック、の実装を考えてます。