Django Gmap v3ウィジェット-検索を使用したジオロケーション、JSONFieldでの座標とアドレスの保存

こんにちは このタスクは、djangoプロジェクトの1つでユーザーに位置情報(Googleマップv3)を実装するように設定されていました。ソリューションを共有したいと思います。



必要な機能:


  1. 現在の位置のマーカー、マーカーを移動する機能(ドラッグ)、クリックイベントで設定されたマップ出力
  2. 住所で検索(オートコンプリート)
  3. 座標と住所自体の両方を保存する(場所がある場合)




いつものように、開発は同様のソリューションの検索から始まり、機能の一部、つまり1ポイントを与えることに基づいて例が示されました。 スニペットへのリンク 。 いくつかの欠点がありました。 プロジェクトにカスタムユーザーフィールドを追加するには、標準モジュールAUTH_PROFILE_MODULEを使用しました。 したがって、ユーザープロファイルを編集するための管理パネルに、ブロック付きのインラインフィールドを追加しました(admin.StackedInline)。 これらのインラインブロックのマークアップを生成する場合、djangoは入力のIDに「-」記号を使用します。 各ブロックにプレフィックスを追加します。 JavaScriptは、ご存じのように、関数名での「-」の使用を好まないため、最初に行うことは、すべての記号「-」を関数名に「_」に変換することです。

functionName=name.replace('-', '_')
      
      





また、座標は文字列「x、y」として保存され、その後に出力用の分割が続きます。 これらの同じコンマが発生する可能性のあるアドレスもフィールドに追加すると、競合が発生します。 ソリューションとして、TextFieldを使用してJSONオブジェクトを格納できるようにする別のスニペットが使用されました。 スニペットへのリンク 。 したがって、座標とアドレスはJSONオブジェクトとして保存されました。

 value = {'lat': lat, 'lng': lng, 'address': address}
      
      





サーバーでのレンダリング時の処理:

 if value is None: lat, lng, address = DEFAULT_LAT, DEFAULT_LNG, DEFAULT_ADDRESS value = {'lat': lat, 'lng': lng, 'address': address} else: lat, lng, address = float(value['lat']), float(value['lng']), value['address'] curLocation = json.dumps(value, cls=DjangoJSONEncoder)
      
      





クライアント側の処理:

 function savePosition_%(functionName)s(point, address) { var input = document.getElementById("id_%(name)s"); var location = {'lat': point.lat().toFixed(6), 'lng': point.lng().toFixed(6)}; location.address = '%(defAddress)s'; if (address) { location.address = address; } input.value = JSON.stringify(location); map_%(functionName)s.panTo(point); }
      
      





現在の位置情報を表示するフィールドを追加しました:

 html += '<br /><label>%s: </label><span>%s</span>' % (u' ', address)
      
      





2つのポイントを実装するために、google.maps.GeocoderとjQueryオートコンプリートが使用されました。

 google.maps.event.addListener(marker, 'dragend', function(mouseEvent) { geocoder.geocode({'latLng': mouseEvent.latLng}, function(results, status) { if (status == google.maps.GeocoderStatus.OK && results[0]) { $('#address_%(name)s').val(results[0].formatted_address); savePosition_%(functionName)s(mouseEvent.latLng, results[0].formatted_address); } else { savePosition_%(functionName)s(mouseEvent.latLng); } }); }); google.maps.event.addListener(map_%(functionName)s, 'click', function(mouseEvent){ marker.setPosition(mouseEvent.latLng); geocoder.geocode({'latLng': mouseEvent.latLng}, function(results, status) { if (status == google.maps.GeocoderStatus.OK && results[0]) { $('#address_%(name)s').val(results[0].formatted_address); savePosition_%(functionName)s(mouseEvent.latLng, results[0].formatted_address); } else { savePosition_%(functionName)s(mouseEvent.latLng); } }); }); $('#address_%(name)s').autocomplete({ source: function(request, response) { geocoder.geocode({'address': request.term}, function(results, status) { response($.map(results, function(item) { return { value: item.formatted_address, location: item.geometry.location } })); }) }, select: function(event, ui) { marker.setPosition(ui.item.location); savePosition_%(functionName)s(ui.item.location, ui.item.value); } });
      
      





検索するフィールドを追加しました:

 html += '<label>%s: </label><input id="address_%s" type="text"/>' % (u'  ', name)
      
      







すべてをまとめると、次のスニペットが得られました。


 from django.conf import settings from main.JSONField import JSONField from django.core.serializers.json import DjangoJSONEncoder from django.utils import simplejson as json DEFAULT_WIDTH = 300 DEFAULT_HEIGHT = 300 DEFAULT_LAT = 55.75 DEFAULT_LNG = 37.62 DEFAULT_ADDRESS = u'( )' class LocationWidget(forms.TextInput): def __init__(self, *args, **kw): self.map_width = kw.get("map_width", DEFAULT_WIDTH) self.map_height = kw.get("map_height", DEFAULT_HEIGHT) super(LocationWidget, self).__init__(*args, **kw) self.inner_widget = forms.widgets.HiddenInput() def render(self, name, value, *args, **kwargs): if value is None: lat, lng, address = DEFAULT_LAT, DEFAULT_LNG, DEFAULT_ADDRESS value = {'lat': lat, 'lng': lng, 'address': address} else: lat, lng, address = float(value['lat']), float(value['lng']), value['address'] curLocation = json.dumps(value, cls=DjangoJSONEncoder) js = ''' <script type="text/javascript"> //<![CDATA[ var map_%(functionName)s; function savePosition_%(functionName)s(point, address) { var input = document.getElementById("id_%(name)s"); var location = {'lat': point.lat().toFixed(6), 'lng': point.lng().toFixed(6)}; location.address = '%(defAddress)s'; if (address) { location.address = address; } input.value = JSON.stringify(location); map_%(functionName)s.panTo(point); } function load_%(functionName)s() { var point = new google.maps.LatLng(%(lat)f, %(lng)f); var options = { zoom: 13, center: point, mapTypeId: google.maps.MapTypeId.ROADMAP }; map_%(functionName)s = new google.maps.Map(document.getElementById("map_%(name)s"), options); geocoder = new google.maps.Geocoder(); var marker = new google.maps.Marker({ map: map_%(functionName)s, position: point, draggable: true }); google.maps.event.addListener(marker, 'dragend', function(mouseEvent) { geocoder.geocode({'latLng': mouseEvent.latLng}, function(results, status) { if (status == google.maps.GeocoderStatus.OK && results[0]) { $('#address_%(name)s').val(results[0].formatted_address); savePosition_%(functionName)s(mouseEvent.latLng, results[0].formatted_address); } else { savePosition_%(functionName)s(mouseEvent.latLng); } }); }); google.maps.event.addListener(map_%(functionName)s, 'click', function(mouseEvent){ marker.setPosition(mouseEvent.latLng); geocoder.geocode({'latLng': mouseEvent.latLng}, function(results, status) { if (status == google.maps.GeocoderStatus.OK && results[0]) { $('#address_%(name)s').val(results[0].formatted_address); savePosition_%(functionName)s(mouseEvent.latLng, results[0].formatted_address); } else { savePosition_%(functionName)s(mouseEvent.latLng); } }); }); $('#address_%(name)s').autocomplete({ source: function(request, response) { geocoder.geocode({'address': request.term}, function(results, status) { response($.map(results, function(item) { return { value: item.formatted_address, location: item.geometry.location } })); }) }, select: function(event, ui) { marker.setPosition(ui.item.location); savePosition_%(functionName)s(ui.item.location, ui.item.value); } }); } $(document).ready(function(){ load_%(functionName)s(); }); //]]> </script> ''' % dict(functionName=name.replace('-', '_'), name=name, lat=lat, lng=lng, defAddress=DEFAULT_ADDRESS) html = self.inner_widget.render("%s" % name, "%s" % curLocation, dict(id='id_%s' % name)) html += '<div id="map_%s" style="width: %dpx; height: %dpx"></div>' % (name, self.map_width, self.map_height) html += '<label>%s: </label><input id="address_%s" type="text"/>' % (u'  ', name) html += '<br /><label>%s: </label><span>%s</span>' % (u' ', address) return mark_safe(js + html) class Media: css = {'all': ( 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/redmond/jquery-ui.css', settings.MEDIA_URL+'css/main.css', )} js = ( 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js', 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js', 'http://maps.google.com/maps/api/js?sensor=false', ) class LocationField(JSONField): def formfield(self, **kwargs): defaults = {'widget': LocationWidget} return super(LocationField, self).formfield(**defaults)
      
      







PS


main.cssには次のものがあります。

 .ui-autocomplete li { list-style-type: none; }
      
      





これは管理パネルでどのように見えるかです:





みんなありがとう!



All Articles