galapagosit’s blog

本家 > http://galapagosit.com/

cocos2d-x(v3.10)でカスタムフォントを使う

Xcode: 7.2.1
cocos2d-x: v3.10

言語はJSを使用しています。
すぐ忘れるので覚書。

ttfファイルをxcodeのResourcesフォルダにドロップ

f:id:galapagosit:20160319120901p:plain

f:id:galapagosit:20160319121118p:plain
上記のようにチェックしてFinish

info.plistを設定

info.plistに
Fonts provided by application
を追加。 追加したフォントを指定。

f:id:galapagosit:20160319121218p:plain

cleanしてビルド

その他

cocos studioで指定したフォント設定はこんなことしなくても反映されたのだが、
プログラムからフォントを指定した場合はやらないといけないみたい。

国土数値情報 行政区域データをGeoDjangoに取り込み

国土数値情報 行政区域データの詳細

上記から行政区画データをダウンロードし、GeoDjangoから扱えるようにするための手順。
postgisはインストール済みとする。

地図情報のダウンロード

"全国"をクリックし最新の"N03-20150101_GML.zip"をダウンロード。

sqlファイル作成

データの格納と表示 — Let's Postgres

上記を参考にsqlファイルを作成

shp2pgsql -s 4612 -D -i -I -W cp932 ~/Desktop/N03-20150101_GML/N03-15_150101.shp gisdata > ~/Desktop/gisdata.sql

参考サイトではJPGISからシェープファイルに変換する手順が必要とあるが、ダウンロードしたファイルに含まれていたのでそれを使用した。

既存のデータベースにデータを投入

psql postgres -f ~/Desktop/gisdata.sql

modelを定義

from django.contrib.gis.db import models


class GisData(models.Model):
    gid = models.IntegerField(primary_key=True)
    n03_001 = models.CharField(max_length=10)
    n03_002 = models.CharField(max_length=20)
    n03_003 = models.CharField(max_length=20)
    n03_004 = models.CharField(max_length=20)
    n03_007 = models.CharField(max_length=5)

    geom = models.MultiPolygonField(spatial_index=True, srid=4612)

    objects = models.GeoManager()

    class Meta:
        db_table = 'gisdata'

    def __str__ (self):
        params = {
            'gid': self.gid,
            'n03_001': self.n03_001,
            'n03_002': self.n03_002,
            'n03_003': self.n03_003,
            'n03_004': self.n03_004,
            'n03_007': self.n03_007,
        }
        return '<GisData({gid}) n03_001:{n03_001} n03_002:{n03_002} n03_003:{n03_003} n03_004:{n03_004} n03_007:{n03_007}>'.format(**params)

使用方法

>>> from django.contrib.gis.geos import Point
>>> from gisdata.models import GisData
>>> point = Point(139.699578, 35.659531) # 渋谷駅周辺の座標
>>> r = GisData.objects.filter(geom__contains=point)
>>> print(r[0])
<GisData(32097) n03_001:東京都 n03_002:None n03_003:渋谷区 n03_004:None n03_007:13113>

上記の感じで取得する。
あまりドキュメントを読まずにやったので、何か間違いがあるかも。

Cocos2d-JsでCocoaPodsを使用する

CocoaPodsのインストールは済ませたものとする

podの初期化

frameworks/runtime-src/proj.ios_mac/
pod init

Podfileの編集

今回はParseをインストールします。

vi Podfile

# Uncomment this line to define a global platform for your project
# platform :ios, '6.0'

target 'Cocos2dJsBase iOS' do
    pod 'Parse'
end

target 'Cocos2dJsBase Mac' do

end

pod installを実行

$ pod install
Analyzing dependencies

CocoaPods 0.38.0.beta.2 is available.
To update use: `gem install cocoapods --pre`
[!] This is a test version we'd love you to try.

For more information see http://blog.cocoapods.org
and the CHANGELOG for this version http://git.io/BaH8pQ.

Downloading dependencies
Installing Bolts (1.2.0)
Installing Parse (1.7.5)
Generating Pods project
Integrating client project

[!] Please close any current Xcode sessions and use `Cocos2dJsBase.xcworkspace` for this project from now on.

[!] The platform of the target `Cocos2dJsBase iOS` (iOS 5.0) may not be compatible with `Parse (1.7.5)` which has a minimum requirement of iOS 6.0.

[!] The `Cocos2dJsBase iOS [Debug]` target overrides the `HEADER_SEARCH_PATHS` build setting defined in `Pods/Target Support Files/Pods-Cocos2dJsBase iOS/Pods-Cocos2dJsBase iOS.debug.xcconfig'. This can lead to problems with the CocoaPods installation
    - Use the `$(inherited)` flag, or
    - Remove the build settings from the target.

[!] The `Cocos2dJsBase iOS [Debug]` target overrides the `LIBRARY_SEARCH_PATHS` build setting defined in `Pods/Target Support Files/Pods-Cocos2dJsBase iOS/Pods-Cocos2dJsBase iOS.debug.xcconfig'. This can lead to problems with the CocoaPods installation
    - Use the `$(inherited)` flag, or
    - Remove the build settings from the target.

[!] The `Cocos2dJsBase iOS [Debug]` target overrides the `OTHER_LDFLAGS` build setting defined in `Pods/Target Support Files/Pods-Cocos2dJsBase iOS/Pods-Cocos2dJsBase iOS.debug.xcconfig'. This can lead to problems with the CocoaPods installation
    - Use the `$(inherited)` flag, or
    - Remove the build settings from the target.

[!] The `Cocos2dJsBase iOS [Release]` target overrides the `HEADER_SEARCH_PATHS` build setting defined in `Pods/Target Support Files/Pods-Cocos2dJsBase iOS/Pods-Cocos2dJsBase iOS.release.xcconfig'. This can lead to problems with the CocoaPods installation
    - Use the `$(inherited)` flag, or
    - Remove the build settings from the target.

[!] The `Cocos2dJsBase iOS [Release]` target overrides the `LIBRARY_SEARCH_PATHS` build setting defined in `Pods/Target Support Files/Pods-Cocos2dJsBase iOS/Pods-Cocos2dJsBase iOS.release.xcconfig'. This can lead to problems with the CocoaPods installation
    - Use the `$(inherited)` flag, or
    - Remove the build settings from the target.

[!] The `Cocos2dJsBase iOS [Release]` target overrides the `OTHER_LDFLAGS` build setting defined in `Pods/Target Support Files/Pods-Cocos2dJsBase iOS/Pods-Cocos2dJsBase iOS.release.xcconfig'. This can lead to problems with the CocoaPods installation
    - Use the `$(inherited)` flag, or
    - Remove the build settings from the target.

エラーが出ているが次の項で修正する。

xcode起動

カレントディレクトリに Cocos2dJsBase.xcworkspace が作成されているので、こちらを使用してxcodeを起動

Build Settingsで

Header Search Paths
Library Search Paths
Other Linker Flags
に$(inherited)を追加

再度pod install

補足

Cocos2dJsBase.xcworkspaceに GameController.framework を手動で追加しないと動かないことがある?

fluxibleのサンプル(チャット)を読む

やること

Fluxible | A Pluggable Container for Isomorphic Flux Applications

というものがある。
今流行りのreact.jsを乗せたfluxアーキテクチャの実装の中では、割とオススメされているっぽいものだ。

これの習得をせまられる状況になったのだが、はっきり言ってチンプンカンプンである。そもそもnode自体の理解もおぼつかないのに、こんなこと人間にはできません。

とはいえ、下記のサンプル(chatの方)を読んで気合いで理解を進めた話。 github.com (commit:967b623e9411df46383e0ebe0adedec48e57d511)

app.js

flux-examples/app.js at master · yahoo/flux-examples · GitHub


var app = new Fluxible({
    component: require('./components/ChatApp.jsx')
});

まずはここから。
これがアプリケーションそのもので、これに対していろいろ設定を加えて行ってるっぽい。
引数のcomponentは
「Stores your top level React component for access using getComponent()
ってソースには書いてあるんだけど、だとしたら"components/Html.jsx"がトップレベルなんじゃないの? 分からないので飛ばします。


app.registerStore(require('./stores/RouteStore'));
app.registerStore(require('./stores/MessageStore'));
app.registerStore(require('./stores/ThreadStore'));
app.registerStore(require('./stores/UnreadThreadStore'));

ここでは、データを溜め込むためのStoreをアプリケーション登録している。

server.js

flux-examples/server.js at master · yahoo/flux-examples · GitHub

サーバサイドでブラウザからのアクセスを待ち受ける為、最初に動くコードである。


require('babel/register')

このアプリに限ったことではないが、コードをES6で記述できるようにするものらしい。


var server = express();

pythonにおけるflask、rubyにおけるsinatra的なやつである。


// Get access to the fetchr plugin instance
var fetchrPlugin = app.getPlugin('FetchrPlugin');
// Register our messages REST service
fetchrPlugin.registerService(require('./services/message'));
// Set up the fetchr middleware
server.use(fetchrPlugin.getXhrPath(), fetchrPlugin.getMiddleware());

これはブラウザからサーバのコードを叩きたい時に、簡単に叩く為の入り口を設定してあげる機能と理解した。
以下のように書くと、サーバ側の処理を以下の様に呼び出せるようになる。

context.service.somefunc('message', message, {} ...

    var context = app.createContext({
        req: req, // The fetchr plugin depends on this
        xhrContext: {
            _csrf: req.csrfToken() // Make sure all XHR requests have the CSRF token
        }
    })

これがリクエストに紐づくコンテキスト。プラグインなんかを使うとこいつに対して便利なメソッドが生えるとか、生えないとか。


    debug('Executing showChat action');
    context.executeAction(navigateAction, { url: req.url, type: 'pageload' }, function (err) {

ここでアクションを実行し、結果的に必要な情報をStoreに登録している。


        debug('Exposing context state');
        var exposed = 'window.App=' + serialize(app.dehydrate(context)) + ';';

        debug('Rendering Application component into html');
        var html = React.renderToStaticMarkup(HtmlComponent({
            state: exposed,
            markup: React.renderToString(context.createElement())
        }));

        debug('Sending markup');
        res.send(html);
  • exposedの中身をconsole.log gist.github.com contextに紐づくデータがjson形式でシリアライズされている様だ。
    スレッド一覧やチャットの発言のデータも含まれる。

  • htmlの中身をconsole.log gist.github.com exposedの内容を含むhtml全体が入っている。
    data-reactid=".1dbtrevj3ls.0.1" みたいなのが、タグの属性に入ってるのがミソなんだろう、きっと。

client.js

flux-examples/client.js at master · yahoo/flux-examples · GitHub

こちらは、上記のサーバにアクセスしたのち、ブラウザ側で最初に実行されるコード。


bootstrapDebug('rehydrating app');
app.rehydrate(dehydratedState, function (err, context) {

dehydratedStateはサーバ側で格納されていたexposedの事である、app.rehydrateする事でcontextに復元を行っている。


    var mountNode = document.getElementById('app');

    bootstrapDebug('React Rendering');
    React.render(context.createElement(), mountNode, function () {
        bootstrapDebug('React Rendered');
    });

mountNodeっていうのが、app.jsで指定していた'./components/ChatApp.jsx'の中身になっているっぽい。
context.createElement()
というのは、「React.createElement」メソッドの結果が返るみたい。

nginxで自己認証証明書(おれおれ証明書)を設定

証明書の作成

秘密鍵を作成

# openssl genrsa -des3 -out server.key 2048
Generating RSA private key, 2048 bit long modulus
.........................+++
.......................................+++
e is 65537 (0x10001)
Enter pass phrase for server.key:
Verifying - Enter pass phrase for server.key:

パスフレーズは後で外すので適当に

証明書署名要求を作成

# openssl req -new -key server.key -out server.csr
Enter pass phrase for server.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Shinjyuku
Organization Name (eg, company) [Internet Widgits Pty Ltd]:xxx .inc
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:xxx.com
Email Address []:xxx@gmail.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Common Nameとhost名を一致させる事、curlでテストする際に以下のエラーが出てしまいます。
ただ、他の環境だと平気かもしれないです。

curl: (51) SSL: certificate subject name 'xxx.com' does not match target host name 'yyy.com'

秘密鍵パスフレーズを外す

openssl rsa -in server.key -out server.key

証明書を作成

openssl x509 -req -days 36500 -in server.csr -signkey server.key -out server.crt

nginx設定

serverに以下を追加

        listen 443 ssl;
        ssl_certificate /path/to/server.crt;
        ssl_certificate_key /path/to/server.key;

curlで確認

適当なマシンにserver.crtを転送し

curl --cacert ./server.crt https://xxx.com

iosでpush通知

参考にしたサイト

公式

https://developer.apple.com/jp/documentation/RemoteNotificationsPG.pdf

アプリ側

Push通知をちゃんとね

Objective-C - iOS8でのPush通知対応 - Qiita

サーバ側

djacobs/PyAPNs · GitHub

django - Sending iOS push notifications with python and PyAPNS - Stack Overflow

mysql(MYISAM)でGeoDjango

djangoで位置情報を使うためにはGeoDjangoという仕組みを使うと良いらしい。

GeoDjango | Django documentation | Django

この機能をフルで使う為にはデータベースをPostgreSQLにしなければならないのだが、簡単な機能であればmysqlでも大丈夫。

やり方

settings.py

INSTALLED_APPSに'django.contrib.gis'を追加する

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.gis',
)

DATABASES設定のENGINEを'django.contrib.gis.db.backends.mysql'に変更。
※'django.db.backends.mysql'では無いので注意

OPTIONSの部分でストレージエンジンをMYISAMに変更。
mysql5.6の時点ではMYISAMでないとGEOMETRY型(BLOB型)の列にスペシャルインデックスを貼る事ができない。
innodbでは貼れない

DATABASES = {
    'default': {
        'ENGINE': 'django.contrib.gis.db.backends.mysql',
        'NAME': 'dbname',
        'USER': 'root',
        'PASSWORD': '',
        'HOST': '127.0.0.1',
        'PORT': '',
        'OPTIONS': {
           "init_command": "SET storage_engine=MYISAM",
        }
    }
}

models.py

モデルの作成例。
ポイントは継承先とマネージャをデフォルトのものから変更する事。

from django.contrib.gis.db import models

# Create your models here.


class GeoDataLog(models.Model):
    latlng = models.GeometryField(spatial_index=True)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    objects = models.GeoManager()

使い方

insert

point = Point(float(lat), float(lng))
GeoDataLog.objects.create(latlng=point)

select

普通にマネージャインスタンスからクエリセットを作って、とか出来なくはないが、 位置情報を使った複雑なクエリを発行したいなら、生のsqlで取るのが良さそう。

下記は指定の位置から緯度経度+-{latlng_range}内の短形領域内で指定の位置から近いデータを抽出する為のサンプル
SQLインジェクションには十分注意して下さい!

        sql = """
SELECT
    id AS id,
    GLength(
        GeomFromText(
            CONCAT(
                'LineString({lat} {lng},',
                X(latlng),
                ' ',
                Y(latlng),
                ')'
            )
        )
    ) AS length_km,
    X(latlng) AS lat,
    Y(latlng) AS lng
FROM geodata_geodatalog
WHERE
    MBRContains(
        GeomFromText(
            Concat(
                'LineString(',
                {lat} + {latlng_range},
                ' ',
                {lng} + {latlng_range},
                ',',
                {lat} - {latlng_range},
                ' ',
                {lng} - {latlng_range},
                ')'
            )
        ),
        latlng
    )
HAVING
    length_km < {length_limit_km}
ORDER BY length_km
""".format(
            lat=lat,
            lng=lng,
            latlng_range=latlng_range,
            length_limit_km=length_limit_km
        )

        cursor = connection.cursor()
        cursor.execute(sql)
        cursor.fetchall()