GIG

赴くままに技術を。

iPhone 4sからiPhone 6に変えてみた

iPhone 5が出てからもがんとしてiPhone 4s(これだけで3代目)を手放しませんでした。 個人的な信条からなのですが、画面の大きさにこだわるAppleなんて見たくなかったし、OSが更新されていくので、機能面で購入する動機がないなーとぼんやり思ってました。

替えやがった

そんなある日、2015/1月中に下取りしたらiPhone 6(16GB)の機種代がカバーされると知って、うっかり帰り道アキバヨドバシへっ (信条なんてホコリなみの軽さでふっとんだ)

4.7inchというほぼ本体を握っているような薄さ。 取り付けが若干苦労しましたが、満足してます。 外し方はそのときにでも考えるか(汗

  • フィルム: (商品名どわすれ) ガラスフィルム

ケースにも付属のフィルムが付いてますが、前々からガラスフィルムを使って見たかったので、購入してみました(赤いハイヒールで画面を踏んでいるパッケージのやつ)。

ところが... iPhone 6ようにもかかわらず、画面の方がややはみ出している--; 画面のさわり心地は良いのですが、いまいち。

結局よかったのか?

やはり通勤中の片手使いには決して使いやすいものではないなと思います。 その分、電子書籍の見易さは、画面の大きさと綺麗さで段違いに良い! そういう意味だとタブレットの2台持ちはしなくなったなー

Angularジェネレータでプロジェクトの生成時にエラー

そろそろ腰を据えて、AngularJSを修得せんと。 angular-seedをcloneして使おうかと思ったけど、最近はYeomanということで、 早速AngularJSのプロジェクトを生成しようとしたら、下記のようなエラーが発生。

> node-gyp rebuild

gyp ERR! configure error
gyp ERR! stack Error: Python executable "python" is v3.3.5, which is not supported by gyp.
gyp ERR! stack You can pass the --python switch to point to Python >= v2.5.0 & < 3.0.0.
gyp ERR! stack     at failPythonVersion (/Users/hoge/.anyenv/envs/ndenv/versions/v0.10.29/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:108:14)
gyp ERR! stack     at /Users/hoge/.anyenv/envs/ndenv/versions/v0.10.29/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:97:9
gyp ERR! stack     at ChildProcess.exithandler (child_process.js:645:7)
gyp ERR! stack     at ChildProcess.emit (events.js:98:17)
gyp ERR! stack     at maybeClose (child_process.js:755:16)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (child_process.js:822:5)
gyp ERR! System Darwin 14.0.0
gyp ERR! command "node" "/Users/hoge/.anyenv/envs/ndenv/versions/v0.10.29/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /Users/hoge/dev/angular/minimumlist/node_modules/karma/node_modules/chokidar/node_modules/fsevents
gyp ERR! node -v v0.10.29
gyp ERR! node-gyp -v v1.0

node-gypTooTallNate/node-gyp · GitHubPythonの3系に対応してないみたい。 こういうときにXXXenvは便利。プロジェクトを作りたいディレクトリにおりて、2系に切り替えればOK。

$ pyenv local 2.7.6
$ python --version
Python 2.7.6

さて、チュートリアルでもやるか。

iframeを自動的にリサイズ

iframeで他のhtmlを埋め込みたくなったので、iframeのサイズを自動調整するような そういうjquery pluginがないか探していました。 Google先生に聞いてみたところ、下記が良くヒットします(日本語では)。

bowerを通してインストールしたいので、bowerにも聞いてみたところ、 下記のような結果が得られました。

$ bower search iframe
Search results:

    jquery.iframe-transport git://github.com/cmlenz/jquery-iframe-transport.git
    iframe-resizer git://github.com/davidjbradshaw/iframe-resizer.git
    jquery-bgiframe git://github.com/brandonaaron/bgiframe.git
    responsiveiframe git://github.com/npr/responsiveiframe
    iframe-auto-height-jquery-plugin git://github.com/Sly777/Iframe-Height-Jquery-Plugin
    versal-iframe-launcher git://github.com/versal/iframe-launcher.git
    jquery.iframetracker.buzzstarter git://github.com/ggenov/iframeTracker-jquery.git
    jquery-iframeshim git://github.com/alexcheng1982/jquery-iframeshim.git
    basic-seamless-iframe 

この中でサイズの自動調整は、davidjbradshaw/iframe-resizer · GitHubSly777/Iframe-Height-Jquery-Plugin · GitHubです。 後者は先のjquery-iframe-auto-heightと機能として同じみたいですね。 メンテナンスが今もされているという観点で、前者のiframe-resizerを利用してみます。

使い方

Bowerでインストールします(なければ、GitHubからcloneして任意の場所に置いておきます)。

$ bower install -S iframe-resizer

インストールされたJavaScriptは2種類あります。

  • iframeResizer.min.js ; iFrameを埋め込む方のHTMLで読ませる
  • iframeResizer.contentWindow.min.js ; iFrame側のHTMLで読ませる

あとはiframeを埋め込んだ側で、以下のように呼び出して上げます。

$(‘iframe’).iFrameResize({});

これらを使った例を見てみましょう。 まずはiframeでchild.htmlを取り込むparent.htmlの方です。

  • parent.html
<!DOCTYPE html>
<html>
<head>
     <meta charset="utf-8">
    <title>Iframe Resize Demo</title>
</head>
<body>

     <iframe src="./child.html" width="100%" scrolling="no" frameborder="0"></iframe>
    
    <!-- JavaScript -->
    <script src="../bower_components/jquery/dist/jquery.js"></script>
    <!--script src="../bower_components/iframe-resizer/src/iframeResizer.js"></script>
    <script>
         $('iframe').iFrameResize({ });
    </script-->
</body>
</html>

それでもって、iFrameで参照される側の方。

  • child.html
<!DOCTYPE html>
<html>
<head>
     <meta charset="utf-8">
    <title>Child</title>
    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">
</head>
<body>

    <div class="jumbotron">
        <h1>Child</h1>
        <p>これはiframe側の要素。</p>
        <p><a class="btn btn-primary btn-lg" role="button">Learn more</a></p>
    </div>
    
    <!-- JavaScript -->
    <script src="../bower_components/jquery/dist/jquery.js"></script>
    <script src="../bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
    <script src="../bower_components/iframe-resizer/src/iframeResizer.contentWindow.js"></script>
</body>

どうなるの?

  • Before

何も調整していない場合、下の方が切れてしまっていますね。

f:id:hermesian:20140810221923p:plain

  • After

無事下の方まで見えるようにheightが調整されていることが分かります。

f:id:hermesian:20140810221924p:plain

サイズが合わない?

“Troubleshooting > IFrame not sizing correctly”(https://github.com/davidjbradshaw/iframe-resizer#iframe-not-sizing-correctly)に記載されてます。heightCalculationMethodで、maxまたはlowerstElementを設定すると良いみたいです。

スピリチャル・ワールド

f:id:hermesian:20140629214646j:plain

東京写真美術館で5/13から7/13まで開催されている写真展。

文字面だけみると怪しげな、何か新興宗教的な匂いを嗅ぎ取ったけど、日本人の精神世界にフォーカスした写真展でした(日曜美術館のアートシーンで分かった)。

伊勢神宮、富士山のような有名所の写真もあったり、忘れられてしまったような土着の神様もいたり。

どこか不安をかき立てられる写真もあったりしたけど、それも含め落ち着ける空間でした。

あれが良かったな。神事の格好に扮した人のポートレイト。調べたみたら土田ヒロミ氏の「続・俗神」という作品のようで。

そういう土着のものの中に身を置きたい。 そうだから一刻も早く都会を去りたいなと。

DBからモデルにデータを読み込ませる(後編)

チュートリアルを参考に、DBから読み込んで計算

前回作ったDBからデータを引っ張ってきて、計算させてみる。 ほぼチュートリアル(Transport9.java)が参考になりそう。接続させるところだけ修正して、動かしてみた。

他に参考になりそうなところでは、いったんMySQLからcsvファイルを作り、それを読み込むような手法もあった(databases:mysql [GAMS Interfaces Wiki])。 ということで下のコードはTransport9.javaほぼそのまま。

public class TransportFromDB {

    /**
     * Modelを組み立て実行する
     */
    public void runJob() {
        
        GAMSWorkspace ws = new GAMSWorkspace();
        
        // ひな形となるモデルを読み込み
        GAMSJob tp = ws.addJobFromFile("/PATH/TO/YOUR/Model.gms");
        
        GAMSDatabase gdb;
        try {
            // GAMSDatabaseをDBから読み込み、組み立て
            gdb = readDataFromDB(ws);

            // Optionを設定
            // ソルバとしてxpressを設定している
            GAMSOptions opt = ws.addOptions();
            opt.defines("gdxincname", gdb.getName());
            opt.setAllModelTypes("xpress");

            // 計算の実行
            tp.run(opt, gdb);
        
            // 計算結果の出力
            for (GAMSVariableRecord rec : tp.OutDB().getVariable("x")) {
            System.out.println("x(" + rec.getKeys()[0] + ","
                    + rec.getKeys()[1] +
                    "): level=" + rec.getLevel() +
                    " marginal=" + rec.getMarginal());
            }
        } catch (SQLException ex) {
            Logger.getLogger(TransportFromDB.class.getName()).log(Level.SEVERE, null, ex);
        }
        
    }

    /**
     * MySQLからデータを読み込む
     * @param ws GAMSWorkspace
     * @return 返却するGAMSDatabase
     * @throws SQLException 例外
     */
    private GAMSDatabase readDataFromDB(GAMSWorkspace ws) throws SQLException {
        // プロパティファイル(mysql.properties)からパラメータを読み取り
        ResourceBundle rb = ResourceBundle.getBundle("mysql");
        GAMSDatabase gdb = ws.addDatabase();
        
        // Connectionを取得
        Connection conn = DriverManager.getConnection(
                    rb.getString("DB_URL"),
                    rb.getString("DB_USER"),
                    rb.getString("DB_PASSWORD")
        );
        
        // read GAMS sets
        readSet(conn, gdb, "SELECT Plant FROM Plant", "i", 1, "canning plants");
        readSet(conn, gdb, "SELECT Market FROM Market", "j", 1, "markets");
        
        // read GAMS parameters
        readParameter(conn, gdb, "SELECT Plant, Capacity FROM Plant", "a", 1, "capacity of plant i in cases");
        readParameter(conn, gdb, "SELECT Market, Demand FROM Market", "b", 1, "demand at market j in cases");
        readParameter(conn, gdb, "SELECT Plant,Market,Distance FROM Distance", "d", 2, "distance in thousands of miles");

        return gdb;
    }
    
    /**
     * Set項目をDBから取得してGAMSDatabaseに設定する
     * @param conn DBコネクション
     * @param gdb GAMSDatabase
     * @param query SQLクエリ
     * @param setName Set名
     * @param setDimension Setの取り得る次元
     * @param setDescription Setの説明
     * @throws SQLException 例外
     */
    private void readSet(Connection conn, GAMSDatabase gdb, String query, String setName, int setDimension, String setDescription) throws SQLException {
        
        Statement st = conn.createStatement();
        
        ResultSet rs = st.executeQuery(query);
        ResultSetMetaData meta = rs.getMetaData();
        
        if (meta.getColumnCount() != setDimension) {
            System.err.println("ERROR!");
            conn.close();
            System.exit(-1);
        }
        
        GAMSSet set = gdb.addSet(setName, setDimension, setDescription);
        String[] keys = new String[setDimension];
        
        while (rs.next()) {
            for (int i=0; i<setDimension; i++) {
                keys[i] = rs.getString(i+1);
            }
            set.addRecord(keys);
        }
        st.close();   
    }

    /**
     * Parameter項目をDBから取得してGAMSDabaseに設定する
     * @param conn DBコネクション
     * @param gdb GAMSDatabase
     * @param query SQLクエリ
     * @param paramName Parameter名
     * @param paramDim Parameterの取り得る次元
     * @param paramDesciption Parameterの説明
     * @throws SQLException 例外
     */
    private void readParameter(Connection conn, GAMSDatabase gdb, String query, String paramName, int paramDim, String paramDesciption) throws SQLException {
        
        Statement st = conn.createStatement();
        
        ResultSet rs = st.executeQuery(query);
        ResultSetMetaData meta = rs.getMetaData();

        int numberOfColumns = meta.getColumnCount();
        if (numberOfColumns != (paramDim + 1)) {
            System.err.println("ERROR!");
            conn.close();
            System.exit(-1);
        }
        
        GAMSParameter parameter = gdb.addParameter(paramName, paramDim, paramDesciption);
        String[] keys = new String[paramDim];
        
        while(rs.next()) {
            for(int i=0; i<paramDim; i++) {
                keys[i] = rs.getString(i+1);
            }
            parameter.addRecord(keys).setValue(Double.valueOf(rs.getString(numberOfColumns)));
        }
        st.close();
    }
}

コードの中で読んでいるmysql.propertiesは下記で、src/main/resourcesに置いておく。

# DBのプロパティ
DB_URL=jdbc:mysql://localhost:3306/transport
DB_USER=work
DB_PASSWORD=(workに対して設定したもの)

以前書いた最適化計算をGAMSで行う - GIGでは、Netbeans上から動作を確認したが、Netbeans8に上げてからか、システムの環境変数を拾ってくれない。 どこかで調べなくては。

結局コマンドライン上から動かして見たが、次に「おや?」と思ったのが、結果が以前の同じモデルの計算と異なる(ダメじゃん...)。

*.lstファイルを比較してみたけど、展開した式は同じ...。これも調べなくては。

モデルをひっぺがす

上記の場合、当然SQL分をべた書きしている箇所などは外から読ませほうがいい。 Set, Parameterなどデータの型や次元は、今データベースにいれていない。これらはモデルのメタデータというくくりで、実際のデータと分けた方が良いかもしれない。

例えば、以下のmodel.jsonをModelを登録時に作成するなど(そういえば結果の格納は何にも考えていなかった...)

{
    "Model" : {
        "name" : "transport"
    },
    "Data" : {
        "i" : {
            "type" : "set",
            "dim" : "1"
        },
        "j" : {
            "type" : "set",
            "dim" : "1"
        },
        "a" : {
            "type" : "paramer",
            "dim" : "1"
        },
        "b" : {
            "type" : "paramer",
            "dim" : "1"
        },
        "d" : {
            "type" : "paramer",
            "dim" : "2"
        }
    },
    "Results" : ["x.l", "x.m"]
}

まだまだ考えがまとまっていない。。

DBからモデルにデータを読み込ませる(前編)

以前最適化計算をGAMSで行う - GIGの続き。 モデルと使うデータを外から読み込ませて、計算させたいということで、まずはDBに計算するデータを入れてみる。

今回は使うものはこちら。

ということで、MySQLをインストール(インストール方法は割愛)したら早速Workbenchでデータベースを作っていく。

Connectionの作成

接続できるかまずはConnectionを作成する。

f:id:hermesian:20140621133502p:plain

データベースを作成

  • “Local"コネクション画面で、上メニューのf:id:hermesian:20140621133503p:plainリポジトリに”+”(プラス)マークがついたアイコンをクリックする。

  • 以下のプロパティを設定する

    • Schema Name: transport
    • Default Collation; utf8 - default collation

データベースの利用ユーザを作成

f:id:hermesian:20140621133504p:plain

再び”Local"コネクション画面で、左ペインの[Users and Privileges]を選択して、作業ユーザを作成する

  • [Login]タブで
    • “Login Name” ; work
    • “Limit Connectivity to Hosts Matching” ; localhost
    • “Password / Confirm Password” ; (適当なパスワード)
  • [Schema Privileges]タブで
    • [Add Entry]をクリックして、[Selected schema]から対象のデータベースを選択する(今回なら"transport")
    • その後[Select “ALL”]でtransportデータベースに対して、全操作を許可
  • 最後に[OK] > [Apply]ボタンをクリック

ER図を作成

先ほど作成した”tranport”データベース上にテーブルをER図を用いて、作成していく。

f:id:hermesian:20140621133505p:plain

ホーム画面の“Models”の横3つ目のマークを選択し、[Create EER Model from Database]を選択し、画面に従ってER図作成画面に進む。

参考にしたのは、$GAMS_HOME/gams24.2_osx_x64_64_sfx/apifiles/Data/transport.xls。

今回は、CapacityとDemandの両地域の総当たり表なので、中間テーブルとして表現しようとしたが、モデルを登録する人からすると、外部キーの組み合わせを考えながら、登録するのはやってられないので、3つ別々のテーブルとした。

その後、[EER Diagrams] > [Add Diagram]で3つ下のようなテーブルを作ってみる。 オートインクリメントさせたい場合は、[AI]にチェックする(こうすると、後述のExcelシートではIDが必要ないが、IDをリセットさせる必要がある)

f:id:hermesian:20140621133508p:plain

f:id:hermesian:20140621133506p:plain

f:id:hermesian:20140621133507p:plain

ER図からテーブルを作成

それでは、作ったER図からテーブルを作成する。 [Database] > [Forward Engineer]を選択する。

f:id:hermesian:20140621133509p:plain

するとデータベースへの接続画面が表示されるので、必要に応じて設定し[Continue]ボタンをクリックする。

f:id:hermesian:20140621133510p:plain

データベースの作成時のオプションとしては、既に同じテーブルをDropするように”DROP Objects Before Each CREATE Object”にチェックし、[Continue]ボタンをクリックする。

f:id:hermesian:20140621133511p:plain

次の”Select Objects”は[Show Filter]で作りたいテーブルを絞ったりすることができるみたい。今回はそのまま作るので、[Continue]ボタンをクリックする。

それで出来たDDLは以下。

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';

DROP SCHEMA IF EXISTS `transport` ;
CREATE SCHEMA IF NOT EXISTS `transport` DEFAULT CHARACTER SET utf8 ;
USE `transport` ;

-- -----------------------------------------------------
-- Table `transport`.`MARKET`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `transport`.`MARKET` (
  `id` INT NOT NULL,
  `MARKET` VARCHAR(45) NOT NULL,
  `DEMAND` VARCHAR(45) NOT NULL,
  `DESCRIPTION` VARCHAR(45) NULL,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `transport`.`PLANT`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `transport`.`PLANT` (
  `id` INT NOT NULL,
  `PLANT` VARCHAR(45) NOT NULL,
  `CAPACITY` VARCHAR(45) NOT NULL,
  `DESCRIPTION` VARCHAR(45) NULL,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `transport`.`DISTANCE`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `transport`.`DISTANCE` (
  `PLANT` VARCHAR(45) NOT NULL,
  `MARKET` VARCHAR(45) NOT NULL,
  `DISTANCE` VARCHAR(45) NOT NULL)
ENGINE = InnoDB;


SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

最後にこのDDLが発行される。これでForward Engineer終了。 テーブルが作成されていることが確認できる。

ExcelデータをDBUnitを使って読み込み

DBUnitを使って、Excelからデータを読み込むようにしてみた。 読ませるデータは、Excel(xls形式)で各シートが一つのテーブルとして読み込まれる。

@Marketシート

f:id:hermesian:20140621140638p:plain

--2014.06.29 修正 -- "sandiego" -> "san-diego"

@Plantシート

f:id:hermesian:20140629202942p:plain

@Distanceシート

f:id:hermesian:20140621140640p:plain

注意したいのは、xlsx形式のファイルには対応しておらず、実行すると下記のようなエラーが発生する(内部で利用しているApache POIがxlsのAPIを利用しているため)。

Exception in thread "main" org.apache.poi.poifs.filesystem.OfficeXmlFileException: The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)

まずはpom.xmlについて。

<!-- 省略 -->


    <!-- DBUnit -->
    <dependency>
        <groupId>org.dbunit</groupId>
        <artifactId>dbunit</artifactId>
        <version>2.5.0</version>
    </dependency>
    <!-- MySQL Connector -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.30</version>
    </dependency>
    <!-- Apache POI -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>3.2-FINAL</version>
    </dependency>


<!-- 省略 -->

DBUnitApache POIの互換性を考えないと行けなくて、安易にApache POIの最新版を選択すると、下記のような例外が実行時に発生する。

Exception in thread "main" java.lang.NoSuchMethodError: org.apache.poi.hssf.usermodel.HSSFDateUtil.isCellDateFormatted(Lorg/apache/poi/hssf/usermodel/HSSFCell;)Z

現行の3.10-FINALは無理だったので、3.2-FINALを使ってみたら大丈夫でした。(参照: 有効なWikiNameではありません - @//メモ)。

またDBのプロパティはpropertyファイルとして用意する。

@ mysql.properties

# DBのプロパティ
DB_URL=jdbc:mysql://localhost:3306/transport
DB_USER=work
DB_PASSWORD=(適当なぱすわぁど)

最後に実行クラスは以下。

gist610709ea8da4ec1b91e1

怪談狩り (中山 市郎)

怪談狩り 市朗百物語 (幽ブックス)

怪談狩り 市朗百物語 (幽ブックス)

いい加減読もうと思って、本日外出した際に購入し、今晩読了。 怪しいことが今宵、訪れますように。

個人的に好きなのは、スナック経営者や風俗嬢、お笑い芸人が会った怪談。 じっとりと重く、あと引く話が好きなんです。。

先月は東京公演が会ったみたいで、残念ながら参加できなかった。 次回はぜひとも参加したいな。

中山市朗ダークナイト