サンプル・プログラムの実行例
目次
- サンプル・プログラムの実行例
- サンプル・プログラム
- プログラムの方針
- 気象庁防災情報XMLフォーマット
- VXSE53の構造
- 準備:pahooGeoCode クラス
- 準備:地図サービスの選択
- 準備:キャッシュ・システム
- 準備:各種定数
- 解説:気象庁防災情報XMLから最新の震源・震度に関する情報URLを取得
- 解説:地震情報の取り出し
- 解説:地震情報をマッピング情報に変換
- 解説:地図描画について
- 解説:オーバーレイ表示(Leaflet選択時のみ)
- 解説:表示とURLパラメータ
- 解説:ツイート機能
- 解説:html2canvasライブラリ
- 準備:pahooTwitterAPI クラス
- 解説:メディア付き投稿(RAWデータ)
- 解説:ツイート処理
- 活用例
- 参考サイト
サンプル・プログラム
earthquake.php | サンプル・プログラム本体。 |
pahooGeoCode.php | 住所・緯度・経度に関わるクラス pahooGeoCode。 使い方は「PHPで住所・ランドマークから最寄り駅を求める」などを参照。include_path が通ったディレクトリに配置すること。 |
pahooCache.php | キャッシュ処理に関わるクラス pahooCache。 キャッシュ処理に関わるクラスの使い方は「PHPで天気予報を求める」を参照。include_path が通ったディレクトリに配置すること。 |
pahooTwitterAPI.php | Twitter APIを利用するクラス pahooTwitterAPI。 使い方は「PHPでTwitterに投稿(ツイート)する」などを参照。include_path が通ったディレクトリに配置すること。 |
バージョン | 更新日 | 内容 |
---|---|---|
5.4.1 | 2023/09/20 | js_html2image()--Leaflet用html2image()発火プロセス見直し |
5.4 | 2022/03/19 | 気象庁防災情報XMLのhttps化に対応 |
5.3 | 2021/12/03 | 色別標高図+活断層図をオーバーレイ表示 |
5.2 | 2021/06/20 | ツイート機能を追加 |
5.14 | 2021/06/05 | 震源の深さに「約」付記,その他表示改善 |
バージョン | 更新日 | 内容 |
---|---|---|
6.3.1 | 2023/07/09 | bug-fix |
6.3.0 | 2023/07/02 | getPointsGSI()追加 |
6.2.0 | 2023/07/02 | ip2address()追加 |
6.1.0 | 2022/12/30 | ip2address()追加 |
6.0.4 | 2022/12/13 | PHP8.2対応 |
バージョン | 更新日 | 内容 |
---|---|---|
1.1.1 | 2023/02/11 | コメント追記 |
1.1 | 2021/04/08 | simplexml_load()メソッド追加 |
1.0 | 2021/04/02 | 初版 |
バージョン | 更新日 | 内容 |
---|---|---|
5.2.0 | 2023/07/17 | oembed() v2対応 |
5.1.0 | 2023/07/16 | extractMediaURL() -- file:///形式に対応 |
5.0.0 | 2023/07/02 | メソッドをTwitter API v2へ移行;v1.1は別名or廃止 |
4.9.0 | 2023/04/15 | tweet3() 追加 |
4.8.0 | 2023/01/28 | tweet2(),twitter_strcut2(),extractMediaURL()追加 |
プログラムの方針
正規表現を使って、このページから
- 発生日時分
- 震源地
- 震源の位置
- 震源の深さ
- 規模
- 最大震度
気象庁防災情報XMLフォーマット
https://www.data.jma.go.jp/developer/xml/data/yyyymmddhhmmss_番号_電文コード_連番.xml震源・震度に関する情報は、長期フィードの地震火山の中にある電文コード VXSE53 に入っている。そこで、フィードから配信日時 yyyymmddhhmmss が最も大きく、VXSE53 を含むURLを取り出せば、それが目指す情報XMLとなる。
VXSE53の構造
ここから必要な情報を取り出す。
準備:pahooGeoCode クラス
37: class pahooGeoCode {
38: var $items; //検索結果格納用
39: var $error; //エラー・フラグ
40: var $errmsg; //エラー・メッセージ
41: var $hits; //検索ヒット件数
42: var $webapi; //直前に呼び出したWebAPI URL
43:
44: //Google Cloud Platform APIキー
45: //https://cloud.google.com/maps-platform/
46: //※Google Maps APIを利用しないのなら登録不要
47: var $GOOGLE_API_KEY_1 = '**************************'; //HTTPリファラ用
48: var $GOOGLE_API_KEY_2 = '**************************'; //IP制限用
49:
50: //Yahoo! JAPAN Webサービス アプリケーションID
51: //https://e.developer.yahoo.co.jp/register
52: //※Yahoo! JAPAN Webサービスを利用しないのなら登録不要
53: var $YAHOO_APPLICATION_ID = '*****************************';
クラスについては「PHPでクラスを使ってテキストの読みやすさを調べる」を参照されたい。
地図としてGoogleマップを利用するのであれば、Google Cloud Platform APIキー が必要で、その入手方法は「Google Cloud Platform - WebAPIの登録方法」を参照されたい。
準備:地図サービスの選択
44: //地図描画サービスの選択
45: // 0:Google
46: // 2:地理院地図・OSM
47: define('MAPSERVICE', 2);
準備:キャッシュ・システム
68: //キャッシュ保持時間(分) 0:キャッシュしない
69: //気象庁へのアクセス負荷軽減のため,適切な時間を設定してください.
70: define('LIFE_CACHE_FEED', 5); //高頻度 - 地震火山フィードに対して
71: define('LIFE_CACHE_FEED_L', 120); //長期 - 地震火山フィードに対して
72: define('LIFE_CACHE_DATA', 720); //地震情報に対して
73:
74: //キャッシュ・ディレクトリ
75: //書き込み可能で,外部からアクセスされないディレクトリを指定してください.
76: //天気予報系のプログラムとは別のディレクトリを指定してください.
77: define('DIR_CACHE_FEED', './pcache1/');
78: define('DIR_CACHE_FEED_L', './pcache2/');
79: define('DIR_CACHE_DATA', './pcache3/');
キャッシュ保持時間、キャッシュ・ディレクトリともに、自サイトの環境に応じて変更してほしい。天気予報系プログラムと別のキャッシュ・ディレクトリにした方が、お互いのキャッシュ保持時間の干渉を受けなくなる。
配布ファイルは、新しい地震情報が入ってくることを考え、フィードの方を短く、地震情報の方は長くキャッシュ保持時間を設定してある。
準備:各種定数
49: //マップの表示サイズ(単位:ピクセル)
50: define('MAP_WIDTH', 600);
51: define('MAP_HEIGHT', 400);
52: //マップID
53: define('MAPID', 'map_id');
54: //初期値
55: define('DEF_ZOOM', 6); //ズーム
56: define('DEF_TYPE', 'ROADMAP'); //マップタイプ
57: define('DEF_OVERLAYS', 'GSIELEV,GSIFAULT'); //オーバーレイ(Leaflet使用時のみ)
58: define('INFO_WIDTH', (MAP_WIDTH * 0.75)); //情報ウィンドウの最大幅
59: define('INFO_OFFSET_X', +20); //情報ウィンドウのオフセット位置(X)
60: define('INFO_OFFSET_Y', -20); //情報ウィンドウのオフセット位置(Y)
61:
62: //Spinner - jQuery UI を使用するかどうか
63: define('USESPINNER', TRUE);
64:
65: define('DEF_NUMBER', 1); //求めたい震源の数(初期値)
66: define('MAX_NUMBER', 50); //求めたい震源の数(最大)
解説:気象庁防災情報XMLから最新の震源・震度に関する情報URLを取得
383: /**
384: * 気象庁防災情報XMLから震源・震度に関する情報URLを取得
385: * @param int $mode 0:最新1件のみ取得,1:すべての情報URL取得
386: * @param array $urls URL格納配列
387: * @param string $errmsg エラーメッセージ格納用
388: * @return bool TRUE:取得成功/FALSE:取得失敗
389: */
390: function jma_getLastEarthquakeURLs($mode, &$urls, &$errmsg) {
391: //URLパターン
392: $vxse53 = '/https?\:\/\/www\.data\.jma\.go\.jp\/developer\/xml\/data\/([0-9\_]+)VXSE53\_[0-9]+\.xml/ui';
393:
394: //随時フィードの解析
395: $cnt = 0;
396: $pcc = new pahooCache(LIFE_CACHE_FEED, DIR_CACHE_FEED);
397: $xml = $pcc->simplexml_load(FEED);
398: //レスポンス・チェック
399: if ($pcc->iserror() || !isset($xml->entry)) {
400: $errmsg = '気象庁防災情報XMLにアクセスできません';
401: return FALSE;
402: }
403: foreach ($xml->entry as $node) {
404: //URLを取得
405: if (preg_match($vxse53, $node->id, $arr) > 0) {
406: $urls[$cnt] = $arr[0];
407: $cnt++;
408: }
409: }
410: $pcc = NULL;
411:
412: //長期フィードの解析
413: $pcc = new pahooCache(LIFE_CACHE_FEED_L, DIR_CACHE_FEED_L);
414: $xml = $pcc->simplexml_load(FEED_L);
415: //レスポンス・チェック
416: if ($pcc->iserror() || !isset($xml->entry)) {
417: $errmsg = '気象庁防災情報XMLにアクセスできません';
418: return FALSE;
419: }
420: foreach ($xml->entry as $node) {
421: //URLを取得
422: if (preg_match($vxse53, $node->id, $arr) > 0) {
423: if (array_search($arr[0], $urls) === FALSE) {
424: $urls[$cnt] = $arr[0];
425: $cnt++;
426: }
427: }
428: }
429: $pcc = NULL;
430:
431: //エラー・チェック
432: if ($cnt == 0) {
433: $errmsg = '直近の地震情報はありません';
434: return FALSE;
435: }
436:
437: //URLを日時の新しい順にソート
438: rsort($urls);
439:
440: return TRUE;
441: }
VXSE53 を含むURLを配列 $urls に格納していき、最後に配列 $urls を大きい順にソートすることで、ひづけんの新しい順にソートしたことになる。
解説:地震情報の取り出し
443: /**
444: * 地震情報取得(気象庁防災情報XMLから)
445: * @param object $pgc pahooGeoCodeオブジェクト
446: * @param array $items 地震情報を格納する配列
447: * @param string $urls 情報XMLのURLを格納する配列
448: * @param string $errmsg エラーメッセージ格納用
449: * @param int $count 取得件数(省略時=1)
450: * @return bool TRUE:取得成功/FALSE:失敗
451: */
452: function get_earthquake($pgc, &$items, &$urls, &$errmsg, $count=1) {
453: //名前空間
454: define('JMX_EB', 'http://xml.kishou.go.jp/jmaxml1/elementBasis1/');
455: //マッチングパターン
456: $pat1 = '/([0-9]+)\-([0-9]+)\-([0-9]+)T([0-9]+)\:([0-9]+)/ui'; //年月日時分
457: $pat2 = '/([\+\-][0-9\.]+)([\+\-][0-9\.]+)([\-\+\/])([0-9]*)/ui'; //緯度・経度・深さ
458:
459: //オブジェクト生成
460: $pcc = new pahooCache(LIFE_CACHE_DATA, DIR_CACHE_DATA);
461:
462: //最新の震源・震度に関する情報URLを取得
463: $urls = array();
464: $mode = ($count == 1) ? 0 : 1;
465: jma_getLastEarthquakeURLs($mode, $urls, $errmsg);
466: if ($errmsg != '') return FALSE;
467:
468: foreach ($urls as $key=>$vxse53) {
469: //取得件数が上限だったらループ脱出
470: if ($key >= $count) break;
471:
472: //震源情報の取得
473: $xml = $pcc->simplexml_load($vxse53);
474: //レスポンス・チェック
475: if ($pcc->iserror() || !isset($xml->Body->Earthquake)) {
476: $errmsg = '気象庁防災情報XMLから最新の震源・震度情報を取得できません';
477: return FALSE;
478: }
479:
480: //地震発生日時の取得
481: if ((preg_match($pat1, (string)$xml->Body->Earthquake->OriginTime, $arr) > 0) && isset($arr[5])) {
482: $items[$key]['year'] = (int)$arr[1];
483: $items[$key]['month'] = (int)$arr[2];
484: $items[$key]['day'] = (int)$arr[3];
485: $items[$key]['hour'] = (int)$arr[4];
486: $items[$key]['minuite'] = (int)$arr[5];
487: } else {
488: $errmsg = '気象庁防災情報XMLから最新の震源・震度情報を取得できません';
489: return FALSE;
490: }
491:
492: //震源地の取得
493: if (isset($xml->Body->Earthquake->Hypocenter->Area->Name)) {
494: $items[$key]['location'] = $xml->Body->Earthquake->Hypocenter->Area->Name;
495: } else {
496: $items[$key]['location'] = '不明';
497: }
498: if ($items[$key]['location'] == '') {
499: $items[$key]['location'] = '不明';
500: }
501: $node = $xml->Body->Earthquake->Hypocenter->Area->children(JMX_EB);
502: if (preg_match($pat2, (string)$node->Coordinate, $arr)) {
503: if (isset($arr[1]) && isset($arr[2])) {
504: list($items[$key]['longitude'], $items[$key]['latitude']) = $pgc->tokyo_wgs84((float)$arr[2], (float)$arr[1]);
505: } else {
506: $items[$key]['latitude'] = $items[$key]['longitude'] = '不明';
507: }
508: if (isset($arr[3]) && ($arr[3] == '/')) {
509: $items[$key]['depth'] = '不明';
510: } else if (isset($arr[4])) {
511: $items[$key]['depth'] = (float)$arr[4];
512: } else {
513: $items[$key]['depth'] = '不明';
514: }
515: }
516:
517: //マグニチュードの取得
518: $node = $xml->Body->Earthquake->children(JMX_EB);
519: if (isset($node->Magnitude)) {
520: $items[$key]['magnitude'] = (float)$node->Magnitude;
521: } else {
522: $items[$key]['magnitude'] = '不明';
523: }
524:
525: //震度を取得
526: if (isset($xml->Body->Intensity->Observation->MaxInt)) {
527: $items[$key]['maxintensity'] = (int)$xml->Body->Intensity->Observation->MaxInt;
528: } else {
529: $items[$key]['maxintensity'] = '不明';
530: }
531: }
532:
533: //オブジェクト解法
534: $pcc = NULL;
535:
536: return TRUE;
537: }
583: /**
584: * 日本測地系を世界測地系に変換する
585: * @param float $long 経度(日本測地系)
586: * @param float $lat 緯度(日本測地系)
587: * @return float array(経度,緯度)(世界測地系)
588: */
589: function tokyo_wgs84($long, $lat) {
590: $glong = $long - $lat * 0.000046038 - $long * 0.000083043 + 0.010040;
591: $glat = $lat - $lat * 0.00010695 + $long * 0.000017464 + 0.0046017;
592: return array($glong, $glat);
593: }
解説:地震情報をマッピング情報に変換
550: /**
551: * 地震情報をマッピング情報に変換
552: * @param array $items 地震情報を格納した配列
553: * @param array $points マッピング情報を格納する配列
554: * @return int 変換したマッピング情報の件数
555: */
556: function info2points(&$items, &$points) {
557: $i = 0;
558: $j = 0;
559: foreach ($items as $i=>$item) {
560: //同じマッピング位置があるかどうか
561: $flag = FALSE;
562: for ($k = 0; $k < $j; $k++) {
563: if (($items[$i]['latitude'] == $points[$k + 1]['latitude']) && ($items[$i]['longitude'] == $points[$k + 1]['longitude'])) {
564: $flag = TRUE;
565: $points[$k + 1]['description'] .= '<br /><hr />';
566: break;
567: }
568: }
569: //新規のマッピング位置
570: if (! $flag) {
571: $k = $j;
572: $points[$k + 1]['latitude'] = $items[$i]['latitude'];
573: $points[$k + 1]['longitude'] = $items[$i]['longitude'];
574: $points[$k + 1]['title'] = '';
575: $points[$k + 1]['description'] = '';
576: $j++;
577: }
578: $items[$i]['id'] = num2alpha($k + 1);
579: $dt = makeDateTime($items[$i]);
580: $latitude = sprintf('%.1f', $items[$i]['latitude']);
581: $longitude = sprintf('%.1f', $items[$i]['longitude']);
582: if (is_numeric($items[$i]['magnitude'])) {
583: $magnitude = sprintf('%.1f', $items[$i]['magnitude']);
584: } else {
585: $magnitude = $items[$i]['magnitude'];
586: }
587: if (is_numeric($items[$i]['depth'])) {
588: if ($items[$i]['depth'] == 0) {
589: $depth = 'ごく浅い';
590: } else {
591: $depth = sprintf('約%dkm', $items[$i]['depth'] / 1000);
592: }
593: } else {
594: $depth = $items[$i]['depth'];
595: }
596: $points[$k + 1]['description'] .=<<< EOT
597: 発生日時:{$dt}<br />震源地:{$items[$i]['location']}<br />震源の位置:北緯 {$latitude}度,東経 {$longitude}度<br />震源の深さ:{$depth}<br />地震の規模:マグニチュード {$magnitude}<br />最大震度:{$items[$i]['maxintensity']}
598: EOT;
599: //打ち切り条件
600: if ($j >= 26) break;
601: }
602:
603: return $j;
604: }
そこで、ユーザー関数 info2points を使って、震源1つに対して1つの要素が対応する配列 [$items] に対し、同じ緯度・経度に対して1つの要素が対応する配列 [$points] に情報を変換してやる。
こうすることで、同一震源にマップ・アイコンを1つだけ立てて、情報ウィンドウに複数回の地震情報を羅列するようにできる。
解説:地図描画について
解説:オーバーレイ表示(Leaflet選択時のみ)
オーバーレイ地図としては、地理院地図の 色別標高図、活断層図(都市圏活断層図)、治水地形分類図 更新版(2007~2020年)の3つを用意した。
1938: //地理院地図:色別標高図(オーバーレイ)
1939: let GSIELEV = new L.tileLayer(
1940: 'https://cyberjapandata.gsi.go.jp/xyz/relief/{z}/{x}/{y}.png',
1941: {
1942: attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
1943: opacity: 0.6,
1944: minZoom: 5,
1945: maxZoom: 15,
1946: name: 'GSIELEV'
1947: });
1948: //地理院地図:活断層図(オーバーレイ)
1949: let GSIFAULT = new L.tileLayer(
1950: 'https://cyberjapandata.gsi.go.jp/xyz/afm/{z}/{x}/{y}.png',
1951: {
1952: attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
1953: opacity: 0.6,
1954: minZoom: 11,
1955: maxZoom: 16,
1956: name: 'GSIFAULT'
1957: });
1958: //地理院地図:治水地形分類図 更新版(オーバーレイ)
1959: let GSIFLOOD = new L.tileLayer(
1960: 'https://cyberjapandata.gsi.go.jp/xyz/lcmfc2/{z}/{x}/{y}.png',
1961: {
1962: attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
1963: opacity: 0.6,
1964: minZoom: 11,
1965: maxZoom: 16,
1966: name: 'GSIFLOOD'
1967: });
1968:
1969: //baseMapsオブジェクトにオーバーレイ設定
1970: let overlayMaps = {
1971: "色別標高図" : GSIELEV,
1972: "活断層図" : GSIFAULT,
1973: "治水地形分類図" : GSIFLOOD,
1974: };
1975:
1976: //layersコントロールにbaseMapsオブジェクトを設定して地図に追加
1977: L.control.layers(baseMaps, overlayMaps).addTo(map);
1978: {$type}.addTo(map);
1979: {$addoverlay}
解説:表示とURLパラメータ
877: //パラメータ
878: $number = getParam('number', FALSE, DEF_NUMBER);
879: $number = trim($number);
880: if (preg_match('/^[0-9]+$/i', $number) > 0) {
881: $errmsg = '';
882: if (($number < 1) || ($number > MAX_NUMBER)) {
883: $errmsg = sprintf('判定できる整数の範囲は,1から%dまでです', MAX_NUMBER);
884: }
885: } else {
886: $errmsg = '数値は正の整数(自然数)を指定してください';
887: }
888: $zoom = getParam('zoom', FALSE, DEF_ZOOM);
889: $zoom = ($zoom == '') ? DEF_ZOOM : $zoom;
890: $type = getParam('type', FALSE, DEF_TYPE);
891: $type = ($type == '') ? DEF_TYPE : $type;
892: $overlays = getParam('overlays', FALSE, DEF_OVERLAYS);
893: $overlays = ($overlays == '') ? DEF_OVERLAYS : $overlays;
earthquake.php?number=10のようにすることで、number で指定した値が求めたい震源の数になる。
また、
earthquake.php?number=10&zoom=8&type=GSISTD&overlays=GSIELEV,GSIFAULTのように指定すると、震源数が10、拡大率が8、ベースマップが地理院地図、オーバーレイは色別標高図と活断層図で表示する。
解説:ツイート機能
FALSE なら、pahooTwitterAPI クラスを読み込まず、ツイート・ボタンも表示しない。ツイート・ボタンの作成については、「HTMLとCSSでさまざまなアイコンを表示する」を参照してほしい。
画像化したいオブジェクト(ID名)は定数 TARGET で指定する。
解説:html2canvasライブラリ
142: <script src="https://html2canvas.hertzen.com/dist/html2canvas.js"></script>
143: <script>
144: function mytweet() {
145: $('#tweet').val('1');
146: document.myform.submit();
147: }
画像化を実行するJavaScript関数は html2canvas である。
848: <div id="{$target}" name="{$target}" style="width:{$width2}px;">
849: <p>
850: ⚠️最近の地震情報 {$dt}現在
851: </p>
852: {$html}
853: </div>
レンダリングエンジンによって違うのかもしれないが、html2canvas ライブラリによって画像化される範囲が実際よりやや小さいため、あえてマップ領域より、幅を20ピクセル大きくした範囲を画像化範囲としている。
318: /**
319: * HTMLオブジェクトの画像化
320: * @param なし
321: * @return string JavaScriptコード
322: */
323: function js_html2image() {
324: $target = TARGET;
325: $js = '';
326:
327: //Googleマップの場合
328: if (MAPSERVICE == 0) {
329: $js .=<<< EOT
330: google.maps.event.addListener(map, 'tilesloaded', function() {
331: var capture = document.querySelector('#{$target}');
332: html2canvas(capture, {useCORS: true, allowTaint:true}).then(canvas => {
333: var base64 = canvas.toDataURL('image/png'); //画像化
334: $('#base64').val(base64);
335: });
336: });
337:
338: EOT;
339:
340: //Leafletの場合(ブラウザによってはうまく動作しない)
341: } else {
342: $js .=<<< EOT
343: HTMLCanvasElement.prototype.getContext = function(origFn) {
344: return function(type, attribs) {
345: attribs = attribs || {};
346: attribs.preserveDrawingBuffer = true;
347: return origFn.call(this, type, attribs);
348: };
349: } (HTMLCanvasElement.prototype.getContext);
350:
351: //HTML画像化イベント登録
352: function html2image() {
353: var capture = document.querySelector('#{$target}');
354: html2canvas(capture, {useCORS: true, allowTaint:true}).then(canvas => {
355: var base64 = canvas.toDataURL('image/png'); //画像化
356: $('#base64').val(base64);
357: });
358: };
359:
360: //ズーム変更イベント
361: map.on('zoomend', function() {
362: html2image();
363: });
364:
365: //マップ移動イベント
366: map.on('moveend', function() {
367: html2image();
368: });
369:
370: //html2image()を発火させるために,ズームアウトして500msec後に元に戻す.
371: var zoom = map.getZoom();
372: map.setZoom(zoom - 1);
373: setTimeout(function() {
374: map.setZoom(zoom);
375: }, 500);
376:
377: EOT;
378: }
379:
380: return $js;
381: }
Leafletの場合、tilesloaded に相当するイベントがないため、ズーム変更完了イベント zoomend にフックするようにした。強制的にズームアウトし、500ミリ秒後に元ズーム値に戻す操作を行う。しかし、この方法だとブラウザによって、マップ画像が無い状態で画像化されてしまうことがあるようだ。もし対策方法をご存じの方がいたらお知らせいただきたい。
これらをJavaScriptとして生成するユーザー関数が js_html2image である。
準備:pahooTwitterAPI クラス
19: class pahooTwitterAPI {
20: var $responses; //応答データ
21: var $webapi; //直前に呼び出したWebAPI URL
22: var $error; //エラーフラグ
23: var $errmsg; //エラーメッセージ
24: var $errcode; //エラーコード
25: var $connection;
26:
27: //OAuth用パラメータ
28: // https://apps.twitter.com/
29: var $TWTR_CONSUMER_KEY = '***************'; //Cunsumer key
30: var $TWTR_CONSUMER_SECRET = '***************'; //Consumer secret
31: var $TWTR_ACCESS_KEY = '***************'; //Access Token (oauth_token)
32: var $TWTR_ACCESS_SECRET = '***************'; //Access Token Secret (oauth_token_secret)
解説:メディア付き投稿(RAWデータ)
584: /**
585: * バイナリデータを使ったメディア付きメッセージをツイートする.
586: * Tweetet API v2 を使用する.
587: * @param string $message 投稿メッセージ(UTF-8限定)
588: * @param array $items メディアデータ(バイナリデータ配列)
589: * @return bool TRUE:リクエスト成功/FALSE:失敗
590: */
591: function tweet_media_raw($message, $items) {
592: //メディアのアップロード
593: $media_ids = array();
594: $cnt = 0;
595: //Tweetet API v1.1 を使用する(v2にメディアアップロードが未実装のため)
596: $this->connection->setApiVersion('1.1');
597: foreach ($items as $data) {
598: $tmpname = $this->saveTempFile($data);
599: $media = $this->connection->upload('media/upload', ['media' => $tmpname]);
600: unlink($tmpname);
601: if (! isset($media->media_id_string)) break; //処理失敗
602: $media_ids[] = (string)$media->media_id_string;
603: $cnt++;
604: if ($cnt > 3) break; //最大4つまで
605: }
606:
607: //メディア付きツイート(Tweetet API v2 を使用する)
608: $this->connection->setApiVersion('2');
609: $option = [
610: 'text' => $message,
611: 'media' => [
612: 'media_ids' => $media_ids
613: ]
614: ];
615: $status = $this->connection->post('tweets', $option, TRUE);
616: $this->webapi = 'https://api.twitter.com/2/tweets';
617:
618: //処理に成功した.
619: if ($this->isSuccess()) {
620: $this->responses = $status->data;
621: $this->errcode = NULL;
622: $this->errmsg = '';
623: $this->error = FALSE;
624: $res = TRUE;
625: //処理に失敗した.
626: } else {
627: if ($this->isAuthError() == FALSE) {
628: $this->errmsg = $status->detail;
629: $this->error = TRUE;
630: }
631: $res = FALSE;
632: }
633: return $res;
634: }
解説:ツイート処理
293: /**
294: * ツイート処理
295: * @param string $message 投稿文
296: * @param string $res 応答メッセージ格納用
297: * @return bool TRUE:成功/FALSE:失敗または未処理
298: */
299: function mediaTweet($message, &$res) {
300: if (! TWITTER) return FALSE;
301:
302: $ret = TRUE;
303: if (isset($_POST['base64']) && ($_POST['base64'] != '')) {
304: $base64 = preg_replace('/data\:image\/png\;base64\,/ui', '', $_POST['base64']);
305: $raws = array(base64_decode($base64));
306: $ptw = new pahooTwitterAPI();
307: $ptw->tweet_media_raw($message, $raws);
308: $errmsg = $ptw->errmsg;
309: $ret = ! $ptw->error;
310: $ptw = NULL;
311: if ($ret) {
312: $res = 'ツイートしました';
313: }
314: }
315: return $ret;
316: }
ブラウザからPOSTで受け取った画像データ $_POST['base64'&$x5D; はBASE64でデコードされており、冒頭に余計なヘッダ情報が付いているので、このヘッダ情報を除き( preg_replace )、バイナリデータにデコードする( base64_decode )。
続いて pahooTwitterAPI クラスを呼び出し、tweet_media_raw メソッドを使って、メッセージと画像を一気にツイートする。
909: //ツイート機能
910: if (getParam('tweet', FALSE, 0) > 0) {
911: $dt = nowDT();
912: $message =<<< EOT
913: ⚠️最近の地震情報 {$dt}現在
914:
915: (ご参考)PHPで最近の地震情報を表示する https://www.pahoo.org/e-soul/webtech/php05/php05-17-01.shtm #地震
916:
917: EOT;
918: mediaTweet($message, $res);
活用例
参考サイト
- WebAPIの登録方法:ぱふぅ家のホームページ
- PHPで住所・ランドマークから最寄り駅を求める:ぱふぅ家のホームページ
- PHPで緯度・経度から住所を求める:ぱふぅ家のホームページ
- JavaScriptでHTML表示を画像保存する:ぱふぅ家のホームページ
- HTMLとCSSでさまざまなアイコンを表示する:ぱふぅ家のホームページ
- Twitter API - WebAPIの登録方法:ぱふぅ家のホームページ
- PHPでTwitterに画像付きメッセージ投稿:ぱふぅ家のホームページ
- C++ で直近の地震情報を取得する:ぱふぅ家のホームページ
- 日本の直近の地震情報:みんなの知識 ちょっと便利帳
そこで今回は、気象庁防災情報XMLから、直近の地震情報を取得するPHPスクリプトを作成してみることにする。
余震が多いことが分かりやすいように、求めたい震源の数を任意に指定できるように改良した。表示マップの種類を変えたり、マップを含めてツイートすることができる。
また、Windowsアプリを「C++ で直近の地震情報を取得する」で公開している。あわせてご試用いただきたい。
(2023年9月20日)js_html2image()--Leaflet用html2image()発火プロセス見直し