sanj's changes merged

This commit is contained in:
Johnson Chetty 2012-02-21 11:36:25 +01:00
commit 34705baf62
11 changed files with 485 additions and 3 deletions

View File

@ -0,0 +1,14 @@
from django.template.defaultfilters import slugify
from mumbai.models import *
def do():
for cls in [Road, Area, Stop]:
for obj in cls.objects.all():
slug = slugify(obj.display_name)
if cls.objects.filter(slug=slug).count() > 1:
slug += "2"
obj.slug = slug
obj.save()
for r in Route.objects.all():
r.slug = r.alias
r.save()

View File

@ -0,0 +1,11 @@
from django.conf.urls.defaults import *
import apiviews
urlpatterns = patterns('',
(r'^route/(?P<slug>.*)$', apiviews.route), #FIXME: better regexp for route alias?
(r'^area/(?P<slug>.*)$', apiviews.area),
(r'^stop/(?P<slug>.*)$', apiviews.stop),
(r'^routes/$', apiviews.routes),
(r'^areas/$', apiviews.areas),
(r'^stops/$', apiviews.stops),
)

View File

@ -0,0 +1,66 @@
from models import *
from ox.django.shortcuts import get_object_or_404_json, render_to_json_response
from django.contrib.auth.decorators import login_required
def route(request, slug):
route = get_object_or_404_json(Route, slug=slug)
stops = [r.stop.get_geojson() for r in RouteDetail.objects.filter(route=route)]
return render_to_json_response({
'route': route.get_dict(),
'stops': {
'type': 'FeatureCollection',
'features': stops
}
})
def area(request, slug):
area = get_object_or_404_json(Area, slug=slug)
stops = [stop.get_geojson() for stop in Stop.objects.filter(area=area)]
return render_to_json_response({
'area': area.get_dict(),
'stops': {
'type': 'FeatureCollection',
'features': stops
}
})
def routes(request):
qset = Route.objects.all()
if request.GET.has_key('q'):
q = request.GET.get('q', '')
qset = qset.filter(alias__icontains=q) #FIXME: make a better Q object
routes = [route.alias for route in qset]
return render_to_json_response(routes)
def areas(request):
qset = Area.objects.all()
if request.GET.has_key('q'):
q = request.GET.get('q', '')
qset = qset.filter(display_name__icontains=q)
areas = [area.slug for area in qset]
return render_to_json_response(areas)
def stops(request):
qset = Stop.objects.all()
if request.GET.has_key('q'):
q = request.GET.get('q', '')
qset = qset.filter(display_name__icontains=q) #FIXME: This definitely needs to be a Q object with OR lookups for area name, road name, etc.
return render_to_json_response({
'type': 'FeatureCollection',
'features': [stop.get_geojson() for stop in qset]
})
def stop(request, slug):
if request.POST:
if not slug:
stop = Stop() #FIXME: should this return an error instead?
else:
stop = get_object_or_404_json(Stop, slug=slug)
return render_to_json_response(stop.from_geojson(request.POST))
else:
stop = get_object_or_404_json(Stop, slug=slug)
return render_to_json_response(stop.get_geojson()) #FIXME: please don't repeat this code, its retarded.

View File

@ -1,7 +1,9 @@
from django.contrib.gis.db import models from django.contrib.gis.db import models
from django.contrib.gis.geos import Point
from django import forms from django import forms
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic from django.contrib.contenttypes import generic
import json
STOP_CHOICES = ( ('U','Up'), STOP_CHOICES = ( ('U','Up'),
('D', 'Down'), ('D', 'Down'),
@ -49,13 +51,23 @@ class Area(models.Model):
geometry = models.PolygonField(blank=True, null=True) geometry = models.PolygonField(blank=True, null=True)
alt_names = generic.GenericRelation("AlternativeName") alt_names = generic.GenericRelation("AlternativeName")
def get_dict(self):
return {
'id': self.id,
'code': self.code,
'slug': self.slug,
'name': self.name,
'name_mr': self.name_mr,
'display_name': self.display_name
#FIXME add alt_names and geometry
}
def get_absolute_url(self): def get_absolute_url(self):
return "/area/%s/" % self.name return "/area/%s/" % self.name
def __unicode__(self): def __unicode__(self):
return self.name return self.name
class Road(models.Model): class Road(models.Model):
code = models.IntegerField()#primary_key=True) code = models.IntegerField()#primary_key=True)
slug = models.SlugField(null=True) slug = models.SlugField(null=True)
@ -95,6 +107,55 @@ class Stop(models.Model):
point = models.PointField(null=True) point = models.PointField(null=True)
alt_names = generic.GenericRelation("AlternativeName") alt_names = generic.GenericRelation("AlternativeName")
def get_dict(self):
return {
'id': self.id,
'code': self.code,
'slug': self.slug,
'official_name': self.name,
'display_name': self.display_name,
'road': self.road.name,
'area': self.area.name,
'name_mr': self.name_mr,
'routes': ", ".join([r.route.alias for r in RouteDetail.objects.filter(stop=self)]),
'alternative_names': ", ".join([a.name for a in self.alt_names.all().filter(typ='common')])
}
def get_geojson(self, srid=4326):
if self.point is not None:
geom = json.loads(self.point.transform(srid, True).geojson)
else:
geom = {}
properties = self.get_dict()
return {
'type': 'Feature',
'properties': properties,
'geometry': geom
}
def from_geojson(self, geojson):
geom = geojson['geometry']['coordinates']
data = geojson['properties']
self.point = Point(geom[0], geom[1])
self.display_name = data['display_name']
self.name_mr = data['name_mr']
if data.has_key('alternative_names') and data['alternative_names'].strip() != '':
for a in self.alt_names.all():
a.delete()
for a in data['alternative_names'].split(","):
alt_name = AlternativeName()
alt_name.name = a['name']
alt_name.typ = 'common'
alt_name.save()
self.alt_names.add(alt_name)
#FIXME: add alt names logic
self.save()
return self.get_geojson()
def __unicode__(self): def __unicode__(self):
return self.name return self.name
@ -130,6 +191,14 @@ class Route(models.Model):
def __unicode__(self): def __unicode__(self):
return self.alias return self.alias
def get_dict(self):
return {
'id': self.id,
'code': self.code,
'alias': self.alias,
'slug': self.slug,
'distance': str(self.distance)
}
class RouteDetail(models.Model): class RouteDetail(models.Model):
route_code = models.TextField() route_code = models.TextField()
@ -141,6 +210,7 @@ class RouteDetail(models.Model):
class Meta: class Meta:
verbose_name = 'Route Detail' verbose_name = 'Route Detail'
ordering = ['serial']
def __unicode__(self): def __unicode__(self):
return str(self.route) + " : " + str(self.serial) return str(self.route) + " : " + str(self.serial)

View File

@ -2,6 +2,7 @@
from models import * from models import *
from django.shortcuts import render_to_response, get_object_or_404 from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext from django.template import RequestContext
from django.contrib.auth.decorators import login_required
def index(request): def index(request):
return render_to_response("index.html", {}) return render_to_response("index.html", {})
@ -36,3 +37,9 @@ def area(request, name):
}) })
return render_to_response("area.html", context) return render_to_response("area.html", context)
def editstops(request):
context = RequestContext(request, {})
return render_to_response("editstops.html", context)

View File

@ -11,6 +11,8 @@ ADMINS = (
# ('Your Name', 'your_email@domain.com'), # ('Your Name', 'your_email@domain.com'),
) )
SITENAME = "ChaloBEST"
LOCAL_DEVELOPMENT = True LOCAL_DEVELOPMENT = True
JSON_DEBUG = True JSON_DEBUG = True

View File

@ -0,0 +1,80 @@
html, body {
width: 100%;
height: 100%;
}
#wrapper {
width: 100%;
height: 100%;
}
#listCol {
height: 98%;
overflow-y: scroll;
overflow-x: hidden;
width: 24%;
float: left;
}
#mapCol {
width: 50%;
height: 95%;
float: left;
}
#formCol {
width: 24%;
float: left;
}
.listWrapper {
display: none;
}
.selected {
color: blue;
border: 1px solid #f00;
}
.listItem {
cursor: pointer;
}
.tabButton {
cursor: pointer;
width: 50px;
border: 1px solid #000;
float: left;
text-align: center;
padding: 4px;
}
.selectedListItem .listItemText {
color: blue;
}
.selectedStop {
color: blue !important;
}
.clear {
clear: both;
}
.has_point {
color: #222;
}
.no_has_point {
color: #666;
}
ul {
list-style-type: none;
padding-left: 4px;
}
.highlightedStop {
background-color: #FFFF00;
}

View File

@ -0,0 +1,184 @@
var API_BASE = "/1.0/",
map;
(function() {
$(function() {
initMap();
$('.tabButton').click(function() {
if ($(this).hasClass("selected")) {
return;
}
var $that = $(this);
var name = $that.attr("data-name");
var $listWrapper = $('#' + name);
if ($that.data("loaded")) {
$.noop(); //dont load data
} else {
$that.data("loaded", true);
var $list = $('#' + name + 'List');
var url = API_BASE + name + "/";
var $loadingLi = $('<div />').text("Loading...").appendTo($list);
$.getJSON(url, {}, function(items) {
$loadingLi.remove();
$.each(items, function(i,v) {
var $li = $('<div />')
.addClass("listItem")
.appendTo($list);
var $txt = $('<span >').addClass("listItemText").text(v).appendTo($li);
});
});
}
$('.listWrapper').hide();
$listWrapper.show();
$('.selected').removeClass("selected");
$that.addClass("selected");
});
$('.list').click(function(e) {
var name = $(this).attr("id").replace("sList", ""); //FIXME: stick name in a data attr or so?
var $target = $(e.target).parent();
if (!$target.hasClass('listItem')) {
return;
}
if ($target.data("loading")) {
return;
}
$('.selectedListItem').removeClass("selectedListItem");
$target.addClass("selectedListItem");
if ($target.data("hasList")) {
$target.find(".stopsList").toggle();
return;
}
var url = API_BASE + name + "/" + $target.find(".listItemText").text();
$target.data("loading", true);
var $loading = $('<span />').addClass("loadingSpan").text("Loading...").appendTo($target);
$.getJSON(url, {}, function(obj) {
$loading.remove();
var stopsGeojson = obj.stops;
var stops = stopsGeojson.features;
var $stopsList = getStopsList(stops);
var stopsWithGeom = [];
$.each(stops, function(i,v) {
if (!$.isEmptyObject(v.geometry)) {
stopsWithGeom.push(v);
}
});
stopsGeojson.features = stopsWithGeom;
var currFeatures = jsonLayer.features;
jsonLayer.removeFeatures(currFeatures);
jsonLayer.addFeatures(geojson_format.read(stopsGeojson));
var maxExtent = jsonLayer.getDataExtent();
map.zoomToExtent(maxExtent);
$target.append($stopsList);
$target.data("hasList", true);
$target.data("loading", false);
});
});
$('.listSearch').keyup(function(e) {
var val = $(this).val();
var name = $(this).attr("id").replace("Search", "");
var $list = $('#' + name + "List");
$list.find(".listItem").each(function() {
var $that = $(this);
var txt = $that.find(".listItemText").text();
if (txt.indexOf(val) == -1) {
$that.hide();
} else {
$that.show();
}
});
});
});
function getStopsList(stops) {
var $ul = $('<ul />')
.addClass("stopsList")
.click(function(e) {
var $target = $(e.target);
if ($target.hasClass("selectedStop")) {
return;
}
$('.selectedStop').removeClass("selectedStop");
$target.addClass("selectedStop");
var props = $target.data("properties");
var $form = getStopForm(props);
$('#formCol').empty();
$('#formCol').append($form);
});
$.each(stops, function(i,v) {
var props = v.properties;
var geom = v.geometry;
var $li = $('<li />')
.addClass("stopItem")
.data("slug", props.slug)
.addClass(props.slug) //FIXME: please dont set data AND addClass AND include slug in properties.
.data("properties", props).data("geometry", geom)
.text(props.display_name)
.hover(function() {
//TODO: when hover over a stop name in list, it should set some styleMap stuff on the map to colour the moused-over location.
}, function() {
})
.appendTo($ul);
$.isEmptyObject(geom) ? $li.addClass("no_has_point") : $li.addClass("has_point");
});
return $ul;
}
function getStopForm(stop) {
// console.log(stop);
var $div = $('<div />');
var $displayName = $('<div />').text(stop.display_name).appendTo($div);
var $routes = $('<div />').text("Routes: " + stop.routes).appendTo($div);
// var $form = $('<form />').apendTo($div);
return $div;
}
function initMap() {
var center = new OpenLayers.LonLat(72.855211097628413, 19.010775291486027);
map = new OpenLayers.Map("mapCol", {});
var layers = [];
// layers[0] = new OpenLayers.Layer.OSM();
layers[0] = new OpenLayers.Layer.Google(
"Google Streets", // the default
{numZoomLevels: 20, isBaseLayer: true}
);
geojson_format = new OpenLayers.Format.GeoJSON();
//yes, jsonLayer is global. Yes, I know it's wrong.
jsonLayer = layers[1] = new OpenLayers.Layer.Vector({'geometryType': 'Point'});
// map.addLayer(vector_layer);
map.addLayers(layers);
map.setCenter(center, 12);
mapControl = new OpenLayers.Control.SelectFeature(layers[1]);
zoomControl = new OpenLayers.Control.ZoomToMaxExtent();
map.addControl(mapControl);
// map.addControl(zoomControl);
mapControl.activate();
// zoomControl.activate();
layers[1].events.on({
'featureselected': onFeatureSelect,
'featureunselected': onFeatureUnselect
});
}
function onFeatureSelect(feature) {
var slug = feature.attributes.slug;
alert("selected " + slug);
var matchedStops = $('.' + slug);
matchedStops.addClass('highlightedStop');
}
function onFeatureUnselect(feature) {
var slug = feature.attributes.slug;
alert("unselected " + slug);
var matchedStops = $('.' + slug);
matchedStops.removeClass('highlightedStop');
}
})();

View File

@ -1,6 +1,7 @@
<!doctype html> <!doctype html>
<html> <html>
<head> <head>
<meta charset="utf-8" />
<script type="text/javascript" src="/static/js/jquery-1.7.1.min.js"></script> <script type="text/javascript" src="/static/js/jquery-1.7.1.min.js"></script>
<title>ChaloBEST: {% block title %} {% endblock %}</title> <title>ChaloBEST: {% block title %} {% endblock %}</title>
{% block head %} {% block head %}

View File

@ -0,0 +1,43 @@
{% extends 'base.html' %}
{% block head %}
<link rel="stylesheet" href="/static/css/editstops.css" />
<script type="text/javascript" src="/static/js/editstops.js"></script>
<script type="text/javascript" src="http://openlayers.org/dev/OpenLayers.js"></script>
<script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=AIzaSyBbz_Bjqtf5U9qGZw4xoS5PxLDadHsC0AA'></script>
{% endblock %}
{% block body %}
<div id="listCol">
<div id="tabButtons">
<div id="areasTabBtn" class="tabButton" data-name="areas">
Areas
</div>
<div id="routesTabBtn" class="tabButton" data-name="routes">
Routes
</div>
<div class="clear"></div>
</div>
<div id="listsWrapper">
<div id="areas" class="listWrapper">
<input type="text" id="areasSearch" class="listSearch" />
<div id="areasList" class="list">
</div>
</div>
<div id="routes" class="listWrapper">
<input type="text" id="routesSearch" class="listSearch" />
<div id="routesList" class="list">
</div>
</div>
</div>
</div>
<div id="mapCol">
</div>
<div id="formCol">
</div>
{% endblock %}

View File

@ -4,6 +4,9 @@ from os.path import join
# Uncomment the next two lines to enable the admin: # Uncomment the next two lines to enable the admin:
from django.contrib import admin from django.contrib import admin
admin.autodiscover() admin.autodiscover()
#import ox.django.api.urls
#import mumbai
urlpatterns = patterns('', urlpatterns = patterns('',
# Example: # Example:
@ -14,7 +17,8 @@ urlpatterns = patterns('',
(r'^route/(?P<alias>[a-zA-Z0-9\s\-]*?)/$', 'mumbai.views.route'), (r'^route/(?P<alias>[a-zA-Z0-9\s\-]*?)/$', 'mumbai.views.route'),
(r'^areas/$', 'mumbai.views.areas'), (r'^areas/$', 'mumbai.views.areas'),
(r'^area/(?P<name>.*?)/$', 'mumbai.views.area'), (r'^area/(?P<name>.*?)/$', 'mumbai.views.area'),
(r'^editstops/$', 'mumbai.views.editstops'),
(r'^1.0/', include('mumbai.apiurls')),
# Uncomment the admin/doc line below to enable admin documentation: # Uncomment the admin/doc line below to enable admin documentation:
(r'^admin/doc/', include('django.contrib.admindocs.urls')), (r'^admin/doc/', include('django.contrib.admindocs.urls')),
#(r'^grappelli/', include('grappelli.urls')), #(r'^grappelli/', include('grappelli.urls')),