Cordova/AndroidアプリをカスタムURLスキームから立ち上がるようにする
使用するプラグイン
cordova-plugin-customurlscheme を使用します。https://www.npmjs.com/package/cordova-plugin-customurlscheme あたりを参照して下さい。
# 実際に作ってみる
プロジェクトを作る
まずプロジェクトを作ります。customurlschemeをプロジェクトにインストールする際に、スキームを決めておきます。
cordova create (フォルダ名) (ネームスペース) (タイトル) cd (フォルダ名) cordova platform add android cordova plugin add cordova-plugin-customurlscheme --variable URL_SCHEME=(スキーム名)
関数を入れる
function handleOpenURL(url) { setTimeout( function() { alert("received url: " + url); }, 0 ); }
これで、カスタムURLスキームによってアプリが起動した場合には、URLがアラートとして表示されます。
確かめてみる
内部ストレージのHTMLファイルを読み込む
まず、次のようなアンカーを入れたHTMLファイルを作ります。
<a href="(スキーム名):">CLICK</a>
"CLICK" となっている箇所をクリックすると、(スキーム名)に関連付けられているアプリが起動します。
が、デスクトップでは起動しないので、Androidに転送します。
デスクトップのエクスプローラで、「内部ストレージ」にHTMLファイルを転送します。
Androidのブラウザから開いてみます。ローカルパスが (内部ストレージ)/abc.html
なら、ブラウザでURLを次のように指定します。
file:///sdcard/abc.html
となります。
Cordova/Android でファイル操作
cordova-plugin-file
https://github.com/apache/cordova-plugin-file
プロジェクトを作る
cordova create (ディレクトリ名) (ネームスペース) (タイトル) cd (ディレクトリ名) cordova platform add android cordova plugin add cordova-plugin-file
クラス作成例
たとえば、次のようなクラスを作りました。
function TextFileSystem(dirPath) { this.dirPath = dirPath }; TextFileSystem.prototype.readFile = function readFile(fileName, success, fail) { window.resolveLocalFileSystemURL( this.dirPath+fileName, function(fileEntry) { fileEntry.file( function(file) { var reader = new FileReader(); reader.onloadend = function() { success(this.result); }; reader.readAsText(file); }, fail ); }, fail ); }; TextFileSystem.prototype.createFile = function createFile(fileName, data, success, fail) { window.resolveLocalFileSystemURL( this.dirPath, function(fileSystem) { // ファイルを生成する、または上書きオープンする fileSystem.getFile( fileName, { exclusive: false, create: true, }, function(fileEntry) { // 作成できたので書き込む fileEntry.createWriter( function(writer) { writer.write(data); success(); }, fail ); }, fail ); }, fail ); }; TextFileSystem.prototype.removeFile = function writeFile(fileName, success, fail) { // fetches filesystem. window.resolveLocalFileSystemURL( this.dirPath, function(fileSystem) { fileSystem.getFile( fileName, { exclusive: false, create: false, }, function(fileEntry) { fileEntry.remove(); success(); }, fail ); }, fail ); };
resolveLocalFileSystemURLは都度実行しています
resolveLocalFileSystemURL() を都度実行していますが、正直いいのか悪いのかわかりません。Dalvik側の onPause(), onResume() とかとの関係で、勝手にインスタンスが潰されると困るな、と。
failコールバック
fail のコールバックは一つのパラメータを取ります。chrome では error パラメータのオブジェクトは code のみからなります。
function (error) { console.log(error.code); // 数字だけ出る }
エラーコードは https://developer.mozilla.org/en-US/docs/Web/API/FileError あたり参照。
cordova.file.* の中身 on Android
applicationDirectory: "file:///android_asset/" applicationStorageDirectory: "file:///data/user/0/(ネームスペース)/" cacheDirectory: "file:///data/user/0/j(ネームスペース)/cache/" dataDirectory: "file:///data/user/0/(ネームスペース)/files/" documentsDirectory: null externalApplicationStorageDirectory: "file:///storage/emulated/0/Android/data/(ネームスペース)/" externalCacheDirectory: "file:///storage/emulated/0/Android/data/j(ネームスペース)/cache/" externalDataDirectory: "file:///storage/emulated/0/Android/data/(ネームスペース)/files/" externalRootDirectory: "file:///storage/emulated/0/" sharedDirectory: null syncedDataDirectory: null tempDirectory: null
nullが出ているので注意が必要です。
また、iOSとは値が違ってくるのだろうと思います。
パソコンとつなげるなら for Windows
externalDataDirectory
(file:///storage/emulated/0/Android/data/(ネームスペース)/files/
)は、Windowsでは
(デバイス)\内部共有ストレージ\Android\data\(ネームスペース)\files
になります。
GeoDjangoに挑戦中 #5 GeoJSONを読むLeaflet
参照サイト
https://homata.gitbook.io/geodjango/geodjango/tutorial#geojson-serializer
jqueryとleafletのURL
jqeury (js)
https://code.jquery.com/ から適切なバージョンと圧縮タイプを選ぶと、URLとコピーボタンが現れるので、コピーしてペーストします。
leaflet (js, css)
https://leafletjs.com/examples/quick-start/ はたぶん最新でないかと考えて、ここから、<script>
タグと<link>
タグをコピペしました。
前提
http://(ホスト名):(ポート)/(アプリ名)/geojson
で GeoJSONを吐くものとします。
ファイル
index.html
ローカルのパスは(アプリ名)/templates/(アプリ名)/index.html
です。
URLは/(アプリ名)/
になっています((アプリ名)/urls.py
による)。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Map</title> {% load static %} <meta viewport="width=device-width,initial-scale=1"> <script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin="" /> <script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js" integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==" crossorigin=""></script> <style> HTML, body, #MAP { margin: 0; padding: 0; width: 100%; height: 100%; position: relative; } </style> <script src="{% static "(アプリ名)/js/index.js" %}"></script> <body> <div id="MAP"></div> </body> </html>
script
のsrc
属性値は{% static "(アプリ名)/js/index.js" %}
となっていますが、適切なURLに置換されます。
index.js
ローカルのパスは(アプリ名)/static/(アプリ名)/js/index.js
です。
URLは/static/(アプリ名)/js/index.js
です。
window.onload = function() { var lyr_stdmap = L.tileLayer( "https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png", { "id": "stdmap", "attribution": "<a href=\"http://portal.cyberjapan.jp/help/termsofuse.html\" target=\"_blank\">国土地理院</a>" } ); var map = L.map( "MAP", { "layers": [lyr_stdmap] } ); map.setView([34.5, 133.39], 16); // GeoJSONレイヤ // HTMLのURLが http://.../(アプリ名)/ になっているので // http://.../(アプリ名)/geojson/ にアクセス $.getJSON("geojson/", function(data) { L.geoJson(data).addTo(map); }); }
GeoDjangoに挑戦中 #4 GeoJSONを吐く
参考URL
https://homata.gitbook.io/geodjango/geodjango/tutorial#geojson-serializer
ビューを登録する
(アプリ名)/urls.py
urlpatterns = [ .... path('geojson/', GeojsonAPIView.as_view(), name='geojson_view'), .... ]
(アプリ名)/views.py
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status import traceback import json from django.core.serializers import serialize class GeojsonAPIView(APIView): def get(self, request, *args, **keywords): try: encjson = serialize( 'geojson', FieldPolygon.objects.all(), srid=4326, geometry_field='geom', fields=('gid', 'name') ) result = json.loads(encjson) response = Response(result, status=status.HTTP_200_OK) except Exception as e: traceback.print_exc() response = Response({}, status=status.HTTP_404_NOT_FOUND) except: response = Response({}, status=status.HTTP_404_NOT_FOUND) return response
fields
に指定したカラムがプロパティになります。FieldPolygon.object.all()
にFieldPolygon.object.filter()
等をかけられるようです。
GeoDJangoに挑戦中 #3 JSONを吐くまで
参考URL
https://homata.gitbook.io/geodjango/geodjango/tutorial#restful-apino
モジュール追加
portsに無いのでpipから入れる。
pip install djangorestframework pip install djangorestframework-gis pip install django-filter pip install markdown
これらを(プロジェクト名)/settings.py
に登録します。
INSTALLED_APPS = [ ... 'rest_framework', 'rest_framework_gis', 'django_filters', ... ]
アプリの登録(やってない場合)
APPSに登録
(プロジェクト名)/settings.py
INSTALLED_APPS = [ ... (アプリ名), ... ]
(プロジェクト名)/urls.py から (アプリ名)/urls.py に引き継ぐ
(プロジェクト名)/urls.py
urlpatterns = [ ... url(r'^(アプリ名)/', include('(アプリ名).urls')), ... ]
シリアライザの作成
(アプリ名)/serializers.py
from rest_framework import serializers from .models import FieldPolygon class FieldPolygonSerializer(serializers.ModelSerializer): class Meta: model = FieldPolygon # fields = ('__all__') fields = ('gid', 'name', 'geom')
(アプリ名)/views.py
from rest_framework import viewsets from rest_framework_gis.filters import DistanceToPointFilter, InBBoxFilter from rest_framework.pagination import PageNumberPagination from .serializers import FieldPolygonSerializer from .models import FieldPolygon class MyPagination(PageNumberPagination): page_size_query_param = 'page_size' class FieldPolygonViewSet(viewsets.ModelViewSet): queryset = FieldPolygon.objects.all() serializer_class = FieldPolygonSerializer pagination_class = MyPagination filter_backends = (DistanceToPointFilter,) distance_filter_field = 'geom' distance_filter_convert_meters = True
http://.../(アプリ名)/json からリストを見れるようにする
(アプリ名)/urls.py
rom django.urls import path, include from rest_framework.routers import DefaultRouter from .views import FieldPolygonViewSet router = DefaultRouter() router.register('fieldpolygon', FieldPolygonViewSet) urlpatterns = [ path('json/', include(router.urls)), ]
ブラウザで見る ただし WKT で出る
http://(ホスト名):(ポート)/(アプリ名)/json
でアクセスすると、HTMLベースでビュー一覧を表示してくれます。
http://(ホスト名):(ポート)/(アプリ名)/json/(ビュー名)
で、ビューの中身を表示してくれます。
ただし、GeoJSONではなく、geom
はWKTで出ました。
GeoDjangoに挑戦中 #2 地物管理まで
参考URL
https://homata.gitbook.io/geodjango/geodjango/import
PostGISテーブル
プロジェクト全体の設定で、使用するデータベースを指定します。(プロジェクト名)/settings.py
を変更します。
PostgreSQLをデフォルトデータベースに指定
DATABASES = { 'default': { 'ENGINE': 'django.contrib.gis.db.backends.postgis', 'NAME': (データベース名), 'USER': (ユーザ名), 'HOST':'localhost', }, }
GISデータを使うようにする
プロジェクト全体の設定でGISデータ型を使用するようにします。(プロジェクト名)/settings.py
を変更します。
INSTALLED_APPS = [ ... 'django.contrib.gis', ... ]
psycopg2が必要
ModuleNotFoundError: No module named 'psycopg2'
と怒られたので py37-psycopg2 を入れました。
データモデル
仮定するfieldテーブル
ここでは次のテーブルがデータベースにあるとします。
CREATE TABLE field ( gid SERIAL PRIMARY KEY, name TEXT, geom GEOMETRY(POLYGON, 4612); );
modules.pyの作成
(アプリ名)/modules.py
を作成します。
from django.db import models from django.contrib.gis.db import models class Border(models.Model): gid = models.IntegerField(primary_key=True) name = models.TextField() geom = models.PolygonField(srid=4612) class Meta: db_table = 'field'
TEXT型(models.TextField()
)とVARCHAR型(models.CharField()
)とは別になっています。
djangoはテーブル名は(アプリ名)_(モデルクラス名)
と仮定します。変更するには、サブクラスMeta
を作り、db_table
フィールドにテーブル名を与えます。
また、djangoは主キーはid
と仮定し、モデルに存在しないなら自動追加します。shp2pgsqlからPostGISテーブルを作る場合には、デフォルトの主キーはgid
です。gidでprimary_key=True
とすることで、id
の自動追加は抑制されます。
admin.py の設定
(アプリ名)/admin.py
でBorder
クラスの管理方法を登録します。
from django.contrib.gis import admin from .models import Border admin.site.register(Border, admin.OSMGeoAdmin)
管理ツールで地物も管理できるようにする
(プロジェクト名)/urls.py
で既に管理ツールを実行できるようにしていると思いますが、これを少し変更します。
#from django.contrib import admin from django.contrib.gis import admin
url(r'^admin/', admin.site.urls)
のadmin
をdjango.contrib.gis.admin
に切り替えます。
アプリを登録
(プロジェクト名)/settigs.py
で次のようにアプリを登録します。
INSTALLED_APPS = [ ... (アプリ名), ... ]
登録しないと、全く動きません。
Djangoに挑戦中 #1
URL
インストール
FreeBSDのパッケージからインストール。py37-django
だとDjango 1系ですので、py37-django30
とします。
pkg install py37-django30
プロジェクト作成からリモートのブラウザで見るまで
プロジェクトで一つのサーバみたいです。その下にアプリケーションがぶら下がります。
django-admin startproject (プロジェクト名) [(フォルダ名)]
または
./manage.py startproject (プロジェクト名) [(フォルダ名)]
フォルダなど
この時、(フォルダ名)
の下にアプリが付きます。
初期状態では、(プロジェクト名)
というアプリができあがります。たぶん、プロジェクト全体を統括するアプリで、 サーバのルートディレクトリに該当します。
プロジェクトの設定
(プロジェクト名)/settings.py
ALLOWED_HOSTS = ["foo.bar.example"] LANGUAGE_CODE = 'ja-JP' TIME_ZONE = 'Asia/Tokyo'
ALLOWED_HOSTS
で指定したホスト名でアクセスしないと、サーバは応じてくれません。FQDNで書いてIPアドレス指定するとダメです。
マイグレート
まだよくわかっていません。
./manage.py migrate
とりあえず起動
./manage.py runserver (IPアドレス):(ポート)
デフォルトは、127.0.0.1:8000 です。
管理者を置く
./manage.py createsuperuser
http://(ホスト名)/admin
にアクセスすると見えます。
表示だけアプリを作る
django-admin startapp (アプリケーション名)
または
./manage.py startapp (アプリケーション名)
urls設定
(プロジェクト名)/urls.py
"/(アプリ名)/.*"の設定は(アプリ名).urls
= (アプリ名)/urls.py
に回す設定にします。
from django.conf.urls import url, include urlpatterns = [ url(r'^(アプリ名)/', include('(アプリ名).urls')), ... ]
(アプリ名)/urls.py (新規)
from django.conf.urls import url from . import views # urlのP1が''なら、後ろは何でもマッチする # '^$'で本当の空っぽでないといけない urlpatterns = [ url('^$', views.index, name='index'), ]
(アプリ名)/views.py でHTMLを表示
from django.http import HttpResponse def index(request): return HttpResponse('<h1>Hello!</h1>')
アクセス
http://(ホスト名)/(アプリ名)/
でアクセスできます。
テンプレート/静的ファイルを使ってみる
さきほどのは HttpResponse()
を使ってHTMLをviews.py
から流し込んでいました。テンプレートや静的ファイルを使っていきます。
上述の参考サイトの https://python.keicode.com/django/how-to-serve-static-files.php を参考にしました。
テンプレートを使う
(アプリ名)/templates/(アプリ名)/index.html
を例えば次のようにします。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>myapp2</title> {% load static %} <script src="{% static "myapp2/js/index.js" %}"></script> <meta viewport="width=device-width,initial-scale=1"> <body> <h1>myapp2</h1> </body> </html>
(アプリ名)/views.py
を次のようにします。
from django.shortcuts import render def index(request): contexts = {} return render(request,'(アプリ名)/index.html',contexts)
これで http://(ホスト名):(ポート)/(アプリ名)/
からindex.html
が見れます。
静的ファイルを使う
続いて、(アプリ名)/static/(アプリ名)/js/index.js
を次のようにします。
window.onload = function() { alert("hello"); }
これで http://(ホスト名):(ポート)/static/(アプリ名)/js/index.js
からindex.js
が見れます。