Fixed #26697 -- Removed contrib.gis.maps.
This commit is contained in:
parent
727d7ce6cb
commit
5a51b44936
|
@ -1,69 +0,0 @@
|
|||
"""
|
||||
This module houses the GoogleMap object, used for generating
|
||||
the needed javascript to embed Google Maps in a Web page.
|
||||
|
||||
Google(R) is a registered trademark of Google, Inc. of Mountain View, California.
|
||||
|
||||
Example:
|
||||
|
||||
* In the view:
|
||||
return render(request, 'template.html', {'google': GoogleMap(key="abcdefg")})
|
||||
|
||||
* In the template:
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
{{ google.xhtml }}
|
||||
<head>
|
||||
<title>Google Maps via GeoDjango</title>
|
||||
{{ google.style }}
|
||||
{{ google.scripts }}
|
||||
</head>
|
||||
{{ google.body }}
|
||||
<div id="{{ google.dom_id }}" style="width:600px;height:400px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Note: If you want to be more explicit in your templates, the following are
|
||||
equivalent:
|
||||
{{ google.body }} => "<body {{ google.onload }} {{ google.onunload }}>"
|
||||
{{ google.xhtml }} => "<html xmlns="http://www.w3.org/1999/xhtml" {{ google.xmlns }}>"
|
||||
{{ google.style }} => "<style>{{ google.vml_css }}</style>"
|
||||
|
||||
Explanation:
|
||||
- The `xhtml` property provides the correct XML namespace needed for
|
||||
Google Maps to operate in IE using XHTML. Google Maps on IE uses
|
||||
VML to draw polylines. Returns, by default:
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml">
|
||||
|
||||
- The `style` property provides the correct style tag for the CSS
|
||||
properties required by Google Maps on IE:
|
||||
<style type="text/css">v\:* {behavior:url(#default#VML);}</style>
|
||||
|
||||
- The `scripts` property provides the necessary <script> tags for
|
||||
including the Google Maps javascript, as well as including the
|
||||
generated javascript.
|
||||
|
||||
- The `body` property provides the correct attributes for the
|
||||
body tag to load the generated javascript. By default, returns:
|
||||
<body onload="gmap_load()" onunload="GUnload()">
|
||||
|
||||
- The `dom_id` property returns the DOM id for the map. Defaults to "map".
|
||||
|
||||
The following attributes may be set or customized in your local settings:
|
||||
* GOOGLE_MAPS_API_KEY: String of your Google Maps API key. These are tied
|
||||
to a domain. May be obtained from https://developers.google.com/maps/
|
||||
* GOOGLE_MAPS_API_VERSION (optional): Defaults to using "2.x"
|
||||
* GOOGLE_MAPS_URL (optional): Must have a substitution ('%s') for the API
|
||||
version.
|
||||
"""
|
||||
from django.contrib.gis.maps.google.gmap import GoogleMap, GoogleMapSet
|
||||
from django.contrib.gis.maps.google.overlays import (
|
||||
GEvent, GIcon, GMarker, GPolygon, GPolyline,
|
||||
)
|
||||
from django.contrib.gis.maps.google.zoom import GoogleZoom
|
||||
|
||||
__all__ = [
|
||||
'GoogleMap', 'GoogleMapSet', 'GEvent', 'GIcon', 'GMarker', 'GPolygon',
|
||||
'GPolyline', 'GoogleZoom',
|
||||
]
|
|
@ -1,244 +0,0 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.gis.maps.google.overlays import (
|
||||
GMarker, GPolygon, GPolyline,
|
||||
)
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.html import format_html
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.six.moves import range
|
||||
|
||||
|
||||
class GoogleMapException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
# The default Google Maps URL (for the API javascript)
|
||||
# TODO: Internationalize for Japan, UK, etc.
|
||||
GOOGLE_MAPS_URL = 'http://maps.google.com/maps?file=api&v=%s&key='
|
||||
|
||||
|
||||
class GoogleMap(object):
|
||||
"A class for generating Google Maps JavaScript."
|
||||
|
||||
# String constants
|
||||
onunload = mark_safe('onunload="GUnload()"') # Cleans up after Google Maps
|
||||
vml_css = mark_safe('v\:* {behavior:url(#default#VML);}') # CSS for IE VML
|
||||
xmlns = mark_safe('xmlns:v="urn:schemas-microsoft-com:vml"') # XML Namespace (for IE VML).
|
||||
|
||||
def __init__(self, key=None, api_url=None, version=None,
|
||||
center=None, zoom=None, dom_id='map',
|
||||
kml_urls=[], polylines=None, polygons=None, markers=None,
|
||||
template='gis/google/google-map.js',
|
||||
js_module='geodjango',
|
||||
extra_context={}):
|
||||
|
||||
# The Google Maps API Key defined in the settings will be used
|
||||
# if not passed in as a parameter. The use of an API key is
|
||||
# _required_.
|
||||
if not key:
|
||||
try:
|
||||
self.key = settings.GOOGLE_MAPS_API_KEY
|
||||
except AttributeError:
|
||||
raise GoogleMapException(
|
||||
'Google Maps API Key not found (try adding '
|
||||
'GOOGLE_MAPS_API_KEY to your settings).'
|
||||
)
|
||||
else:
|
||||
self.key = key
|
||||
|
||||
# Getting the Google Maps API version, defaults to using the latest ("2.x"),
|
||||
# this is not necessarily the most stable.
|
||||
if not version:
|
||||
self.version = getattr(settings, 'GOOGLE_MAPS_API_VERSION', '2.x')
|
||||
else:
|
||||
self.version = version
|
||||
|
||||
# Can specify the API URL in the `api_url` keyword.
|
||||
if not api_url:
|
||||
self.api_url = getattr(settings, 'GOOGLE_MAPS_URL', GOOGLE_MAPS_URL) % self.version
|
||||
else:
|
||||
self.api_url = api_url
|
||||
|
||||
# Setting the DOM id of the map, the load function, the JavaScript
|
||||
# template, and the KML URLs array.
|
||||
self.dom_id = dom_id
|
||||
self.extra_context = extra_context
|
||||
self.js_module = js_module
|
||||
self.template = template
|
||||
self.kml_urls = kml_urls
|
||||
|
||||
# Does the user want any GMarker, GPolygon, and/or GPolyline overlays?
|
||||
overlay_info = [[GMarker, markers, 'markers'],
|
||||
[GPolygon, polygons, 'polygons'],
|
||||
[GPolyline, polylines, 'polylines']]
|
||||
|
||||
for overlay_class, overlay_list, varname in overlay_info:
|
||||
setattr(self, varname, [])
|
||||
if overlay_list:
|
||||
for overlay in overlay_list:
|
||||
if isinstance(overlay, overlay_class):
|
||||
getattr(self, varname).append(overlay)
|
||||
else:
|
||||
getattr(self, varname).append(overlay_class(overlay))
|
||||
|
||||
# If GMarker, GPolygons, and/or GPolylines are used the zoom will be
|
||||
# automatically calculated via the Google Maps API. If both a zoom
|
||||
# level and a center coordinate are provided with polygons/polylines,
|
||||
# no automatic determination will occur.
|
||||
self.calc_zoom = False
|
||||
if self.polygons or self.polylines or self.markers:
|
||||
if center is None or zoom is None:
|
||||
self.calc_zoom = True
|
||||
|
||||
# Defaults for the zoom level and center coordinates if the zoom
|
||||
# is not automatically calculated.
|
||||
if zoom is None:
|
||||
zoom = 4
|
||||
self.zoom = zoom
|
||||
if center is None:
|
||||
center = (0, 0)
|
||||
self.center = center
|
||||
|
||||
def render(self):
|
||||
"""
|
||||
Generates the JavaScript necessary for displaying this Google Map.
|
||||
"""
|
||||
params = {'calc_zoom': self.calc_zoom,
|
||||
'center': self.center,
|
||||
'dom_id': self.dom_id,
|
||||
'js_module': self.js_module,
|
||||
'kml_urls': self.kml_urls,
|
||||
'zoom': self.zoom,
|
||||
'polygons': self.polygons,
|
||||
'polylines': self.polylines,
|
||||
'icons': self.icons,
|
||||
'markers': self.markers,
|
||||
}
|
||||
params.update(self.extra_context)
|
||||
return render_to_string(self.template, params)
|
||||
|
||||
@property
|
||||
def body(self):
|
||||
"Returns HTML body tag for loading and unloading Google Maps javascript."
|
||||
return format_html('<body {} {}>', self.onload, self.onunload)
|
||||
|
||||
@property
|
||||
def onload(self):
|
||||
"Returns the `onload` HTML <body> attribute."
|
||||
return format_html('onload="{}.{}_load()"', self.js_module, self.dom_id)
|
||||
|
||||
@property
|
||||
def api_script(self):
|
||||
"Returns the <script> tag for the Google Maps API javascript."
|
||||
return format_html('<script src="{}{}" type="text/javascript"></script>',
|
||||
self.api_url, self.key)
|
||||
|
||||
@property
|
||||
def js(self):
|
||||
"Returns only the generated Google Maps JavaScript (no <script> tags)."
|
||||
return self.render()
|
||||
|
||||
@property
|
||||
def scripts(self):
|
||||
"Returns all <script></script> tags required with Google Maps JavaScript."
|
||||
return format_html('{}\n <script type="text/javascript">\n//<![CDATA[\n{}//]]>\n </script>',
|
||||
self.api_script, mark_safe(self.js))
|
||||
|
||||
@property
|
||||
def style(self):
|
||||
"Returns additional CSS styling needed for Google Maps on IE."
|
||||
return format_html('<style type="text/css">{}</style>', self.vml_css)
|
||||
|
||||
@property
|
||||
def xhtml(self):
|
||||
"Returns XHTML information needed for IE VML overlays."
|
||||
return format_html('<html xmlns="http://www.w3.org/1999/xhtml" {}>', self.xmlns)
|
||||
|
||||
@property
|
||||
def icons(self):
|
||||
"Returns a sequence of GIcon objects in this map."
|
||||
return set(marker.icon for marker in self.markers if marker.icon)
|
||||
|
||||
|
||||
class GoogleMapSet(GoogleMap):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
A class for generating sets of Google Maps that will be shown on the
|
||||
same page together.
|
||||
|
||||
Example:
|
||||
gmapset = GoogleMapSet( GoogleMap( ... ), GoogleMap( ... ) )
|
||||
gmapset = GoogleMapSet( [ gmap1, gmap2] )
|
||||
"""
|
||||
# The `google-multi.js` template is used instead of `google-single.js`
|
||||
# by default.
|
||||
template = kwargs.pop('template', 'gis/google/google-multi.js')
|
||||
|
||||
# This is the template used to generate the GMap load JavaScript for
|
||||
# each map in the set.
|
||||
self.map_template = kwargs.pop('map_template', 'gis/google/google-single.js')
|
||||
|
||||
# Running GoogleMap.__init__(), and resetting the template
|
||||
# value with default obtained above.
|
||||
super(GoogleMapSet, self).__init__(**kwargs)
|
||||
self.template = template
|
||||
|
||||
# If a tuple/list passed in as first element of args, then assume
|
||||
if isinstance(args[0], (tuple, list)):
|
||||
self.maps = args[0]
|
||||
else:
|
||||
self.maps = args
|
||||
|
||||
# Generating DOM ids for each of the maps in the set.
|
||||
self.dom_ids = ['map%d' % i for i in range(len(self.maps))]
|
||||
|
||||
def load_map_js(self):
|
||||
"""
|
||||
Returns JavaScript containing all of the loading routines for each
|
||||
map in this set.
|
||||
"""
|
||||
result = []
|
||||
for dom_id, gmap in zip(self.dom_ids, self.maps):
|
||||
# Backup copies the GoogleMap DOM id and template attributes.
|
||||
# They are overridden on each GoogleMap instance in the set so
|
||||
# that only the loading JavaScript (and not the header variables)
|
||||
# is used with the generated DOM ids.
|
||||
tmp = (gmap.template, gmap.dom_id)
|
||||
gmap.template = self.map_template
|
||||
gmap.dom_id = dom_id
|
||||
result.append(gmap.js)
|
||||
# Restoring the backup values.
|
||||
gmap.template, gmap.dom_id = tmp
|
||||
return mark_safe(''.join(result))
|
||||
|
||||
def render(self):
|
||||
"""
|
||||
Generates the JavaScript for the collection of Google Maps in
|
||||
this set.
|
||||
"""
|
||||
params = {'js_module': self.js_module,
|
||||
'dom_ids': self.dom_ids,
|
||||
'load_map_js': self.load_map_js(),
|
||||
'icons': self.icons,
|
||||
}
|
||||
params.update(self.extra_context)
|
||||
return render_to_string(self.template, params)
|
||||
|
||||
@property
|
||||
def onload(self):
|
||||
"Returns the `onload` HTML <body> attribute."
|
||||
# Overloaded to use the `load` function defined in the
|
||||
# `google-multi.js`, which calls the load routines for
|
||||
# each one of the individual maps in the set.
|
||||
return mark_safe('onload="%s.load()"' % self.js_module)
|
||||
|
||||
@property
|
||||
def icons(self):
|
||||
"Returns a sequence of all icons in each map of the set."
|
||||
icons = set()
|
||||
for map in self.maps:
|
||||
icons |= map.icons
|
||||
return icons
|
|
@ -1,332 +0,0 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from functools import total_ordering
|
||||
|
||||
from django.contrib.gis.geos import (
|
||||
LinearRing, LineString, Point, Polygon, fromstr,
|
||||
)
|
||||
from django.utils import six
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.utils.html import html_safe
|
||||
|
||||
|
||||
@html_safe
|
||||
@python_2_unicode_compatible
|
||||
class GEvent(object):
|
||||
"""
|
||||
A Python wrapper for the Google GEvent object.
|
||||
|
||||
Events can be attached to any object derived from GOverlayBase with the
|
||||
add_event() call.
|
||||
|
||||
For more information please see the Google Maps API Reference:
|
||||
https://developers.google.com/maps/documentation/javascript/reference#event
|
||||
|
||||
Example:
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.contrib.gis.maps.google import GoogleMap, GEvent, GPolyline
|
||||
|
||||
def sample_request(request):
|
||||
polyline = GPolyline('LINESTRING(101 26, 112 26, 102 31)')
|
||||
event = GEvent('click',
|
||||
'function() { location.href = "http://www.google.com"}')
|
||||
polyline.add_event(event)
|
||||
return render(request, 'mytemplate.html', {
|
||||
'google': GoogleMap(polylines=[polyline]),
|
||||
})
|
||||
"""
|
||||
|
||||
def __init__(self, event, action):
|
||||
"""
|
||||
Initializes a GEvent object.
|
||||
|
||||
Parameters:
|
||||
|
||||
event:
|
||||
string for the event, such as 'click'. The event must be a valid
|
||||
event for the object in the Google Maps API.
|
||||
There is no validation of the event type within Django.
|
||||
|
||||
action:
|
||||
string containing a Javascript function, such as
|
||||
'function() { location.href = "newurl";}'
|
||||
The string must be a valid Javascript function. Again there is no
|
||||
validation fo the function within Django.
|
||||
"""
|
||||
self.event = event
|
||||
self.action = action
|
||||
|
||||
def __str__(self):
|
||||
"Returns the parameter part of a GEvent."
|
||||
return '"%s", %s' % (self.event, self.action)
|
||||
|
||||
|
||||
@html_safe
|
||||
@python_2_unicode_compatible
|
||||
class GOverlayBase(object):
|
||||
def __init__(self):
|
||||
self.events = []
|
||||
|
||||
def latlng_from_coords(self, coords):
|
||||
"Generates a JavaScript array of GLatLng objects for the given coordinates."
|
||||
return '[%s]' % ','.join('new GLatLng(%s,%s)' % (y, x) for x, y in coords)
|
||||
|
||||
def add_event(self, event):
|
||||
"Attaches a GEvent to the overlay object."
|
||||
self.events.append(event)
|
||||
|
||||
def __str__(self):
|
||||
"The string representation is the JavaScript API call."
|
||||
return '%s(%s)' % (self.__class__.__name__, self.js_params)
|
||||
|
||||
|
||||
class GPolygon(GOverlayBase):
|
||||
"""
|
||||
A Python wrapper for the Google GPolygon object. For more information
|
||||
please see the Google Maps API Reference:
|
||||
https://developers.google.com/maps/documentation/javascript/reference#Polygon
|
||||
"""
|
||||
def __init__(self, poly,
|
||||
stroke_color='#0000ff', stroke_weight=2, stroke_opacity=1,
|
||||
fill_color='#0000ff', fill_opacity=0.4):
|
||||
"""
|
||||
The GPolygon object initializes on a GEOS Polygon or a parameter that
|
||||
may be instantiated into GEOS Polygon. Please note that this will not
|
||||
depict a Polygon's internal rings.
|
||||
|
||||
Keyword Options:
|
||||
|
||||
stroke_color:
|
||||
The color of the polygon outline. Defaults to '#0000ff' (blue).
|
||||
|
||||
stroke_weight:
|
||||
The width of the polygon outline, in pixels. Defaults to 2.
|
||||
|
||||
stroke_opacity:
|
||||
The opacity of the polygon outline, between 0 and 1. Defaults to 1.
|
||||
|
||||
fill_color:
|
||||
The color of the polygon fill. Defaults to '#0000ff' (blue).
|
||||
|
||||
fill_opacity:
|
||||
The opacity of the polygon fill. Defaults to 0.4.
|
||||
"""
|
||||
if isinstance(poly, six.string_types):
|
||||
poly = fromstr(poly)
|
||||
if isinstance(poly, (tuple, list)):
|
||||
poly = Polygon(poly)
|
||||
if not isinstance(poly, Polygon):
|
||||
raise TypeError('GPolygon may only initialize on GEOS Polygons.')
|
||||
|
||||
# Getting the envelope of the input polygon (used for automatically
|
||||
# determining the zoom level).
|
||||
self.envelope = poly.envelope
|
||||
|
||||
# Translating the coordinates into a JavaScript array of
|
||||
# Google `GLatLng` objects.
|
||||
self.points = self.latlng_from_coords(poly.shell.coords)
|
||||
|
||||
# Stroke settings.
|
||||
self.stroke_color, self.stroke_opacity, self.stroke_weight = stroke_color, stroke_opacity, stroke_weight
|
||||
|
||||
# Fill settings.
|
||||
self.fill_color, self.fill_opacity = fill_color, fill_opacity
|
||||
|
||||
super(GPolygon, self).__init__()
|
||||
|
||||
@property
|
||||
def js_params(self):
|
||||
return '%s, "%s", %s, %s, "%s", %s' % (self.points, self.stroke_color, self.stroke_weight, self.stroke_opacity,
|
||||
self.fill_color, self.fill_opacity)
|
||||
|
||||
|
||||
class GPolyline(GOverlayBase):
|
||||
"""
|
||||
A Python wrapper for the Google GPolyline object. For more information
|
||||
please see the Google Maps API Reference:
|
||||
https://developers.google.com/maps/documentation/javascript/reference#Polyline
|
||||
"""
|
||||
def __init__(self, geom, color='#0000ff', weight=2, opacity=1):
|
||||
"""
|
||||
The GPolyline object may be initialized on GEOS LineStirng, LinearRing,
|
||||
and Polygon objects (internal rings not supported) or a parameter that
|
||||
may instantiated into one of the above geometries.
|
||||
|
||||
Keyword Options:
|
||||
|
||||
color:
|
||||
The color to use for the polyline. Defaults to '#0000ff' (blue).
|
||||
|
||||
weight:
|
||||
The width of the polyline, in pixels. Defaults to 2.
|
||||
|
||||
opacity:
|
||||
The opacity of the polyline, between 0 and 1. Defaults to 1.
|
||||
"""
|
||||
# If a GEOS geometry isn't passed in, try to construct one.
|
||||
if isinstance(geom, six.string_types):
|
||||
geom = fromstr(geom)
|
||||
if isinstance(geom, (tuple, list)):
|
||||
geom = Polygon(geom)
|
||||
# Generating the lat/lng coordinate pairs.
|
||||
if isinstance(geom, (LineString, LinearRing)):
|
||||
self.latlngs = self.latlng_from_coords(geom.coords)
|
||||
elif isinstance(geom, Polygon):
|
||||
self.latlngs = self.latlng_from_coords(geom.shell.coords)
|
||||
else:
|
||||
raise TypeError('GPolyline may only initialize on GEOS LineString, LinearRing, and/or Polygon geometries.')
|
||||
|
||||
# Getting the envelope for automatic zoom determination.
|
||||
self.envelope = geom.envelope
|
||||
self.color, self.weight, self.opacity = color, weight, opacity
|
||||
super(GPolyline, self).__init__()
|
||||
|
||||
@property
|
||||
def js_params(self):
|
||||
return '%s, "%s", %s, %s' % (self.latlngs, self.color, self.weight, self.opacity)
|
||||
|
||||
|
||||
@total_ordering
|
||||
class GIcon(object):
|
||||
"""
|
||||
Creates a GIcon object to pass into a Gmarker object.
|
||||
|
||||
The keyword arguments map to instance attributes of the same name. These,
|
||||
in turn, correspond to a subset of the attributes of the official GIcon
|
||||
javascript object:
|
||||
|
||||
https://developers.google.com/maps/documentation/javascript/reference#Icon
|
||||
|
||||
Because a Google map often uses several different icons, a name field has
|
||||
been added to the required arguments.
|
||||
|
||||
Required Arguments:
|
||||
varname:
|
||||
A string which will become the basis for the js variable name of
|
||||
the marker, for this reason, your code should assign a unique
|
||||
name for each GIcon you instantiate, otherwise there will be
|
||||
name space collisions in your javascript.
|
||||
|
||||
Keyword Options:
|
||||
image:
|
||||
The url of the image to be used as the icon on the map defaults
|
||||
to 'G_DEFAULT_ICON'
|
||||
|
||||
iconsize:
|
||||
a tuple representing the pixel size of the foreground (not the
|
||||
shadow) image of the icon, in the format: (width, height) ex.:
|
||||
|
||||
GIcon('fast_food',
|
||||
image="/media/icon/star.png",
|
||||
iconsize=(15,10))
|
||||
|
||||
Would indicate your custom icon was 15px wide and 10px height.
|
||||
|
||||
shadow:
|
||||
the url of the image of the icon's shadow
|
||||
|
||||
shadowsize:
|
||||
a tuple representing the pixel size of the shadow image, format is
|
||||
the same as ``iconsize``
|
||||
|
||||
iconanchor:
|
||||
a tuple representing the pixel coordinate relative to the top left
|
||||
corner of the icon image at which this icon is anchored to the map.
|
||||
In (x, y) format. x increases to the right in the Google Maps
|
||||
coordinate system and y increases downwards in the Google Maps
|
||||
coordinate system.)
|
||||
|
||||
infowindowanchor:
|
||||
The pixel coordinate relative to the top left corner of the icon
|
||||
image at which the info window is anchored to this icon.
|
||||
"""
|
||||
def __init__(self, varname, image=None, iconsize=None,
|
||||
shadow=None, shadowsize=None, iconanchor=None,
|
||||
infowindowanchor=None):
|
||||
self.varname = varname
|
||||
self.image = image
|
||||
self.iconsize = iconsize
|
||||
self.shadow = shadow
|
||||
self.shadowsize = shadowsize
|
||||
self.iconanchor = iconanchor
|
||||
self.infowindowanchor = infowindowanchor
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.varname == other.varname
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.varname < other.varname
|
||||
|
||||
def __hash__(self):
|
||||
# XOR with hash of GIcon type so that hash('varname') won't
|
||||
# equal hash(GIcon('varname')).
|
||||
return hash(self.__class__) ^ hash(self.varname)
|
||||
|
||||
|
||||
class GMarker(GOverlayBase):
|
||||
"""
|
||||
A Python wrapper for the Google GMarker object. For more information
|
||||
please see the Google Maps API Reference:
|
||||
https://developers.google.com/maps/documentation/javascript/reference#Marker
|
||||
|
||||
Example:
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.contrib.gis.maps.google.overlays import GMarker, GEvent
|
||||
|
||||
def sample_request(request):
|
||||
marker = GMarker('POINT(101 26)')
|
||||
event = GEvent('click',
|
||||
'function() { location.href = "http://www.google.com"}')
|
||||
marker.add_event(event)
|
||||
return render(request, 'mytemplate.html', {
|
||||
'google': GoogleMap(markers=[marker]),
|
||||
})
|
||||
"""
|
||||
def __init__(self, geom, title=None, draggable=False, icon=None):
|
||||
"""
|
||||
The GMarker object may initialize on GEOS Points or a parameter
|
||||
that may be instantiated into a GEOS point. Keyword options map to
|
||||
GMarkerOptions -- so far only the title option is supported.
|
||||
|
||||
Keyword Options:
|
||||
title:
|
||||
Title option for GMarker, will be displayed as a tooltip.
|
||||
|
||||
draggable:
|
||||
Draggable option for GMarker, disabled by default.
|
||||
"""
|
||||
# If a GEOS geometry isn't passed in, try to construct one.
|
||||
if isinstance(geom, six.string_types):
|
||||
geom = fromstr(geom)
|
||||
if isinstance(geom, (tuple, list)):
|
||||
geom = Point(geom)
|
||||
if isinstance(geom, Point):
|
||||
self.latlng = self.latlng_from_coords(geom.coords)
|
||||
else:
|
||||
raise TypeError('GMarker may only initialize on GEOS Point geometry.')
|
||||
# Getting the envelope for automatic zoom determination.
|
||||
self.envelope = geom.envelope
|
||||
# TODO: Add support for more GMarkerOptions
|
||||
self.title = title
|
||||
self.draggable = draggable
|
||||
self.icon = icon
|
||||
super(GMarker, self).__init__()
|
||||
|
||||
def latlng_from_coords(self, coords):
|
||||
return 'new GLatLng(%s,%s)' % (coords[1], coords[0])
|
||||
|
||||
def options(self):
|
||||
result = []
|
||||
if self.title:
|
||||
result.append('title: "%s"' % self.title)
|
||||
if self.icon:
|
||||
result.append('icon: %s' % self.icon.varname)
|
||||
if self.draggable:
|
||||
result.append('draggable: true')
|
||||
return '{%s}' % ','.join(result)
|
||||
|
||||
@property
|
||||
def js_params(self):
|
||||
return '%s, %s' % (self.latlng, self.options())
|
|
@ -1,166 +0,0 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from math import atan, exp, log, pi, sin
|
||||
|
||||
from django.contrib.gis.geos import GEOSGeometry, LinearRing, Point, Polygon
|
||||
from django.contrib.gis.maps.google.gmap import GoogleMapException
|
||||
from django.utils.six.moves import range
|
||||
|
||||
# Constants used for degree to radian conversion, and vice-versa.
|
||||
DTOR = pi / 180.
|
||||
RTOD = 180. / pi
|
||||
|
||||
|
||||
class GoogleZoom(object):
|
||||
"""
|
||||
GoogleZoom is a utility for performing operations related to the zoom
|
||||
levels on Google Maps.
|
||||
|
||||
This class is inspired by the OpenStreetMap Mapnik tile generation routine
|
||||
`generate_tiles.py`, and the article "How Big Is the World" (Hack #16) in
|
||||
"Google Maps Hacks" by Rich Gibson and Schuyler Erle.
|
||||
|
||||
`generate_tiles.py` may be found at:
|
||||
http://trac.openstreetmap.org/browser/applications/rendering/mapnik/generate_tiles.py
|
||||
|
||||
"Google Maps Hacks" may be found at http://safari.oreilly.com/0596101619
|
||||
"""
|
||||
|
||||
def __init__(self, num_zoom=19, tilesize=256):
|
||||
"Initializes the Google Zoom object."
|
||||
# Google's tilesize is 256x256, square tiles are assumed.
|
||||
self._tilesize = tilesize
|
||||
|
||||
# The number of zoom levels
|
||||
self._nzoom = num_zoom
|
||||
|
||||
# Initializing arrays to hold the parameters for each one of the
|
||||
# zoom levels.
|
||||
self._degpp = [] # Degrees per pixel
|
||||
self._radpp = [] # Radians per pixel
|
||||
self._npix = [] # 1/2 the number of pixels for a tile at the given zoom level
|
||||
|
||||
# Incrementing through the zoom levels and populating the parameter arrays.
|
||||
z = tilesize # The number of pixels per zoom level.
|
||||
for i in range(num_zoom):
|
||||
# Getting the degrees and radians per pixel, and the 1/2 the number of
|
||||
# for every zoom level.
|
||||
self._degpp.append(z / 360.) # degrees per pixel
|
||||
self._radpp.append(z / (2 * pi)) # radians per pixel
|
||||
self._npix.append(z / 2) # number of pixels to center of tile
|
||||
|
||||
# Multiplying `z` by 2 for the next iteration.
|
||||
z *= 2
|
||||
|
||||
def __len__(self):
|
||||
"Returns the number of zoom levels."
|
||||
return self._nzoom
|
||||
|
||||
def get_lon_lat(self, lonlat):
|
||||
"Unpacks longitude, latitude from GEOS Points and 2-tuples."
|
||||
if isinstance(lonlat, Point):
|
||||
lon, lat = lonlat.coords
|
||||
else:
|
||||
lon, lat = lonlat
|
||||
return lon, lat
|
||||
|
||||
def lonlat_to_pixel(self, lonlat, zoom):
|
||||
"Converts a longitude, latitude coordinate pair for the given zoom level."
|
||||
# Setting up, unpacking the longitude, latitude values and getting the
|
||||
# number of pixels for the given zoom level.
|
||||
lon, lat = self.get_lon_lat(lonlat)
|
||||
npix = self._npix[zoom]
|
||||
|
||||
# Calculating the pixel x coordinate by multiplying the longitude value
|
||||
# with the number of degrees/pixel at the given zoom level.
|
||||
px_x = round(npix + (lon * self._degpp[zoom]))
|
||||
|
||||
# Creating the factor, and ensuring that 1 or -1 is not passed in as the
|
||||
# base to the logarithm. Here's why:
|
||||
# if fac = -1, we'll get log(0) which is undefined;
|
||||
# if fac = 1, our logarithm base will be divided by 0, also undefined.
|
||||
fac = min(max(sin(DTOR * lat), -0.9999), 0.9999)
|
||||
|
||||
# Calculating the pixel y coordinate.
|
||||
px_y = round(npix + (0.5 * log((1 + fac) / (1 - fac)) * (-1.0 * self._radpp[zoom])))
|
||||
|
||||
# Returning the pixel x, y to the caller of the function.
|
||||
return (px_x, px_y)
|
||||
|
||||
def pixel_to_lonlat(self, px, zoom):
|
||||
"Converts a pixel to a longitude, latitude pair at the given zoom level."
|
||||
if len(px) != 2:
|
||||
raise TypeError('Pixel should be a sequence of two elements.')
|
||||
|
||||
# Getting the number of pixels for the given zoom level.
|
||||
npix = self._npix[zoom]
|
||||
|
||||
# Calculating the longitude value, using the degrees per pixel.
|
||||
lon = (px[0] - npix) / self._degpp[zoom]
|
||||
|
||||
# Calculating the latitude value.
|
||||
lat = RTOD * (2 * atan(exp((px[1] - npix) / (-1.0 * self._radpp[zoom]))) - 0.5 * pi)
|
||||
|
||||
# Returning the longitude, latitude coordinate pair.
|
||||
return (lon, lat)
|
||||
|
||||
def tile(self, lonlat, zoom):
|
||||
"""
|
||||
Returns a Polygon corresponding to the region represented by a fictional
|
||||
Google Tile for the given longitude/latitude pair and zoom level. This
|
||||
tile is used to determine the size of a tile at the given point.
|
||||
"""
|
||||
# The given lonlat is the center of the tile.
|
||||
delta = self._tilesize / 2
|
||||
|
||||
# Getting the pixel coordinates corresponding to the
|
||||
# the longitude/latitude.
|
||||
px = self.lonlat_to_pixel(lonlat, zoom)
|
||||
|
||||
# Getting the lower-left and upper-right lat/lon coordinates
|
||||
# for the bounding box of the tile.
|
||||
ll = self.pixel_to_lonlat((px[0] - delta, px[1] - delta), zoom)
|
||||
ur = self.pixel_to_lonlat((px[0] + delta, px[1] + delta), zoom)
|
||||
|
||||
# Constructing the Polygon, representing the tile and returning.
|
||||
return Polygon(LinearRing(ll, (ll[0], ur[1]), ur, (ur[0], ll[1]), ll), srid=4326)
|
||||
|
||||
def get_zoom(self, geom):
|
||||
"Returns the optimal Zoom level for the given geometry."
|
||||
# Checking the input type.
|
||||
if not isinstance(geom, GEOSGeometry) or geom.srid != 4326:
|
||||
raise TypeError('get_zoom() expects a GEOS Geometry with an SRID of 4326.')
|
||||
|
||||
# Getting the envelope for the geometry, and its associated width, height
|
||||
# and centroid.
|
||||
env = geom.envelope
|
||||
env_w, env_h = self.get_width_height(env.extent)
|
||||
center = env.centroid
|
||||
|
||||
for z in range(self._nzoom):
|
||||
# Getting the tile at the zoom level.
|
||||
tile_w, tile_h = self.get_width_height(self.tile(center, z).extent)
|
||||
|
||||
# When we span more than one tile, this is an approximately good
|
||||
# zoom level.
|
||||
if (env_w > tile_w) or (env_h > tile_h):
|
||||
if z == 0:
|
||||
raise GoogleMapException('Geometry width and height should not exceed that of the Earth.')
|
||||
return z - 1
|
||||
|
||||
# Otherwise, we've zoomed in to the max.
|
||||
return self._nzoom - 1
|
||||
|
||||
def get_width_height(self, extent):
|
||||
"""
|
||||
Returns the width and height for the given extent.
|
||||
"""
|
||||
# Getting the lower-left, upper-left, and upper-right
|
||||
# coordinates from the extent.
|
||||
ll = Point(extent[:2])
|
||||
ul = Point(extent[0], extent[3])
|
||||
ur = Point(extent[2:])
|
||||
# Calculating the width and height.
|
||||
height = ll.distance(ul)
|
||||
width = ul.distance(ur)
|
||||
return width, height
|
|
@ -1,12 +0,0 @@
|
|||
{% load i18n %}<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" {{ gmap.xmlns }}>
|
||||
<head>
|
||||
<title>{% block title %}{% trans "Google Maps via GeoDjango" %}{% endblock %}</title>
|
||||
{{ gmap.style }}
|
||||
{{ gmap.scripts }}
|
||||
</head>
|
||||
<body {{ gmap.onload }} {{ gmap.onunload }}>
|
||||
{% if gmap.dom_ids %}{% for dom_id in gmap.dom_ids %}<div id="{{ dom_id }}" style="width:600px;height:400px;"></div>{% endfor %}
|
||||
{% else %}<div id="{{ gmap.dom_id }}" style="width:600px;height:400px;"></div>{% endif %}
|
||||
</body>
|
||||
</html>
|
|
@ -1,37 +0,0 @@
|
|||
{% load l10n %}
|
||||
{% autoescape off %}
|
||||
{% localize off %}
|
||||
{% block vars %}var geodjango = {};{% for icon in icons %}
|
||||
var {{ icon.varname }} = new GIcon(G_DEFAULT_ICON);
|
||||
{% if icon.image %}{{ icon.varname }}.image = "{{ icon.image }}";{% endif %}
|
||||
{% if icon.shadow %}{{ icon.varname }}.shadow = "{{ icon.shadow }}";{% endif %} {% if icon.shadowsize %}{{ icon.varname }}.shadowSize = new GSize({{ icon.shadowsize.0 }}, {{ icon.shadowsize.1 }});{% endif %}
|
||||
{% if icon.iconanchor %}{{ icon.varname }}.iconAnchor = new GPoint({{ icon.iconanchor.0 }}, {{ icon.iconanchor.1 }});{% endif %} {% if icon.iconsize %}{{ icon.varname }}.iconSize = new GSize({{ icon.iconsize.0 }}, {{ icon.iconsize.1 }});{% endif %}
|
||||
{% if icon.infowindowanchor %}{{ icon.varname }}.infoWindowAnchor = new GPoint({{ icon.infowindowanchor.0 }}, {{ icon.infowindowanchor.1 }});{% endif %}{% endfor %}
|
||||
{% endblock vars %}{% block functions %}
|
||||
{% block load %}{{ js_module }}.{{ dom_id }}_load = function(){
|
||||
if (GBrowserIsCompatible()) {
|
||||
{{ js_module }}.{{ dom_id }} = new GMap2(document.getElementById("{{ dom_id }}"));
|
||||
{{ js_module }}.{{ dom_id }}.setCenter(new GLatLng({{ center.1 }}, {{ center.0 }}), {{ zoom }});
|
||||
{% block controls %}{{ js_module }}.{{ dom_id }}.setUIToDefault();{% endblock %}
|
||||
{% if calc_zoom %}var bounds = new GLatLngBounds(); var tmp_bounds = new GLatLngBounds();{% endif %}
|
||||
{% for kml_url in kml_urls %}{{ js_module }}.{{ dom_id }}_kml{{ forloop.counter }} = new GGeoXml("{{ kml_url }}");
|
||||
{{ js_module }}.{{ dom_id }}.addOverlay({{ js_module }}.{{ dom_id }}_kml{{ forloop.counter }});{% endfor %}
|
||||
{% for polygon in polygons %}{{ js_module }}.{{ dom_id }}_poly{{ forloop.counter }} = new {{ polygon }};
|
||||
{{ js_module }}.{{ dom_id }}.addOverlay({{ js_module }}.{{ dom_id }}_poly{{ forloop.counter }});
|
||||
{% for event in polygon.events %}GEvent.addListener({{ js_module }}.{{ dom_id }}_poly{{ forloop.parentloop.counter }}, {{ event }});{% endfor %}
|
||||
{% if calc_zoom %}tmp_bounds = {{ js_module }}.{{ dom_id }}_poly{{ forloop.counter }}.getBounds(); bounds.extend(tmp_bounds.getSouthWest()); bounds.extend(tmp_bounds.getNorthEast());{% endif %}{% endfor %}
|
||||
{% for polyline in polylines %}{{ js_module }}.{{ dom_id }}_polyline{{ forloop.counter }} = new {{ polyline }};
|
||||
{{ js_module }}.{{ dom_id }}.addOverlay({{ js_module }}.{{ dom_id }}_polyline{{ forloop.counter }});
|
||||
{% for event in polyline.events %}GEvent.addListener({{ js_module }}.{{ dom_id }}_polyline{{ forloop.parentloop.counter }}, {{ event }}); {% endfor %}
|
||||
{% if calc_zoom %}tmp_bounds = {{ js_module }}.{{ dom_id }}_polyline{{ forloop.counter }}.getBounds(); bounds.extend(tmp_bounds.getSouthWest()); bounds.extend(tmp_bounds.getNorthEast());{% endif %}{% endfor %}
|
||||
{% for marker in markers %}{{ js_module }}.{{ dom_id }}_marker{{ forloop.counter }} = new {{ marker }};
|
||||
{{ js_module }}.{{ dom_id }}.addOverlay({{ js_module }}.{{ dom_id }}_marker{{ forloop.counter }});
|
||||
{% for event in marker.events %}GEvent.addListener({{ js_module }}.{{ dom_id }}_marker{{ forloop.parentloop.counter }}, {{ event }}); {% endfor %}
|
||||
{% if calc_zoom %}bounds.extend({{ js_module }}.{{ dom_id }}_marker{{ forloop.counter }}.getLatLng()); {% endif %}{% endfor %}
|
||||
{% if calc_zoom %}{{ js_module }}.{{ dom_id }}.setCenter(bounds.getCenter(), {{ js_module }}.{{ dom_id }}.getBoundsZoomLevel(bounds));{% endif %}
|
||||
{% block load_extra %}{% endblock %}
|
||||
}else {
|
||||
alert("Sorry, the Google Maps API is not compatible with this browser.");
|
||||
}
|
||||
}
|
||||
{% endblock load %}{% endblock functions %}{% endlocalize %}{% endautoescape %}
|
|
@ -1,8 +0,0 @@
|
|||
{% extends "gis/google/google-map.js" %}
|
||||
{% block functions %}
|
||||
{{ load_map_js }}
|
||||
{{ js_module }}.load = function(){
|
||||
{% for dom_id in dom_ids %}{{ js_module }}.{{ dom_id }}_load();
|
||||
{% endfor %}
|
||||
}
|
||||
{% endblock %}
|
|
@ -1,2 +0,0 @@
|
|||
{% extends "gis/google/google-map.js" %}
|
||||
{% block vars %}{# No vars here because used within GoogleMapSet #}{% endblock %}
|
|
@ -382,6 +382,10 @@ Backwards incompatible changes in 1.11
|
|||
``contrib.gis`` was first released, :ref:`gdalbuild` is now a required
|
||||
dependency for GeoDjango. In older versions, it's only required for SQLite.
|
||||
|
||||
* ``contrib.gis.maps`` is removed as it interfaces with a retired version of
|
||||
the Google Maps API and seems to be unmaintained. If you're using it, `let
|
||||
us know <https://code.djangoproject.com/ticket/14284>`_.
|
||||
|
||||
Database backend API
|
||||
--------------------
|
||||
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from unittest import skipUnless
|
||||
|
||||
from django.contrib.gis.geos import HAS_GEOS
|
||||
from django.test import SimpleTestCase
|
||||
from django.test.utils import modify_settings, override_settings
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
GOOGLE_MAPS_API_KEY = 'XXXX'
|
||||
|
||||
|
||||
@skipUnless(HAS_GEOS, 'Geos is required.')
|
||||
@modify_settings(
|
||||
INSTALLED_APPS={'append': 'django.contrib.gis'},
|
||||
)
|
||||
class GoogleMapsTest(SimpleTestCase):
|
||||
|
||||
@override_settings(GOOGLE_MAPS_API_KEY=GOOGLE_MAPS_API_KEY)
|
||||
def test_google_map_scripts(self):
|
||||
"""
|
||||
Testing GoogleMap.scripts() output. See #20773.
|
||||
"""
|
||||
from django.contrib.gis.maps.google.gmap import GoogleMap
|
||||
|
||||
google_map = GoogleMap()
|
||||
scripts = google_map.scripts
|
||||
self.assertIn(GOOGLE_MAPS_API_KEY, scripts)
|
||||
self.assertIn("new GMap2", scripts)
|
||||
|
||||
@override_settings(GOOGLE_MAPS_API_KEY=GOOGLE_MAPS_API_KEY)
|
||||
def test_unicode_in_google_maps(self):
|
||||
"""
|
||||
Test that GoogleMap doesn't crash with non-ASCII content.
|
||||
"""
|
||||
from django.contrib.gis.geos import Point
|
||||
from django.contrib.gis.maps.google.gmap import GoogleMap, GMarker
|
||||
|
||||
center = Point(6.146805, 46.227574)
|
||||
marker = GMarker(center,
|
||||
title='En français !')
|
||||
google_map = GoogleMap(center=center, zoom=18, markers=[marker])
|
||||
self.assertIn("En français", google_map.scripts)
|
||||
|
||||
def test_gevent_html_safe(self):
|
||||
from django.contrib.gis.maps.google.overlays import GEvent
|
||||
event = GEvent('click', 'function() {location.href = "http://www.google.com"}')
|
||||
self.assertTrue(hasattr(GEvent, '__html__'))
|
||||
self.assertEqual(force_text(event), event.__html__())
|
||||
|
||||
def test_goverlay_html_safe(self):
|
||||
from django.contrib.gis.maps.google.overlays import GOverlayBase
|
||||
overlay = GOverlayBase()
|
||||
overlay.js_params = '"foo", "bar"'
|
||||
self.assertTrue(hasattr(GOverlayBase, '__html__'))
|
||||
self.assertEqual(force_text(overlay), overlay.__html__())
|
Loading…
Reference in New Issue