#javascript #python #django #google-maps #coordinates
#javascript #python #django #google-карты #координаты
Вопрос:
У меня есть модель базы данных Django, которая выглядит следующим образом:
class Point(models.Model):
date = models.DateTimeField(auto_now_add=True)
long = models.DecimalField(max_digits=10, decimal_places=7)
lat = models.DecimalField(max_digits=10, decimal_places=7)
def as_json(self):
return dict(lat=str(self.lat),lng=str(self.long))
def __unicode__(self):
return "date: %s nlong: %s nlat: %sn" % (str(self.date), str(self.lat), str(self.long))
Моя проблема:
Я пытаюсь создать Ajax-карту Google на моем интерфейсе, которая выполняет запрос, который отправляет нижние левые и верхние правые координаты вида. Затем сервер разделяет представление на сетку 10 * 10 и выдает мне самые последние 100 точек из каждой единицы в сетке.
Запрос:
var tosend = {
coords: m_map.getBounds(),
zoom: m_map.getZoom()
};
// alert(JSON.stringify(tosend));
$.ajax({
type: "POST",
url: "/points/",
async: "true",
dataType: "json",
data: JSON.stringify(tosend),
success: updatePins
});
Что видит сервер:
getBounds() возвращает объект {‘pa’:{lat : somelat, long: somelong}, ‘xa'{lat: somelat, long: somelong}
например (на стороне Python):
{u'coords': {u'pa': {u'k': -173.3125, u'j': -122.6875}, u'xa': {u'k': -42.51152335203773, u'j': 74.60508620624063}}, u'zoom': 2}
Мои мысли перед кодом:
Сервер Django получает postData в этом представлении и обрабатывает все это. Теоретически алгоритм в sendNeeded() должен возвращать все точки в представлении, поскольку я еще не ограничил запросы.
Моя база данных содержит более 30 000 хорошо расположенных координат, которые на 100% не являются проблемой. Я думаю, что проблема в моем алгоритме или в том, что я неправильно истолковал то, что возвращает gmaps API через «getBounds()».
Важно:
Я замечаю, что когда я увеличиваю вид на Америку или другие конкретные места, я периодически получаю около 10 000 возвращаемых точек, но это не согласовано, и я знаю, что это неправильно.
Просмотр сервера:
from django.shortcuts import render
from django.http import HttpResponse
from django.template import RequestContext, loader
import json
from models import Point
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def points(request):
return json_flights(request)
def globe(request):
template = loader.get_template('points/globe.html')
context = RequestContext(request)
return HttpResponse(template.render(context))
def json_flights(request):
input = json.loads(request.body)
lb_lat = input['coords']['xa']['k'],
lb_long = input['coords']['pa']['j'],
rt_lat = input['coords']['pa']['k'],
rt_long = input['coords']['xa']['j'],
zoom = input['zoom']
case = 0
#create map_view object for easy passability
map_view = {
'lb_lat' : input['coords']['xa']['k'],
'lb_long' : input['coords']['pa']['j'],
'rt_lat' : input['coords']['pa']['k'],
'rt_long' : input['coords']['xa']['j'],
'zoom' : input['zoom'],
'case' : 0
}
# assign the case to it
if (lb_long < rt_long) and (lb_lat < rt_lat):
map_view['case'] = 0
elif (lb_long < rt_long) and (lb_lat > rt_lat):
map_view['case'] = 1
elif (lb_long > rt_long) and (lb_lat < rt_lat):
map_view['case'] = 2
elif (lb_long > rt_long) and (lb_lat > rt_lat):
map_view['case'] = 3
print "input from client: ", input
pointstosend = []
pointlist = []
#run function to send 100 per square
pointlist = sendOnlyNeeded(map_view)
#get ready to send
json_results = {
'insertdatapayloadhere': "Sample Value",
'coords': pointlist
}
# print "json to send::: ", jsontosend
return HttpResponse(json.dumps(json_results), content_type='application/json')
def sendOnlyNeeded(map_view):
zoom = map_view['zoom']
points_to_return = []
if map_view['case'] == 0:
view_width = map_view['rt_long'] - map_view['lb_long']
view_height = map_view['rt_lat'] - map_view['lb_lat']
elif map_view['case'] == 1:
view_width = map_view['rt_long'] - map_view['lb_long']
view_height = (90 - map_view['lb_lat']) (map_view['rt_lat'] 90)
elif map_view['case'] == 2:
view_width = (map_view['rt_long'] 180) (180 - map_view['lb_long'])
view_height = map_view['rt_lat'] - map_view['lb_lat']
elif map_view['case'] == 3:
view_width = map_view['rt_long'] - map_view['lb_long']
view_height = map_view['rt_lat'] - map_view['lb_lat']
print 'view_width', view_width , 'view_height', view_height
# constant for tweaking
number_per_unit = 100
# set the grid dimensions
griddimensions = [10, 10]
#find the height and width of each rectangle within the view
unit_height = view_height / griddimensions[1]
unit_width = view_width / griddimensions[0]
#iterate by viewable gridunit and filter the num_per_gu markers to send.
# starting at lb_lat, chop up and push markers from grid until you get to rt_lat
# start>------->
# >------------>
# >--------->end
long_cursor = map_view['lb_long']
lat_cursor = map_view['lb_lat']
widthcount = 0;
heightcount = 0
print "view height::::::", view_height
while heightcount < view_height:
if lat_cursor > 90:
lat_cursor = -90
while widthcount < view_width:
unitbounds = {
'lb_lat': lat_cursor,
'rt_lat': lat_cursor unit_height,
'lb_long': long_cursor ,
'rt_long': long_cursor unit_width
}
print "unitbounds: ", unitbounds
querySetResult = getMarkersinBox(unitbounds, number_per_unit)
for cords in querySetResult:
points_to_return.append([float(cords.long), float(cords.lat)])
#points_to_return.append(preparedata(getMarkersinBox(unitbounds, number_per_unit)))
#check if you went off the map. notice we let it overflow. this way, we're guaranteed not to miss any points.
long_cursor = unit_width
if long_cursor > 180:
long_cursor = -180
#counter to stop
widthcount = unit_width
print "widthcount::::", widthcount
lat_cursor = unit_height
heightcount = unit_height
print "heightcount:::", heightcount
return points_to_return
def getMarkersinBox(unitbounds, number_per_unit):
from django.db.models import Q
numbtosend = number_per_unit
lb_lat = unitbounds['lb_lat']
lb_long = unitbounds['lb_long']
rt_lat = unitbounds['rt_lat']
rt_long = unitbounds['rt_long']
if (lb_long < rt_long) and (lb_lat < rt_lat):
queryresult = Point.objects.filter(long__gte=lb_long, long__lte=rt_long, lat__gte=lb_lat, lat__lte=rt_lat).order_by('id')
elif (lb_long < rt_long) and (lb_lat > rt_lat):
queryresult = Point.objects.filter(Q(long__gte=lb_long), Q(long__lte=rt_long), Q(lat__gte=lb_lat) | Q(lat__lte=rt_lat)).order_by('id')
elif (lb_long > rt_long) and (lb_lat < rt_lat):
queryresult = Point.objects.filter(Q(long__gte=lb_long) | Q(long__lte=rt_long), Q(lat__gte=lb_lat), Q(lat__lte=rt_lat)).order_by('id')
elif (lb_long > rt_long) and (lb_lat > rt_lat):
queryresult = Point.objects.filter(Q(long__gte=lb_long) | Q(long__lte=rt_long), Q(lat__gte=lb_lat) | Q(lat__lte=rt_lat)).order_by('id')
return queryresult
(csrf освобожден для тестирования).
Весь мой код карты для хорошей оценки:
var m_infoBox = null;
var m_map;
var markers = [];
var image = {
url: '/static/pictures/map_globe_pin.png',
size: new google.maps.Size(18, 17),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(8, 8)
};
var sampd;
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(32.000000, 32.000000),
zoom: 2,
minZoom: 2,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
m_map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
google.maps.event.addListener(m_map, 'idle', queryServerForMarkers);
google.maps.event.addListenerOnce(m_map, 'idle', function() {
fixPanHandles();
});
google.maps.event.addListenerOnce(m_map, 'idle', function() {
setTimeout(function() {
fixPanHandles();
}, 500);
});
google.maps.event.addListener(m_map, 'bounds_changed', function() {
checkIfMarkersStillVisible();
});
m_map.getMinimumResolution = function() {
return 12;
};
}
google.maps.event.addDomListener(window, 'load', initialize);
function checkIfMarkersStillVisible() {
for (var i = 0; i < markers.length; i ) {
if (!m_map.getBounds().contains(markers[i].getPosition())) {
markers[i].setMap(null);
} else {
markers[i].setMap(m_map);
}
}
}
function InfoBox(opts, uid, mid, content) {
google.maps.OverlayView.call(this);
this.latlng_ = opts.latlng;
this.map_ = opts.map;
this.offsetVertical_ = -76;
this.offsetHorizontal_ = -105;
this.height_ = 58;
this.uid_ = uid;
this.mid_ = mid;
this.content_ = content;
var me = this;
this.boundsChangedListener_ =
google.maps.event.addListener(this.map_, "bounds_changed", function() {});
this.setMap(this.map_);
return this;
}
function queryServerForMarkers() {
//clean up first
var tosend = {
coords: m_map.getBounds(),
zoom: m_map.getZoom()
};
// alert(JSON.stringify(tosend));
$.ajax({
type: "POST",
url: "/points/",
async: "true",
dataType: "json",
data: JSON.stringify(tosend),
success: updatePins
});
}
function updatePins(data) {
sampd = data;
//tells you center:
console.table("Bounds of view: " m_map.getBounds() "ngot new pins: " data.coords.length JSON.stringify(data.coords));
for (var i = 0; i < data.length; i ) {
if ($.inArray(data[i], markers) != -1) {
continue;
}
var coords = new google.maps.LatLng(data.coords[i][0], data.coords[i][1]);
setMarker(m_map, coords, 0, 1223, 1122);
//debug for coords
console.log({
'lat': parseFloat(data.coords[i][0]),
'lng': parseFloat(data.coords[i][1])
});
}
}
function setMarker(map, markerLatLng, markerzIndex, uid, mid) {
var marker = new google.maps.Marker({
position: markerLatLng,
map: map,
icon: image,
// animation: google.maps.Animation.DROP,
zIndex: markerzIndex
});
google.maps.event.addListener(marker, 'mouseover', function() {
if (m_infoBox) {
if (m_infoBox.mid_ == mid) {
return;
}
m_infoBox.setMap(null);
m_infoBox = null;
}
content = loadContent(uid);
m_infoBox = new InfoBox({
latlng: marker.getPosition(),
map: m_map
}, uid, mid, content);
});
markers.push(marker);
}
function loadContent(uid) {
var response = '';
$.ajax({
type: "GET",
url: "/service/globe_float_content/?uid=" uid,
async: true,
success: function(text) {
response = text;
}
});
return response;
}
InfoBox.prototype = new google.maps.OverlayView();
InfoBox.prototype.remove = function() {
if (this.div_) {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
}
};
InfoBox.prototype.draw = function() {
// Creates the element if it doesn't exist already.
this.createElement();
if (!this.div_) return;
// Calculate the DIV coordinates of two opposite corners of our bounds to
// get the size and position of our Bar
var pixPosition = this.getProjection().fromLatLngToDivPixel(this.latlng_);
if (!pixPosition) return;
this.div_.style.display = 'block';
// Assign the correct width for the popup pane to fully display all details in the popup
var divWidth = $('#float_avatar').width() $('#float_name').width() 32;
$('#float_tip_down').css("left", ((divWidth / 2) - 20) "px");
this.div_.style.width = divWidth "px";
this.div_.style.left = (pixPosition.x - (divWidth / 2)) "px";
this.div_.style.height = this.height_ "px";
this.div_.style.top = (pixPosition.y this.offsetVertical_) "px";
};
function fixPanHandles() {
//fix bug with IE and gmaps pan hotspots.
$('#map-canvas div[title^="Pan"]').css("opacity", 0);
}
function setAllMap(map) {
for (var i = 0; i < markers.length; i ) {
markers[i].setMap(map);
}
}
Комментарии:
1. Это не ваша текущая проблема, но вы не хотите писать свой код так, чтобы он зависел от внутренних свойств API (например:
['xa']['k']
), они могут меняться с каждым выпуском API и в конечном итоге изменят и сломают вашу страницу. Используйте документированные методы google.maps. LatLngBounds объект и google.maps. LatLng объект
Ответ №1:
Я хочу подтвердить то, что написал geocodezip. Играя с API, я заметил, что для ограниченного угла «pa» широта была «j», а долгота была «k». Однако для ограниченного угла «xa» широта была «k», а долгота — «j». Я не могу сказать вам, являются ли эти сопоставления согласованными или нет, поэтому использование API было бы лучше.
Если это не поможет, и если вы все еще застряли, то я был бы признателен за более подробную информацию о том, что именно пытается сделать ваш код — в коде, который вы опубликовали, мне потребовалось бы некоторое время, чтобы выяснить, с каким кодом я должен иметь дело.