Kaleab Woldemariam
Kaleab Woldemariam

Reputation: 3003

How to integrate Geodjango with Google Maps API 3?

I have a geodjango queryset containing several fields but want to use only user_name and location (a point field) which I want to use as a marker in google maps API 3.

Bear with me as I don’t know Javascript and I have a series of questions.
Take this as conceptual brainstorming for a novice:

  1. My SO search suggests that I need to serialize the queryset objects to JSON. I use the built-in serializer module to convert to JSON.
    I think the JSON objects are converted in views.py (let’s call it json_data). Are these JSON objects stored in the PostGIS database? Wouldn’t that be redundant?

  2. Furthermore, how do I reference them in the map.js (google maps API 3) javascript file?
    I want to (import?link?) JSON objects to display them as location markers.

  3. I want to know how to declare and iterate the javascript variable locations.

For var(i=0;i< locations.length;i++){[
[json_data.user_name, json_data.point],
]

var map = new google.maps.Map(document.getElementById('map'), {
  center: new google.maps.LatLng(49.279504, -123.1162),
  zoom: 14,
  mapTypeId: google.maps.MapTypeId.ROADMAP
});
var infowindow = new google.maps.InfoWindow();
var marker, i;
for (i = 0; i < locations.length; i++) {
  marker = new google.maps.Marker({
    position: new google.maps.LatLng(locations[i][1], locations[i][2]),
    map: map,
    icon: 'http://maps.google.com/mapfiles/ms/icons/blue-dot.png'
  });
  google.maps.event.addListener(marker, 'click', (function(marker, i) {
    return function() {
      infowindow.setContent(locations[i][0]);
      infowindow.open(map, marker);
    }
  })(marker, i));
}

Guide me if I went unnecessarily convoluted way to do a simple task.

Upvotes: 2

Views: 2757

Answers (2)

John Moutafis
John Moutafis

Reputation: 23134

TL;DR

  1. No, what you are doing is not redundant and nothing get's written to the database from those answers.
  2. You need to make a getJSON() or similar call to your API's endpoint to access the data.
  3. You can do it on the 2nd step's call and declare it as a list.

What you are thinking is pretty much correct but there is room for improvement (thus the long answer below).


Answer:

Some time ago I read a very good initiation tutorial on building a GIS application with geodjango and google maps. Read it and it should give you a great jump start.

After you read that we will follow a somewhat different way which leaves more room to play around with your front-end (use react for example or whatever comes to mind).

The back-end:

  1. Create a view to retrieve the information you want (user_name, location) as JSON, using the values() queryset method which returns a list of dictionaries.
    Since we have to JSONify a list, we will use JsonResponse and we will mark it as unsafe:

    from django.http import JsonResponse
    
    def my_view(request):
        resp = MyModel.objects.all().values('user_name', 'location')
        return JsonResponse(list(resp), safe=False)
    
  2. Add an endpoint to access that view on urls.py:

    urlpatterns = [
        ...
        url(r'^my_endpoint/$', my_view, name='my_endpoint'),
        ...
    ]
    

    Now whenever we access the my_endpoint/ we will get a JSON representation of every object's user_name and location in our database which will look like this:

    [
      {user_name: a_user, location: [lat, lng]},
      {user_name: another_user, location: [lat, lng]},
      ...
    ]
    

Moving to the front-end now:

  1. Make a getJSON() or an ajax() or any other type of call to the API and in the same time create a marker list (close to what @MoshFeu suggests):

    let map = new google.maps.Map(document.getElementById('map'), {
        center: new google.maps.LatLng(49.279504, -123.1162),
        zoom: 14,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    });
    
    let markers = [];
    
    $.getJSON( "my_base_url/my_endpoint", function(data) {
        $.each(data, function() {
            markers.push(
                new google.maps.Marker({
                    position: {
                        lat: data['location'][0], 
                        lng: data['location'][1]
                    },
                    map: map,
                    icon: 'http://maps.google.com/mapfiles/ms/icons/blue-dot.png'
                })     
            );
        });
    });
    ...
    

And we are pretty much done!

You don't need to make any special serialization to your data.
You can query the data from any type of front-end you can imagine which gives you designing freedom.

Upvotes: 3

Artur Barseghyan
Artur Barseghyan

Reputation: 14212

My use-case. I used django.contrib.gis (django.contrib.gis.db.models.PolygonField) and needed to replace default map with Google maps + change default map coordinates, zoom, etc.

TL;DR

  • I created a new app called gis_addons with custom template and widget to use.
  • I instructed my model admin (using formfield_overrides) to use my own map widget.

Make sure to add the gis_addons to INSTALLED_APPS.

File: gis_addons/templates/gis_addons/openlayers_googlemaps.html

{% extends "gis/openlayers.html" %}

{% load i18n l10n %}

{% block base_layer %}
var base_layer = new ol.layer.Tile({
    source: new ol.source.XYZ({
        attributions: [new ol.Attribution({ html: '<a href=""></a>' })],
        maxZoom: 25,
        url: "http://mt0.google.com/vt/lyrs=r&hl=en&x={x}&y={y}&z={z}&s=Ga"
    })
});
{% endblock %}

{% block options %}var options = {
  base_layer: base_layer,
  geom_name: '{{ geom_type }}',
  id: '{{ id }}',
  map_id: '{{ id }}_map',
  map_options: map_options,
  map_srid: {{ map_srid|unlocalize }},
  name: '{{ name }}',
  default_lat: 53.2193835,
  default_lon: 6.5665018,
  default_zoom: 15
};
{% endblock %}

File: gis_addons/widgets.py

from django.contrib.gis.forms.widgets import OpenLayersWidget

class GoogleMapsOpenLayersWidget(OpenLayersWidget):
    """Google Maps OpenLayer widget."""

    template_name = 'gis_addons/openlayers_googlemaps.html'

File: my_app/models.py

from django.db import models
from django.contrib.gis.db import models as gis_models
from django.utils.translation import ugettext_lazy as _

class MyModel(models.Model):

    # ...

    coordinates = gis_models.PolygonField(
        verbose_name=_("Geo coordinates"),
    )

    def __str__(self):
        return self.name

File: my_app/admin.py

from django.contrib import admin
from django.contrib.gis.db.models import PolygonField
from gis_addons.widgets import GoogleMapsOpenLayersWidget
from my_app.models import MyModel

@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):

    # ...

    formfield_overrides = {
        PolygonField: {"widget": GoogleMapsOpenLayersWidget}
    }

Upvotes: 0

Related Questions